Mostrar Mensajes

Esta sección te permite ver todos los posts escritos por este usuario. Ten en cuenta que sólo puedes ver los posts escritos en zonas a las que tienes acceso en este momento.


Mensajes - Kabuto

Páginas: 1 ... 35 36 37 38 39 [40] 41 42 43 44 45 ... 50
781
Hola.

Tienes que crear un ActionListener para que el botón responda.

Puedes hacerlo creando una clase anidada dentro de la clase principal.

La clase que vas a crear ha de heredar de la clase ActionListener. AL heredar de esta clase, se ha implementar obligatoriamente un método llamado actionPerformed().
Dentro de este método, es donde vamos a escribir qué queremos que ocurra cuando se pulse el botón.

Aquí te pongo un ejemplo donde simplemente se muestran mensajes JOptionPane para informar si los datos de usuario y contraseña son correctos.
Ahí es donde deberías crear la otra ventana que se pide mostrar con una foto y no se que más.

Citar
   class AccionBotonOK implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         
         String pass=new String(Password.getPassword());

         if(User.getText().equals(usuario)&& pass.equals(contraseña)){
            JOptionPane.showMessageDialog(null, "Datos login aceptados",
                  "Login Usuario", JOptionPane.INFORMATION_MESSAGE);
         }
         else {
            JOptionPane.showMessageDialog(null, "Datos login rechazados",
                  "Login Usuario", JOptionPane.WARNING_MESSAGE);
         }
      }
   }

Una vez has escrito esta clase, tienes que instanciar un objeto de ella y añadírsela al botón correspondiente:

Citar
      JButton btnOk = new JButton("OK");
      btnOk.setBounds(135, 182, 89, 23);
      btnOk.addActionListener(new AccionBotonOK());
      frame.getContentPane().add(btnOk);

Así, cuando se pulse ese botón, se ejecutará el código escrito en la clase ActionListener que hemos escrito.

Te pego aquí el código completo.
He hecho un par de cambios menores. Para los textos de "Usuario" y "Contraseña" he usado dos JLabel en lugar de los JTexField.
Les he puesto fondo opaco y blanco para que se pueda ver sobre el fondo azul que pusiste.

También he cambiado el sitio donde declaras los String del nombre de usuario y contraseña correctos.
Tu los declaraste dentro del método initialize() pero ahí no servirá porque en cuanto ese método termine, esos String "morirán" junto con el método.

Los he puesto como atributo de clase, así siempre existirán mientras se esté ejecutando el programa.

También he quitado varios import que son totalmente innecesarios, al menos en el estado actual del programa.
Señalo en negrita los cambios:
Citar
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JButton;
import javax.swing.JTextField;
import javax.swing.JPasswordField;
import java.awt.Color;

public class Contraseña {

   private JFrame frame;
   private JTextField User;
   private JPasswordField Password;
   private JLabel txtContrasea;
   private JLabel txtUsuario;

   //Datos login
   private String usuario="santiago";
   private String contraseña="1234";


   /**
    * Launch the application.
    */
   public static void main(String[] args) {
      EventQueue.invokeLater(new Runnable() {
         public void run() {
            try {
               Contraseña window = new Contraseña();
               window.frame.setVisible(true);
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      });
   }

   /**
    * Create the application.
    */
   public Contraseña() {
      initialize();
   }

   /**
    * Initialize the contents of the frame.
    */
   private void initialize() {
      frame = new JFrame();
      frame.getContentPane().setBackground(new Color(0, 0, 205));
      frame.setBounds(100, 100, 450, 300);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().setLayout(null);

      User = new JTextField();
      User.setBounds(135, 31, 180, 20);
      frame.getContentPane().add(User);
      User.setColumns(10);

      Password = new JPasswordField();
      Password.setBounds(135, 80, 180, 20);
      frame.getContentPane().add(Password);

      txtContrasea = new JLabel();
      txtContrasea.setBackground(Color.WHITE);
      txtContrasea.setOpaque(true);
      txtContrasea.setText("Contrase\u00F1a");
      txtContrasea.setBounds(10, 80, 86, 20);
      frame.getContentPane().add(txtContrasea);

      txtUsuario = new JLabel();
      txtUsuario.setBackground(Color.WHITE);
      txtUsuario.setOpaque(true);
      txtUsuario.setText("Usuario");
      txtUsuario.setBounds(10, 31, 86, 20);
      frame.getContentPane().add(txtUsuario);

      JButton btnOk = new JButton("OK");
      btnOk.setBounds(135, 182, 89, 23);
      btnOk.addActionListener(new AccionBotonOK());
      frame.getContentPane().add(btnOk);

   }
   
   /*
    * Aquí programamos lo que queremos que ocurra cuando se
    * pulse el boton OK
    */

   class AccionBotonOK implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         
         String pass=new String(Password.getPassword());

         if(User.getText().equals(usuario)&& pass.equals(contraseña)){
            JOptionPane.showMessageDialog(null, "Datos login aceptados",
                  "Login Usuario", JOptionPane.INFORMATION_MESSAGE);
         }
         else {
            JOptionPane.showMessageDialog(null, "Datos login rechazados",
                  "Login Usuario", JOptionPane.WARNING_MESSAGE);
         }
      }

   }
}

782
Los ordenadores en realidad no pueden generar números aleatorios
Lo que hacen es generar números "pseudoaletorios", es decir, mediante un serie de operaciones matemáticas generan una lista de números que aparentan ser aleatorios.

Esto es lo que hace rand(), devolver una lista pseudoaleatoria. Y se puede comprobar que no es realmente aleatoria porque cada vez que ejecutes el mismo programa donde uses rand(), verás que siempre devuelve la misma secuencia de números que la vez anterior.

rand() genera esa lista a partir de un valor numérico llamado "semilla". Como la "semilla" es siempre la misma, la lista no varía.

Ahí es donde srand() interviene.
srand() puede cambiar la "semilla" y así rand() generará una lista de pseudoaletorios distinta.

Obviamente, cada vez que iniciemos el programa, habría que darle una "semilla" distinta.
Lo habitual es llamar a la función time() para darle como "semilla" el instante de tiempo (fecha y hora) del momento en que se ejecuta el programa.
Así cada vez la semilla será distinta.

Puedes comprobarlo con estos códigos.

Esto generará siempre la misma lista de aleatorios

Código: [Seleccionar]
#include <stdio.h>
#include <stdlib.h>
 
int main(void)
{

    for(int i = 0; i<10; i++)
        printf(" %d ", rand());
    return 0;
}

Pero añadiendo una srand() al principio para cambiar la semilla, la lista de aleatorios será distinta en cada ejecución:

Código: [Seleccionar]
#include <stdio.h>
#include <stdlib.h>
 
int main(void)
{
    srand(time(0)); //Pasamos como semilla el instante actual

    for(int i = 0; i<10; i++)
        printf(" %d ", rand());
    return 0;
}

Mucha más información en este enlace del curso de C


783
Yo soy más de Java/Kotlin y C/C++

En otros lenguajes me pierdo un poco, pero veo un par de cosas a mencionar:

Código: [Seleccionar]
MiArray(10) = MiArray(i)Eso te dará error por que no hay ningún elemento con posicion 10.

Si el array tiene 10 elementos, estarán numerados del 0 al 9. Si intentas apuntar a la posición 10, será cuando te da error avisando de que te has salido "fuera".

Por otro lado, mueves los elementos sin guardar previamente el último valor (el de la posición 9). Este valor hay que guardarlo en otra variable, entonces mueves los elementos y terminado el proceso, el valor guardado se adjudica a la posición 0.

EL array es mejor recorrerlo a la inversa. Si empiezas desde el principio, seguramente solo conseguirás repetir el mismo número en todo el array.
Supón este array:

[10,20,30,40]

Eso son 4 elementos, numerados del 0 al 3.

El valor de la posición 0, hay que pasarlo a posición 1.
El valor de la posición 1, hay que pasarlo al posición 2.
-etc...

¿Qué pasa si empezamos desde el principio?
La i del bucle vale 0.

Así que cogemos el valor de la posición i (0) y lo pasamos a posición i+1 (1).
El array queda así:
[10,10,30,40]

Ya podemos darnos cuenta de que hemos perdido el valor 20
Pero sigamos, el bucle comienza el siguiente paso y ahora i vale 1

Así que cogemos el valor de la posición i (1) y lo pasamos a posición i+1 (2).
El array queda así:
[10,10,10,40]

No hace falta seguir, ahora hemos perdido el 30 y es evidente que también perderemos el 40.

Hay que recorrer al revés.

Bueno, lo primero como dije antes, es guardar el ultimo valor antes de que el bucle mueva nada.

Si volvemos al array de ejemplo:

[10,20,30,40]


Pues primero guardamos el 40, que está en posición 3.

variableAux = miArray(3)

OK, guardado. Ahora comienzca el bucle, pero ha de ir hacia atrás y comenzará en la posicion penúltima.
Al comenzar la i ha de tener la posicion valor 2.

Así que cogemos el valor de la posición i (2) y lo pasamos a posición i+1 (3).
El array queda así:
[10,20,30,30]

Hemos machacado el 40, pero no importa, ya que lo hemos guardado en una variable.
Sigamos el bucle va hacia atrás y la i vale 1.

Así que cogemos el valor de la posición i (1) y lo pasamos a posición i+1 (2).
El array queda así:
[10,20,20,30]

El bucle llega al último paso, la i vale 0.
Así que cogemos el valor de la posición i (0) y lo pasamos a posición i+1 (1).
El array queda así:
[10,10,20,30]


Vale, el bucle ha terminado. Ya solo queda coger el valor que habiamos guardado, el 409 y colocarlo al principio, en la posición 0.
El array queda así:
[40,10,20,30]

LISTO. Se han movido los valores como se pide, no se ha perdido ningún dato, el bucle for no va a intentar acceder a posiciones del array que no existen...

No necesitas ningún IF dentro del bucle.

Tan solo tienes que:
- Guardar último valor en otra variable.
- Hacer que la variable i del FOR vaya desde el penúltimo (no el último) hasta posición 0.
- En cada iteración del bucle, mover lo que está en posición i a la posición i+1
- Terminado el bucle, el valor que habíamos guardado lo colocamos en posicion 0.

Y ya está. La clave es saber establecer la condición ideal del bucle FOR


784
Los pasos a seguir podrían ser:

- Haces copia del último elemento en una variable de apoyo.
- Recorres el array de manera inversa mediante un bucle, empezando por el penúltimo elemento hasta el primero.
- En cada elemento que te posicionas, lo mueves al siguiente.
 Es decir:
- Empiezas en el penúltimo y lo pones el último.
- Pasas al antepenútlimo y lo pasas al penúltimo.
- Pasa al anterior y lo poner el antepenúltimo.
- Etc...

- Cuando el bucle haya terminado de recorrer y mover elementos, entonces recuperas el valor que guardaste en la variable de apoyo y lo colocas en el primer elemento.

Y ya está.
Intenta desarrollar tú el código que siga estos pasos. Si no lo consigues, muestra lo que hayas podido hacer y te ayudamos a completarlo.

785
Notepad++ no compila nada. Tan solo es un editor de texto avanzado, que gracias a su resaltador de sintaxis te facilita escribir código al resaltar en distintos colores cuáles son las palabrar reservadas de Java y cuales no.

Quien compila es el programa javac que invocas desde la línea de comandos.

Seguramente tu problema es que en la línea de comandos no has especificado la extensión .java.

Si tu clase se llama "ernesto", prueba con:

Citar
javac ernesto.java

Por cierto, salvo que realmente estés obligado a solo usar editores de texto y compilar desde línea de comandos, yo recomendaría usar un IDE (interfaz de desarrollo) más avanzado.
NetBeans, Eclipse, IntelliJ... hay varios y todos te hacen la programación más cómoda y de hecho facilitan el aprendizaje.
No solo resaltan texto como NotePad++: detectan errores mientras escribes y te proponen como corregirlo, hacen sugerencias para mejorar el código, te muestran directamente los métodos de los que dispone cada clase, algunos como Eclipse basta con pasar el ratón sobre un comando Java y te muestra en una ventanita la documentación oficial de Oracle....

Te ayudan a visualizar la estructura de tu proyecto y sus packages para tener bien ordenadas las clases que compondrán tu programa.

Tienen modo debug, que ayuda a encontrar errores y también comprender mejor como se ejecuta el código internamente.

Además tienen su propia consola de salida así que no necesitarás la línea de comandos para ejecutar el programa.

Ofrecen un montón de ventajas y todos son gratuitos.

Eso no quita para que no sea importante saber manejarse con un editor de texto sencillo y con la linea de comandos de la consola del sístema.
A veces un programador profesional tiene que poder desarrollar código en ordenadores que no disponen de una interfaz gráfica tipo Windows, como por ejemplo grandes servidores donde no se pueden "desperdiciar" recursos mostrando ventanitas y colores.

Pero para facilitar el aprendizaje en programación creo que es mejor empezar con todas las ayudas posibles y luego ya se irá pasando a niveles más "hardcore" je je...

786
Hola.

No tiene ningún misterio. Simplemente escribir la ruta correcta que apunta a donde están almacenadas esas fotos.
Si puedes dinos la ruta absoluta donde se encuentra el documento HTML que ha de mostrar las imágenes, algo parecido a:
C:\users\gterrasi\escritorio\miWeb\index.html

Y también la ruta absoluta donde estan guardadas las imágenes que quieres mostrar.
Por ejemplo:
C:\users\gterrasi\escritorio\imagenes\foto.jpg


Con eso quizás podemos decirte cuál sería la ruta correcta que hay que especificarle a src

787
Hola, bueno  pensaba usar el "año mes día" aaaammdd,  como recomiendas, ...¿como se podria hacer esto? Gracias.

P.d. Estamos intentando eliminar productos o vendedores, alguna idea para realizarlo?

Vale usando ese formato es muy fácil ordenar.
Explico:
Colecciones como ArrayList tienen un método que permite ordenar los objetos que contiene. Es el método .sort().

Para usar este método hay dos maneras:
- Le pasamos por argumentos en el paréntesis una expresión para indicarle al ArrayList como debe ordenar los objetos que contiene..
- Le pasamos valor null, y confiamos en que el ArrayList sepa como ordenarse.

La primera manera puede ser algo complicado saber construir correctamente dicha expresión.

La segunda manera es la más fácil, pero claro, dependemos de que el ArrayList sepa ordenarse.
Aunque lo cierto, es que esto no depende del ArrayList, sino de los objetos que contiene. Son ellos los que tienen que saber como "compararse" unos con otros para decidir quien es menor y quien es mayor.
Los objetos que saben "compararse", son los que su clase implementa la interfaz Comparable

Por ejemplo los objetos de clases que representan números: Integer, Double, Float.... sí la implementan y por eso saben perfectamente que un 4 es menor que un 5.

La clase String, también la implementa, así que saben perfectamente que la cadena "b" es menor que la cadena "c".

Así que nunca tendremos problemas para ordenar un ArrayList de Integer o de String, basta con llamar a sort(null) y se ordenará al momento el solito.

Pero nuestro ArrayList de Registro de Facturas, va a contener objetos de la clase Factura.
Una clase que hemos creado nosotros y que sus objetos no saben como compararse para decidir quien es menor o mayor.
Así que ahora mismo nuestro ArrayList de Facturas, no sabe como ordenar sus objetos.

Pero para eso estamos nosotros, para enseñarle a la clase Factura como compararse.

Lo primero es implementarle la interfaz Comparable, indicándole el tipo de clase con el que se tiene que comparar, que lo normal es que sea ella misma.

Código: [Seleccionar]
public class Factura implements Comparable<Factura>{
......
......

Al implementar esta interfaz, el compilador inmediatamente nos obliga a implementar también el método compareTo().
Este método es el que explica como ha de compararse un objeto y ha de retorna un int.
Si los objetos se consideran del "mismo valor" ha de retorna 0
Si el objeto recibido por argumentos, es mayor respecto al objeto que invoca el método, ha de retornar -1.
Si el objeto recibido es menor, entonces ha de retornar 1.

Bien, en nuestro caso, tendríamos que escribir un código que compare las fechas.
Podemos ordenarlas de modo que las fechas más antiguas se consideren menores que las fechas más recientes.
Es decir, la fecha 20191105 es menor que la fecha 20191205, porque esta última es más reciente.
Bueno, para hacer esto podríamos empezar a trocear el String que contiene la fecha, para separar el valor del año, del mes y del dia...y parsearlos a valores int, y entonces....

Pero, ¡¡espera!!. Hemos dicho antes que los objetos String ya saben compararse. Y precisamente se comparan de forma que coincide a como queremos ordenar las fechas.

El String "20191105" se considera menor que el String "20191205".
Es más, podemos usar separadores como guiones y la comparación de String fucnionará igual.
"2019-11-05" es menor que "2019-12-05".

Así que no tenemos que hacer nada, tan solo decirle a nuestro método compareTo(), que retorne el resultado de comparar directamente los String de fechas.

Código: [Seleccionar]
@Override
public int compareTo(Factura otraFactura) {
return fecha.compareTo(otraFactura.getFecha());
}

Genial, ahora nuestra clase Factura ya sabe como compararse unas con otras para decidir cual es menor o mayor, en función de su fecha.

Ahora lo que podemos hacer es decirle a nuestra clase de Registro de Facturas, que cada vez que entre una nueva Factura en el ArrayList, lo ordene. Así siempre estará ordenado tras cada inserción.

Código: [Seleccionar]
public boolean addFactura(Factura factura) {
if (facturas.contains(factura))
return false;
else {
facturas.add(factura);
facturas.sort(null); //Null es porque las Facturas ya saben como compararse para ordenarse
return true;
}
}


Con esto, si desde el programa principal, tras haber hecho algunas facturas pedimos un listado, veremos que salen ordenadas por fecha, de más antigua a más moderna.
Las facturas de este listado yo las he ido creando por orden de ID: FAC01, FAC02, FAC03 y FAC04.
Pero al entrar en el ArrayList, se han ido ordenando por la fecha.

Citar
      FACTURAS REGISTRADAS

ID: FAC03 Fecha: 2018-12-12
Notas: jiol

ID: FAC02 Fecha: 2019-06-14
Notas: popop

ID: FAC01 Fecha: 2019-06-15
Notas: fasdfsa

ID: FAC04 Fecha: 2019-08-15
Notas: sdafsa

Bueno, ¿y si queremos que el orden sea al revés? ¿De más reciente a más antiguas?
Entonces bastaría con modificar el método comparteTo() de la clase Factura, diciéndole que modifique el signo del resultado de comparar los Strings de fechas.
Cuando el resultado sea -1, ha de devolver +1 y viceversa.
Para hacer esto, puesto que somos grandes matemáticos xD, ya sabemos que nos basta con multiplicar lo que retorna, por -1

Citar
   @Override
   public int compareTo(Factura otraFactura) {
      return fecha.compareTo(otraFactura.getFecha()) * -1;
   }

Esto revierte a como se ordenan las facturas, ahora las fechas recientes se consideran menores que las antiguas:
Citar
      FACTURAS REGISTRADAS

ID: FAC04 Fecha: 2019-08-15
Notas: sdafad

ID: FAC01 Fecha: 2019-06-15
Notas: fsadfa

ID: FAC02 Fecha: 2019-06-14
Notas: popop

ID: FAC03 Fecha: 2018-12-12
Notas: jiol


Y ya está.
Cierto es que hemos optado por la via fácil, usar un String con formato aaaa-mm-dd.
Y ojo, es que ese formato de fecha cumple en realidad con la norma ISO 8601 y es la que se suele usar en informática precisamente por esto, porque facilita mucho la ordenación alfanumérica.

Sin embargo en la mayoría de paises de habla hispana, lo habitual es usar dd-mm-aaaa.
Pero bueno, no creo que sea necesario ahora pelearse con formatos de fecha.

Lo importante aquí es aprender que para ordenar objetos, podemos hacerlo implementado la interfaz Comparable y decidir nosotros que atributos son decisivos para hacer la comparación.
En el Curso de Java Avanzado tienes un ejemplo algo más elaborado.

Sabiendo ya esto, puedes decidir ya si quieres usar otro formato de fecha, o usar clases como LocalDate

En ambos casos ya se requiere meter más programación.

Respecto a esto:
Citar
P.d. Estamos intentando eliminar productos o vendedores, alguna idea para realizarlo?

Los ArrayList tienen el metodo remove() que debería permitirte hacer esto fácilmente.

Por ejemplo, en la clase Inventario, si quiero eliminar un Producto de su ArrayLsit, me basta con escribirle este método:
Código: [Seleccionar]
public boolean eliminarProducto(Producto producto) {
return productos.remove(producto);
}

Este método recibe un objeto Producto que es el que se ha de eliminar.
Así que se lo pase al método remove() del ArrayList.
remove() lo que hace es que si dicho Producto existe en el ArrayList, lo elimina y retorna true.
Si no existiera, entonces retorna false.

Entonces, en el programa principal, cuando el usuario active la opción "eliminar Producto", tu tendrás que pedirle al usuario el ID de producto que quiere eliminar.
Con ese ID construyes un objeto Producto y se lo pasas a este método para que lo elimine del ArrayList de Inventario.

No hace falta que construyas un Producto con los datos idénticos a como esté en el ArrayList. No necesitas saber la cantidad ni la descripción del producto ni ningún otro valor, solo el ID.
Recuerda que Producto tiene un método llamado equals() en donde se indica que dos objetos Producto son IGUALES si coincide su ID.

Así que te basta con construir un Producto con el ID indicado, y el resto de parámetros del constructor pueden tener cualquier valor, no importa. Solo necesitas que coincida el ID.

Por ejemplo, mi clase Producto que escribí para este ejercicio, si recuerdas, tiene dos constructores y uno solo pide un String con el ID y un valor int para la cantidad.
Yo a ese int le puedo poner cualquier valor, no tiene que coincidir con la cantidad que marca el Inventario para poder borrarlo.
El código podría ser algo así:
Código: [Seleccionar]
System.out.print("Indique el ID del producto a eliminar: ")
String idPRoducto = stdIn.readLine();
if (inventario.eliminarProducto(new Producto(idProducto, 0)) //Pongo una cantidad cualquiera
    System.out.println("Producto ha sido eliminado");
else
    System.out.println("No hay ningún Producto con ese ID en el Inventario");


788
Muchas gracias por todo nwn mi programa ya funciona como deberia y todo gracias a ti nwn Enserio muchas gracias!!
De nada, un placer.

En mi caso necesito poder agregar una  factura, esta debe de contener el IDVendedor, Producto vendido(id), Cantidad (Ademas esta cantidad debe de sustraerse de un producto en caso de existir), Y poder ordenar las facturas de acuerdo a la fecha.
Seria de gran ayuda si pudieses ayudarme, estoy algo perdido en esto tambien jaja..
Preguntas:
¿En una Factura solo habrá un Producto vendido?
En el código que hice para la compañera sapph24, di por hecho que en una Factura puede haber facturado más de un artículo.
Por eso decidí que cada Factura debería tener algún tipo de colección para guardar la info de estos Productos facturados y escogí un ArrayList que contenga objetos Productos.

Pero si solo va a contener un único producto, no será necesario ningún tipo de array. Bastará con un String para el ID producto y un int para la cantidad.

Sobre la fecha. ¿Se ha decidido qué formato va a tener?
Para la fecha se va a usar la clase String y si queremos poder ordenar por fecha, hay que tener muy claro que formato va a tener:
- Dias, mes y año: ddmmaaaa
- Mes, dia y años: mmddaaaa
- Año, mes y dia: aaaammdd (la más comoda para ordenar)

¿Se introduce sin separadores o con separadores?, ejemplos:
- dd-mm-aaaa
- dd/mm/aaaa

El algortimo que ordene fechas dependerá de cuál sea el formato de esta.

789
Vale, creo que te entiendo.
A ver, he añadido las nuevas clases a mi código, lo básico para ver como hacer lo que pides.

A mi modo de ver, sería interesante que cada Factura incluyese su propio ArrayList con los objetos Producto que se están facturando. Así es más cómodo gestionarlo todo.

Mira, esta podría ser la clase Producto con los atributos básicos que supongo ha de tener.
Fíjate que aquí se me ha ocurrido ponerle dos constructores.

En uno, se pide el id de producto, la descripcion y la cantidad.
Este constructor podría ser más útil para añadir Productos al Inventario, ya que aquí los añadiremos una única vez y querremos que tengan su descripción más completa.

En el otro constructor, solo se pide el id y la cantidad. No se pide la descripción.
Este sería más cómodo para cuando tengamos que añadir Productos a una Factura, nos ahorramos tener que poner la descripción.

Que no es necesario hacerlo así, esto es algo que se me ha ocurrido sobre la marcha y aunque luego no se aplique, está bien saber que existe esta posibilidad de tener distintos constructores para elegir según cada ocasión.

Atenta a un método llamado variarCantidad(). Este método, a diferencia de setCantidad(), lo que hará será sumar la cantidad recibida por argumentos a la cantidad actual, y será uno de los método que nos ayudará a cumplir con lo que pides.
Si le pasasemos una cantidad con valor negativo, entonces dicha cantidad se restaría.
Por eso he preferido llamarlo variarCantidad() en lugar de por ejemplo sumarCantidad(), aunque el nombre es irrelevante.

Por supuesto, la clase también incorpora el método equals(), pues dentro de nuestro Inventario no deberían haber objetos Producto repetidos.

Aquí la clase Producto

Código: [Seleccionar]
public class Producto {

private String idProducto;
private String descripcion;
private int cantidad;

/*
* Podemos hacer dos constructores.
* Uno donde no sea necesario dar la descripcion, ideal para añadir Productos en Facturas.
* Otro donde sí se pida la descripción, más adecuado para añadir Productos al Inventario.
*/

public Producto(String id, int cant) {
idProducto = id;
cantidad = cant;
setDescripcion("");
}

public Producto(String id, String descripcion, int cant) {
idProducto = id;
cantidad = cant;
this.setDescripcion(descripcion);
}

public String getIdProducto() {
return idProducto;
}

public String getDescripcion() {
return descripcion;
}

public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}

public int getCantidad() {
return cantidad;
}

public void setCantidad(int cantidad) {
this.cantidad = cantidad;
}

public void variarCantidad(int cantidad) {
this.cantidad += cantidad;
}

@Override
public boolean equals(Object obj) {
if (obj instanceof Producto) {
Producto otroProducto = (Producto)obj;

return idProducto.equals(otroProducto.getIdProducto());
}
else
return false;
}
}


Los Productos se guardarán en la clase Inventario, que básicamente es un ArrayList donde insertar Productos.
Como ya hemos dicho, no pueden haber Productos con el mismo ID, así que antes de insertar un Producto se comprueba si ya existe en el inventario o no.

Aquí hay que destacar el método actualizaCantidadProducto() (sí, es muy largo, puedes ponerle uno más corto si quieres)
Este método lo que hace es recibir un ID de producto y una cantidad, y entonces buscará en el Inventario el Producto que coincida con ese ID y le sumará la cantidad recibida.
De este modo, cuando tengamos una Factura, con este método podremos actualizar el Inventario con las nuevas cantidades indicadas por la Factura.

Clase Inventario
Código: [Seleccionar]
public class Inventario {

private ArrayList<Producto> productos;

public Inventario() {
productos = new ArrayList<Producto>();
}

public boolean addProducto(Producto producto) {
if (productos.contains(producto))
return false;
else
return productos.add(producto);
}

public boolean actualizaCantidadProducto(String idProducto, int cantidad) {
for (Producto prod: productos) {
if (prod.getIdProducto().equals(idProducto)) {
prod.variarCantidad(cantidad);
return true; //Producto encontrado y actualizado
}
}
//Si el bucle termina sin retornar true, es que no se ha encontrado producto con ese id
return false;
}

}


Ahora pasamos a las Facturas.
Como dije antes, además del ID y su atributo notas, me parece interesante que tenga un ArrayList donde se irán añadiendo los Productos que se van a facturar.

A la hora de agregar Productos, al tratarse de una Factura y no el Inventario, en este caso podríamos admitir tener Productos repetidos (mismo ID) en el ArrayList de la Factura.
Pero, creo que es más interesante hacer que cuando se quiera registrar un Producto que ya está listado en la Factura, lo que podemos hacer es sumar la cantidad del Producto recibido a la cantidad del Producto que ya está en la Factura.
Así cada Producto solo estará listado una sola vez, pero con todas las cantidades que se hayan querido facturar.

De todo esto se encargará el método addProducto()
Fíjate que hay otro método llamado getProductosFacturados() que retorna el ArrayList completo.
Este método nos será útil para, cuando en el programa principal hayamos registrado una Factura, obtener los Productos de los cuáles hemos de actualizar las cantidades en el Inventario. Eso lo veremos luego.

Aquí clase Factura, con su obligado método equals(), pues no podemos admitir Facturas repetidas con el mismo ID.
Código: [Seleccionar]
public class Factura {

private String idFactura;
private String nota;
private ArrayList<Producto> productosFacturados;

public Factura(String id, String nota) {
idFactura = id;
this.nota = nota;
productosFacturados = new ArrayList<Producto>();
}

public String getIdFactura() {
return idFactura;
}

public String getNota() {
return nota;
}

public void setNota(String nota) {
this.nota = nota;
}

public void addProducto(Producto producto) {
if (productosFacturados.contains(producto)) {
/*
* Si ya existe este Producto en la factura, en lugar de
* añadirlo, lo que haremos será sumar las cantidades.
* Así en la Factura cada producto aparece una sola vez
* pero con la suma de todas las cantidades facturadas
*/
int indice = productosFacturados.indexOf(producto); //Averiguamos indice del producto coincidente
productosFacturados.get(indice).variarCantidad(producto.getCantidad()); //Sumamos las cantidades
}
else
productosFacturados.add(producto);
}

public ArrayList<Producto> getProductosFacturados() {
return productosFacturados;
}

@Override
public boolean equals(Object obj) {
if (obj instanceof Factura) {
Factura otraFactura = (Factura)obj;
return idFactura.equals(otraFactura.getIdFactura());
}
else
return false;
}

}

Y ahora necesitamos una clase para Registrar las Facturas.
Siguiendo la mecánica habitual, se va a tratar básicamente de un ArrayList para insertar Facturas y que impedirá que estas se repitan.
No tiene nada destacable.

Código: [Seleccionar]
public class FacturasRegistro {

private ArrayList<Factura> facturas;

public FacturasRegistro() {
facturas = new ArrayList<Factura>();
}

public boolean addFactura(Factura factura) {
if (facturas.contains(factura))
return false;
else
return facturas.add(factura);
}

}

OK. Ya tenemos todos los elementos necesarios para trabajar. Ahora hay que ver como los ponemos en funcionamiento.

No se exactamente como quieres que funcione esto en el programa principal.
Pero vamos a suponer que en el menu habrá una opción para registrar una Factura y que al registrarla, se actualicen las cantidades de los Productos facturados en el Inventario.
La secuencia de pasos podría ser:
- Pedir datos para crear una nueva Factura.
- Pedir datos para crear Productos hasta que el usuario indique finalizar.
   Los Productos que se van creando se añaden a la nueva Factura
- Cuando la Factura está lista, se inserta en el Registro de Facturas.
- Si la Factura se ha registrado correctamente (su ID no estaba repetido), entonces actualizamos los Productos del Inventario con las nuevas cantidades.
- Informamos por pantalla de si algún Producto no se ha podido actualizar, quizás porque en el Inventario no se ha encontrado un Producto coincidente con alguno de los que hay en la Factura.



Bien, pues todos estos pasos, son los que va a realizar este método que estaría declarado en la clase Sistema (la principal) y podría ser invocado desde una de las opciones del menú.

Código: [Seleccionar]
private static void facturarArticulos() {
//Creamos factura
System.out.println("Ingresar datos factura: ID, Notas");
try {
Factura nuevaFactura = new Factura(stdIn.readLine(), stdIn.readLine());
//Tenemos factura creada. Hay que añadir artículos.
String idProducto = "";
int cantidad = 0;
do {
System.out.println("Facturando productos.");
System.out.println("Teclee ID del Producto a facturar o escriba 'fin' para terminar");
System.out.println("ID Producto: ");
idProducto = stdIn.readLine();
if (!idProducto.equals("fin")) {
System.out.print("Cantidad a facturar: ");
cantidad = Integer.parseInt(stdIn.readLine());
//Añadimos producto a Factura
nuevaFactura.addProducto(new Producto(idProducto, cantidad));
}
}while(!idProducto.equals("fin"));
//Articulos añadidos a Factura. Hay que registrarla.
if (facturasRegistro.addFactura(nuevaFactura)) {
//La Factura ha sido aceptada, hay que actualizar el inventario.
System.out.println("Factura aceptada.\nSe va a actualizar el Inventario");
for (Producto prod: nuevaFactura.getProductosFacturados()) {
if (inventario.actualizaCantidadProducto(prod.getIdProducto(), prod.getCantidad()))
System.out.println("Producto con ID " + prod.getIdProducto() + "actualizado" );
else {
System.out.println("\nProducto con ID " + prod.getIdProducto() + "no actualizado");
System.out.println("Este producto no existe en el Inventario.\n");
}
}
}
else //La factura NO ha sido aceptada en el Registro.
System.out.println("Factura no registrada. Ya existe una factura con ese ID");
} catch (IOException e) {
System.out.println("Error en lectura de datos.");
} catch (NumberFormatException e) {
System.out.println("El dato introducido no es un número");
}
}

Parece que hay mucho código, pero no es tanto y básicamente lo que hace es seguir los pasos que he descrito antes.

Fíjate que cuando actualizamos las cantidades de los Productos del Inventario, si ocurre el caso de que un Producto que está en la Factura, no está en el Inventario (y por tanto no se puede actualizar ninguna cantidad), aquí se nos abre la posibilidad de añadir directamente ese Producto al Inventario.
Aunque es un Producto que en ese momento no tiene nada en su atributo Descripcion, y quizás antes de añadirlo habría que pedirle al usuario este dato.

Pero bueno, esto lo comento como una posibilidad que puedes decidir añadirla o no. Tampoco es necesario crear el "programa perfecto", si no aprender a a trabajar con distintas clases, aprender como relacionarlas y como almacenarlas en distintas colecciones de datos.

Creo que esto es más o menos lo que querías conseguir. Leete con detenimiento cada clase que he posteado, asegurate de que te queda claro para qué vamos a necesitar cada uno de los métodos que he destacado y sobre todo entender bien este último método, que simplemente cumple con los pasos enumerados anteriormente.

He de decir que no he tenido ocasión de probar su funcionamiento.
Lo he escrito pero no he podido probarlo...estoy bastante seguro de que funciona, pero nunca se sabe je je :P
 
Cualquier duda o cosa que no entiendas, ya sabes, pregúntala por aquí.
Un saludo.

790
OK, nos ponemos con la clase Sistema.

Para leer datos por teclado y mostrar datos en pantalla has declarado las clases BufferedReader y PrintWriter.

No se si es necesario que uses estos objetos porque te lo pide el enunciado o porque es como habitualmente te han enseñado tus profesores o no se...

Yo usaría un simple Scanner para leer datos por teclado y el comodísimo System.out.println() para mostrar en pantalla (de hecho, es lo que usas en el metodo menu() ).
Voy a seguir usando el BufferedReader para no alejarme mucho de lo que tu has hecho, pero el PrintWriter no, porque aunque desactive el "autoflush" me muestra todos los textos en pantalla sin haber llegado aún a esas partes del código, y bueno, yo no me aclaro con esta clase xD

Aunque el BufferedReader tampoco me hace gracia porque siempre va a exigir usar un try catch para leer datos, aunque sean por teclado, cosa que no pasa con el Scanner.


Luego ya sí que veo algo de confusión en la estructura del código...
- Hay un constructor vacío

- hay declarado el objeto CatalogoCliente... pero luego más adelante, mezclado entre los
métodos, hay igualmente declarado un ArrayList de Clientes..

- Hay un bucle while(true).
Esto de usar condicion true para que un bucle se repita, aunque es válido, es una mala práctica de programación.
Y sorprendemente lo enseñan muchísimos docentes. Pero no es buena práctica ya que no se aplica un flujo realmente lógico a la estructura del programa.
Y además, no es necesario hacerlo así. Nunca lo es.

- Para pedir que se elija opción, se usa un poco arbitrariamente las excepciones.
Si el usuario introduce un número de opción no válido, se lanza un NumberFormatException. Este excepción debería representar errores en el formato de un valor numérico, no que se haya escogido una opcion inexistente.
Vale que luego en la práctica nos puede servir, pero tampoco es buena práctica usar las excepciones que no representan lo que realmente ha ocurrido.
En todo caso, deberíamos crear nuestra propia excepción.
Pero vamos, en realidad para controlar esto de las opciones del menú, no se necesitan excepciones.

- Los métodos y algunos objetos NO están declarados como static, con lo cual se hace necesario instanciar un objeto de la clase Sistema para poder acceder a ellos.
Y, aunque esto es opcional, creo que lo más cómodo sería declararlo todo como static para poder acceder a todo desde el main sin tener que instanciar ningún objeto Sistema.

Voy a cambiarlo todo más o menos a como yo lo haría, y tu ya decides si te sirve o no.

- En el método para añadir un nuevo Cliente.
El usuario ha de introducir cada uno de los datos para los atributos.
Lo podemos hacer de dos formas:
1 Almacenar cada uno de esos datos en sus variables separadas y luego pasarselas al contructor del nuevo cliente que vamos a crear.
2 Instanciar el nuevo cliente y hacer la lectura de datos directamente en el contructor.

Para ahorrar tiempo lo he hecho de la segunda forma, aunque he decir que no me gusta mucho. Normalmente prefiero leer datos uno a uno, cada uno en su variable, y luego se las paso todas al constructor del objeto.


Bueno, este mensaje incorpora al final el código de todas las clases.

Pruébalo. El programa ya funciona y permite añadir nuevos clientes y también listarlos en pantalla.

Mira bién el código, pregunta lo que no entiendas y si ya lo tienes claro, intenta completar lo que te falta.

Si hubiera que cambiar algo por alguna exigencia del enunciado o de tus profesores, comentalo e intentamos adaptarlo.

Un saludo.


Clase CatalogoClientes


Código: [Seleccionar]
package catalogoClientes;

import java.util.ArrayList;
import java.util.Iterator;

public class CatalogoClientes implements Iterator<Cliente>{

private ArrayList<Cliente> clientes;

public CatalogoClientes() {
clientes = new ArrayList<Cliente>();
}

/*
* Metodo para añadir Cliente. al ArrayList
* Devuelve TRUE si el cliente es aceptado.
* Devuelve FALSE si es rechazado, porque ya existe
* un Cliente con el mismo ID.
*/
public boolean addCliente(Cliente cliente) {
if (clientes.contains(cliente)) //Ya existe un cliente con el mismo ID
return false; //Lo rechazamos
else {
clientes.add(cliente); //Aceptamos el Cliente
return true;
}
}

public Cliente getCliente (int indice) {
return clientes.get(indice);   
}

public Cliente getClienteByID(String idCliente) {
for (Cliente cli: clientes) {
if (cli.getIdCliente().equals(idCliente))
return cli;
}
return null;
}

public boolean eraseCliente (Cliente cliente) {
return clientes.remove(cliente); //Si el cliente no existiese, retornara false
}

public void ImprimirAll () {
System.out.println("\n\t\tListado Clientes");
if (clientes.isEmpty())
System.out.println("No hay Clientes registrados para mostrar.");
else
for(int i=0;i<clientes.size();i++){
System.out.println(clientes.get(i));   }
}

public Iterator<Cliente> IteradorCliente () {
return clientes.iterator();   }

@Override
public boolean hasNext() {
// TODO Auto-generated method stub
return false;
}

@Override
public Cliente next() {
// TODO Auto-generated method stub
return null;
}
}


Clase Cliente


Código: [Seleccionar]
package catalogoClientes;

public class Cliente extends Persona{
private String idCliente;

public Cliente (String Nombre, String RFC,String Direccion,String Telefono,String RazonSocial, String idCliente) {
super(Nombre, RFC, Direccion, Telefono, RazonSocial);
this.idCliente = idCliente;        }

public String getIdCliente(){
return idCliente;    }

public void setIdCliente(String idCliente) {
this.idCliente=idCliente;   }

public String toString() {
return super.toString() + " ID cliente " + idCliente;   }

public boolean equals (Object obj) {       
if (obj instanceof Cliente) {
//Es un objeto Cliente, hacemos casting para poder comparar atributo idCLiente
Cliente otroCLiente = (Cliente)obj;

if (this.idCliente.equals(otroCLiente.getIdCliente()))
return true; //Los ID son iguales, así que ambos objetos son el MISMO Cliente
else
return false; //Los ID son distintos, así que los objetos son CLientes distintos
}
else
return false; //No es un objeto Cliente, así que son cosas distintas
}
}


Clase Persona

Código: [Seleccionar]
package catalogoClientes;

public class Persona {

private String Nombre, RFC, Direccion,Telefono,RazonSocial;

//ArrayList<Persona> personas= new ArrayList<Persona>();

public Persona(String Nombre, String RFC,String Direccion,String Telefono,String RazonSocial){       
this.Nombre=Nombre;
this.RFC=RFC;
this.Direccion=Direccion;
this.Telefono= Telefono;
this.RazonSocial= RazonSocial;}

public String getNombre(){
return Nombre;}

public void setNombre(String Nombre) {
this.Nombre=Nombre;}
//
public String getRFC(){
return RFC;}

public void setRFC(String RFC) {
this.RFC=RFC;}
//
public String getDireccion(){
return Direccion;}

public void setDireccion(String Direccion) {
this.Direccion=Direccion;}
//
public String getTelefono(){
return Telefono;}

public void setTelefono(String Telefono) {
this.Telefono=Telefono;}
//

public String getRazonSocial(){
return RazonSocial;}

public void setRazonSocial(String RazonSocial) {
this.RazonSocial= RazonSocial;}

public String toString(){
return "Nombre: "+getNombre()+" RFC: "+getRFC()+ " Direccion:"+getDireccion()+
" Telefono: "+getTelefono()+" Razon Social: "+getRazonSocial();
}
}


Clase Proveedor

Código: [Seleccionar]
package catalogoClientes;

public class Provedor extends Persona{
private String idProvedor;
private String fax;
private String email;

public Provedor(String idProvedor, String fax, String email, String Nombre,
String Direccion, String RFC, String Telefono, String RazonSocial){
super(Nombre,Direccion,RFC,Telefono,RazonSocial);
this.idProvedor = idProvedor;
this.fax = fax;
this.email = email; }

public String getIdProvedor(){
return idProvedor;}

public void setIdProvedor(String idProvedor) {
this.idProvedor=idProvedor; }
//

public String getfax(){
return fax; }

public void setFax(String fax) {
this.fax=fax;
}
//

public String getEmail(){
return email;  }

public void setEmail(String email) {
this.email=email;}
}


Clase Sistema

Código: [Seleccionar]
package catalogoClientes;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Sistema {
private static BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
//private static PrintWriter stdOut = new PrintWriter(System.out, true);   
private static CatalogoClientes catalogoClientes = new CatalogoClientes();


public static void main(String[] args){
int opcion = 0;
do {
opcion = pedirOpcion();
switch (opcion) {
case 1:
addCliente();
break;
case 2:
//añadir proveedor
break;
case 3:
catalogoClientes.ImprimirAll();
break;
case 4:
//mostrar proveedores
break;
case 5:
//añadir producto
break;
case 6:
System.out.println("\n\t\tFIN DE PROGRAMA");
}
}while (opcion != 6);
}

private static void menu() {
System.out.println("\nSeleccione la acción: "+"\n"
+"[1] Añadir Cliente\n"
+"[2] Añadir Provedor\n"
+"[3] Mostrar Clientes\n"
+"[4] Mostrar Provedores\n"
+"[5] Añadir Producto\n"
+"[6] CERRAR PROGRAMA");
System.out.print("choice> ");
}


private static int pedirOpcion() {
menu();
try {
int op = Integer.parseInt(stdIn.readLine());
if (op >= 1 || op <= 6)
return op; //La opcion es válida, podemos retornarla como valida.
else {
System.out.println("Elija solo entre [1-6]");
return -1; //Opcion no válida, devolvemos cualquier int distinto de 1-6...
           //...para que el SWITCH lo ignore
}
} catch (NumberFormatException e) {
System.out.println("El dato introducido no es un número");
return -1;
} catch (IOException e) {
System.out.println("Error al leer los datos");
return -1;
}
}

private static void addCliente() {
System.out.println("Ingrese los datos: [Nombre, RFC, Direccion,Telefono, Razon Social,ID Cliente]");
try {
Cliente nuevoCliente = new Cliente(stdIn.readLine(), stdIn.readLine(), stdIn.readLine(),
stdIn.readLine(), stdIn.readLine(), stdIn.readLine());

//Intentamos añadir el nuevo cliente creado. Si estuviera repetido (mismo ID), será rechazado
if (catalogoClientes.addCliente(nuevoCliente))
System.out.println("\nNuevo Cliente registrado...");
else
System.out.println("\nNuevo Cliente rechazado.\nYa existe un Cliente con el ID " + nuevoCliente.getIdCliente());
} catch (IOException e) {
System.out.println("Error en lectura de datos\nNo se pudo crear el Nuevo Cliente");
}

}
}

791
Sobre la clase CatalogoCliente aquí me surge una pregunta.
¿Es necesario que implemente la interfaz Iterator?
Yo es que nunca he necesitado usar iteradores, aunque bueno, solo soy un aprendiz más, apenas un aficionado a la programación. Pero bueno, como tampoco sé todavía cuáles son las funcionalidades completas que ha de tener tu programa, lo dejamos implementado.

Lo que si creo que está mal, es que dentro de esta clase está trabajando con la clase Persona.
Si esto va a ser un Catálogo de Clientes, lo normal, es que trabajemos con la clase Cliente, no con la clase Persona.
Así que hay que modificar todos los métodos, parámetros y argumentos para que hagan referencia a la clase Cliente en lugar de a Persona.

Además hay que aumentar la funcionalidad de algunos métodos.

Por ejemplo, el metodo para añadir un Cliente al ArrayList.
- Los Clientes tienen un idCliente que los identifica y los hace únicos.
- Si los hace únicos, significa que en nuestro ArrayList no podemos permitir tener dos Clientes con el mismo id, ya que en realidad serían el mismo.
- Para impedir que se nos repitan los Clientes en el ArrayList, nos ayudará el método equals() que le habíamos escrito a la clase Cliente, que es precisamente para que nos ayude en estos casos.

El método lo podemos reescribir así:

Código: [Seleccionar]
public boolean addCliente(Cliente cliente) {
if (clientes.contains(cliente)) //Ya existe un cliente con el mismo ID
return false; //Lo rechazamos
else {
clientes.add(cliente); //Aceptamos el Cliente
return true;
}
}

Aquí el ArrayList se llama clientes.
Y antes de añadir el nuevo cliente, le preguntamos al ArrayList si ya contiene ( contains() ) un cliente "igual" al que pretendemos añadir.
Nosotros no lo vemos, pero contains() lo que hace es recorrer todos los objetos Cliente dentro del ArrayList y va preguntando si alguno es igual ( equals() ) al CLiente que le hemos pasado por argumentos.

Si encuentra alguno igual, es decir, uno que tenga el mismo ID (ya que a la clase CLiente le hemos dicho que el atributo IdCliente es quien decide si son iguales o no) devolverá TRUE y en este caso, no debemos aceptar este Cliente, porque ya lo tenemos en el listado y si lo aceptamos lo tendremos repetido.

Así que nuestro método para añadir Clientes, ya no los acepta directamente sin más. Comprueba si está repetido o no y además informa con TRUE/FALSE si el Cliente ha sido aceptado/rechazado.

Además hay un método para buscar una Persona por su ID. Como ya hemos dicho, lo correcto sería que buscase Clientes por ID, no personas.
Lo modificamos y además te recuerdo que para comparar datos de tipo String, hay que usar el método equals()
Si usas el comparador básico de igualdad, el doble igual,  == no te dará resultados válidos.
Este comparador solo sirve para tipos primitivos como int, char, double, float, byte...
Para comparar objetos (los String son objetos, no son primitivos) hay que usar equals()

Código: [Seleccionar]
public Cliente getClienteByID(String idCliente) {
for (Cliente cli: clientes) {
if (cli.getIdCliente().equals(idCliente))
return cli;
}
return null;
}

Ahora viene un método para eliminar Clientes del ArrayList pasando como argumento un objeto Cliente.
EL método que has puesto está bien, pero quizás sería interesante que el método informase de si ha eliminado o no el Cliente.
¿Y si nos han pasado un Cliente para eliminar, que en realidad no existe en el ArrayList?
Quizás sería conveniente informar al usuario de este hecho, ya que quizás así se de cuenta de que nos ha pasado un Cliente posiblemente equivocado.
Si no le informamos, el usuario pensará que nos ha pasado un Cliente correcto y que ha sido eliminado, cuando en realidad no ha sido así.

Mejor informarle de qué resultado ha dado su intento de borrado, y además es muy fácil de hacer, ya que el metodo remove() del ArrayList ya devuelve true/false según si ha borrado o no.
Así que nos basta con retornar, lo que esté retornando este método:

Código: [Seleccionar]
public boolean eraseCliente (Cliente cliente) {
return clientes.remove(cliente); //Si el cliente no existiese, retornara false
}

Luego tienes un método llamado imprimirAll() para mostrar todos los Clientes del listado en pantalla. Has puesto que devuelve un String, pero no es necesario que retorne nada, basta con que escriba en pantalla y punto. Nada más.
Así que lo declaramos como void y quitamos la sentencia return

Código: [Seleccionar]
public void ImprimirAll () {   
for(int i=0;i<clientes.size();i++){
System.out.println(clientes.get(i));   }
}

Los siguientes métodos están relacionados con el Iterator, que no se si los necesitaremos o no realmente. De momento los dejamos tal cual.

Ahora viene el programa principal, de este también hablaremos en otro post separado.

792
Hola. Te voy a comentar varias cosas y hacer varias correciones.

Lo haré en varios mensajes para no hacer un único post gigantesco.

En el último incluiré todo el código tal y como lo he cambiado yo, para que lo uses, lo mires, preguntes dudas y lo que sea.

Intentaré hacerlo lo más parecido a como lo estás desarrollando tú.

Vamos por partes.

Los atributos de las clases deberían ser private, no public.

Por convención, los atributos se declaran privados de manera que solo se pueda interactuar con ellos mediante los getters y setters.

Si se declaran públicos, los getters y los setters son totalmente innecesarios, ya que los atributos son visibles a cualquier otra clase que vaya a interactuar con nuestro objeto. Pero esto la mayoría de las veces no nos interesa que ocurra.

Los getters y setters nos permiten, si queremos, controlar de que manera se acceden a los atributos y podemos poner condiciones para por ejemplo controlar la calidad de los datos.

Te pongo un ejemplo.
Supon que la clase Persona tiene un atributo llamado edad.
Si este atributo lo pongo como public, entonces se podrá acceder directamente al atributo para leer/escribir su contenido. Y esto implica que podrían ocurrir cosas como esta:

Código: [Seleccionar]
Persona persona1 = new Persona();
persona1.edad = -984;

Ahora tengo una persona con -984 años, lo cual es absurdo. La "calidad" de ese atributo es pésima, nula... cualquier cálculo que quiera hacer ahora con este atributo me dará resultados igualmente absurdos.

Sin embargo, si lo declaro como private, ya no se puede acceder directamente al atributo como hemos visto en el ejemplo anterior. Ahora el compilador va a obligar a que se usen los getters y setters, los cuáles sí me van a permitir poner controles a la calidad de los datos.

Si alguien intenta hacer esto:
Código: [Seleccionar]
Persona persona1 = new Persona();
persona1.setEdad(-984);

No le va a funcionar, porque yo en mi setter habré puesto aunque sea un control mínimo de la calidad del dato:
Código: [Seleccionar]
public boolean setEdad(int edad) {
    if (edad < 0) {
        System.out.println("ERROR: La edad no puede ser un valor negativo");
        return false; //Informo de que no ha sido aceptado este valor
    }
    else {
        this.edad = edad;
        return true; //Informo de que SI hemos aceptado este valor
    }
}

Como puedes ver, con los setter puedo controlar la forma de aceptar datos y con los getter podría controlar cómo los devuelvo.
Para obligar a que se usen los getters/setters, me conviene declarar los atributos como private para que sea imposible acceder a ellos directamente.


Otra cosa que quería comentar.
Tu clase Persona, ha de definir como vamos a modelar la representación de una persona.
Es decir, vamos a definir que atributos tiene una persona del "mundo real", pero solo los que nos interesen para la funcionalidad de nuestro programa.

Por eso ponemos los atributos: Nombre, RFC, Direccion,Telefono,RazonSocial;
Porque una persona tiene un nombre, tiene una dirección, un teléfono...

Pero ojo, una persona del "mundo real", no tiene a "muchas otras personas" en su interior...
Lo digo porque has puesto un ArrayList de Personas, como atributo de Persona. Y esto no parece que tenga sentido, así que este atributo no es necesario.
Citar
public class Persona {
   private String Nombre, RFC, Direccion,Telefono,RazonSocial;

ArrayList<Persona> personas= new ArrayList<Persona>();

Y ojo, en programación todo es posible. Si podría ocurrir alguna situación en que necesitemos que una clase Persona, contenga un arrayList de Personas, si por ejemplo quisieramos modelar los hijos que tiene una persona, así que ese ArrayList contendría a sus descendientes.
Pero no es este el caso que nos trae.


Hablemos ahora de tu clase Cliente.
Tiene un único atributo, llamado idCLiente, que como dije antes, ha de ser declarado como private.

Tu método equals() es erróneo en dos sentidos:
Uno, en sintaxis, pues intentas acceder a atributos como Nombre, RFC, Dirección directamente.

Código: [Seleccionar]
public boolean equals (Object obj) {       
if (obj instanceof Cliente) {
Cliente tmpCliente= (Cliente)obj;         
if (super.equals(tmpCliente)   && Nombre.equals(tmpCliente.getNombre())&&RFC.equals(tmpCliente.getRFC())&&Direccion.equals(tmpCliente.getDireccion())&&Telefono.equals(tmpCliente.getTelefono())&&RazonSocial.equals(tmpCliente.getRazonSocial())&&idCliente.equals(tmpCliente.getIdCliente())); {
return true;
}   }
return true;
}
Eso no puedes hacerlo así, de hecho el compilador muestra un warning avisando de que no tiene ni idea de que variables son esas.

Si quieres acceder a esos atributos, has de pedirselos al super mediante getters. Por ejemplo:
Código: [Seleccionar]
super.getNombre().equals(tmpCliente.getNombre()

Dos, a parte de los errores de sintaxis, hay errores de lógica.
Varios, como que por ejemplo no se retorna false en ningún momento, pero el importante que hay que destacar es la elección de atributos para decidir si dos objetos Cliente son equivalentes/iguales.

El nombre, o la dirección, etc.... no deberían ser determinantes para decidir si dos clientes son equivalentes/iguales o no.
El atributo decisivo debería ser solo el id de Cliente. (salvo que el enunciado de este ejercicio indique lo contrario)
EL id, es el identificador que debemos asegurarnos que sea único e irrepetible, y por lo tanto ese debería ser el único atributo decisivo para decidir si dos objetos Cliente son equivalentes.
Del siguiente modo, quedaría más correcto:

Código: [Seleccionar]
public boolean equals (Object obj) {       
if (obj instanceof Cliente) {
//Es un objeto Cliente, hacemos casting para poder comparar atributo idCLiente
Cliente otroCLiente = (Cliente)obj;

if (this.idCliente.equals(otroCLiente.getIdCliente()))
return true; //Los ID son iguales, así que ambos objetos son el MISMO Cliente
else
return false; //Los ID son distintos, así que los objetos son CLientes distintos
}
else
return false; //No es un objeto Cliente, así que son cosas distintas
}

Sobre la clase Provedor, solo recordar que los atributos deberían ser private.
A esta clase no le has puesto método equals(), si fuera necesario ponérselo, de nuevo lo más lógico sería que el atributo decisivo fuera solo y únicamente el id de proveedor.

Sobre la clase CatalogoCliente, continuo en otro mensaje para que este no sea demasiado extenso.

793
Aquí te falta el indice i

Citar
  if (edad.ToString()[i] == edad.ToString()[j])

Por otro lado, no se mucho de C# (¿o estas con VB.net?).
Me baso en tu código para suponer que es correcto usar indices tras la invocación de ToString()

Es más, esto me resulta un poco extraño_
Código: [Seleccionar]
int edad = listBox1.Items.Count;
A mí eso me suena que estás metiendo en la variable edad la longitud de listBox1.
Es decir, eso te daría cuántos items tiene ese listBox.

Pero no se si estás metiendo la edades. Apostaría que no, pues la variable edad es un simple int, así que tan solo puede almacenar un único valor numérico.
Como mucho, podrías guardar una de las edades.., pero es que me parece que ni siquiera eso es lo que contiene, sino como dije, lo que tiene es la longitud de listBox1.

Siento no poder ayudarte mejor, lo mio es Java... pero, desde mi desconocimiento de otros lenguajes, diría que ese código no está haciendo lo que tú necesitas y tendrías que revisarlo.

Espero que alguien más hábil que yo en este foro te pueda orientar.

794
en el if estás comparando exactamente el mismo número:

Citar
if (numeros.ToString()[i] == numeros.ToString()[i])

Cámbialo así:

Citar
if (numeros.ToString()[i] == numeros.ToString()[j])

Por otra parte, el valor de maximaVecesQueSeRepite ha de ser actualizado cada vez que encuentres un número que supere ese máximo de repeticiones. Fijate que su valor SIEMPRE es 0, nunca está variando, así que cualquier número que se repita una sola vez, lo va a superar.

Supongo que te interesa actualizarlo así:

Citar
                if (vecesQueSeRepite > maximaVecesQueSeRepite)
                {
                    moda = numeros;
                    maximaVecesQueSeRepite = vecesQueSeRepite;
                }


795
No necesitas hacer push() como si trabajases con una pila(stack).

Simplemente accede a los elementos valiendote del índice que va aumentando progresivamente gracias al bucle for

Código: [Seleccionar]
function productoria (num){
    var multiplicar = 1;
  for (var i = 0; i < num.length; i++){
     multiplicar = multiplicar * num[i];
  }
  return multiplicar;
}

796
Hola de nuevo.

No he podido evitar modificar el programa para proporcionarle una interfaz gráfica aunque sea sencilla.

EL juego y las reglas son las mismas, solo que ahora vemos un tablero donde se van dibujando los rectángulos en dos colores: azul para jugador 1, rojo para jugador 2.

La única diferencia es que ahora el juego no es automático y hay dos botones para el usuario.
Uno insertar rectángulos, cada vez que se pulse se intentará insertar el rectángulo generado y cambiará de turno para que la próxima vez que se pulse el rectángulo generado será para el otro jugador.

El otro botón elimina rectángulos, pero he conservado el factor azar. Es decir, cuando le jugador pulse para eliminar rectángulos del rival, su éxito dependerá del azar y de hecho la mayoría de veces su intento fracasará, perderá turno y contará como intento fallido.

Igual que antes, cuando ambos jugadores sumen dos intentos fallidos, el juego termina y se muestra el resumen del juego indicando quien ha ganado.

Adjunto el código para quien quiera probarlo o modificarlo. No es muy elegante porque lo he hecho con prisas y sobre el código anterior que no estaba pensado para usar con una GUI, pero bueno, es sencillo.

Aquí en el main podemos indicar las dimensiones del tablero:
Código: [Seleccionar]
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(
new Runnable() {
public void run() {
new Gr(20, 35);
}
}
);
}

El método que decide si tiene éxito o no el intento de eliminar Rectángulos al rival es este de la clase Gr

Código: [Seleccionar]
private boolean decidirEliminarRectangulo() {
Random aleatorio = new Random();
int num = aleatorio.nextInt(100)+1;
if (num % 5 == 0) {
return true;
}
else
return false;
}

Como expliqué antes, solo si se genera un número aleatorio múltiplo de 5 tendrá exito el intento.

Son pocas probabilidades, si alguien quiere mayor probabilidad de éxito solo tiene que cambiar este método escribiendo su propia condición de éxito.

Todo el código está en el archivo zip adjunto.

Un saludo

797
Clase GR

Es la clase que pone en marcha el juego.

Tiene 4 atributos/variables globales:

Código: [Seleccionar]
static Tablero tablero;
static Dados dados;
static int jugador;
static int intentos;

Un objeto Tablero, los Dados, un entero que irá alternando entre los valores 1 y 2 para indicar que Jugador está activo en cada turno, y un contador de intentos.

El enunciado dice que tras dos intentos fallidos, el juego termina. Es decir, dos intentos por cada uno de los dos jugadores, 4 en total
Así que la condición que para que el juego continue ejecutándose, es que el contador de intentos se mantenga por debajo de 4.

Esa es la condición que he puesto al bucle while que dentro del main pone todo en marcha.

En el main declaro un Tablero de 20x20 y pongo en marcha el juego.
El juego es automático así que el usuario no juega, solo observa. Para facilitar la observación, usaré un método que pausará la ejecución del programa en determinados momentos y el usuario podrá continuar pulsando la tecla INTRO.

Lo primero que hace el flujo de programa, es comenzar con el Jugador 1 y empieza por decidir (al azar) si en lugar de tirar los dados, el Jugador quiere eliminar un Rectángulo del rival.
Si el código decide que sí quiere eliminar un Rectángulo del rival, se hará y finalizará su turno.
Si esta decisión al azar sale negativa, o bien sale positiva pero el rival no tiene Rectángulos para poder eliminar, entonces se procederá a tirar los dados para crear un Rectángulo e intentar insertarlo.

Si la inserción falla, el contador de intentos fallidos aumenta y pasa turno al siguiente jugador.
Si la inserción tiene éxito, el contado de intentos se reinicia a 0 y pasa turno al rival.

En todo momento se informa de qué está pasando y se va mostrando los Rectángulos generados y como va quedando el Tablero tras cada turno.

Finalizado el bucle while, se informa de quien ha ganado.

Citar
public static void main(String[] args) {
      
      tablero = new Tablero(20, 20);
      dados = new Dados();
      jugador = 1;
      intentos = 0;
      
      while (intentos < 4) { //Limite dos intentos seguidos fallidos por cada jugador
         System.out.println("\nTurno Jugador: " + jugador);
         
         if (!decidirEliminarRectangulo(jugador)) {
            
            dados.tirarDados();
            Rectangulo rect = new Rectangulo(dados.getDado1(), dados.getDado2(), jugador);
            System.out.println(rect);
            pausa();
            
            if (tablero.insertarRectangulo(rect)) {
               System.out.printf("Rectángulo insertado en %d,%d\n",
                     rect.getPosiciones()[0], rect.getPosiciones()[1]);
               intentos = 0;
            }
            else {
               System.out.println("No se pudo insertar Rectángulo");
               intentos++; //Contamos intento fallido
            }
         }
         
         System.out.println("Tablero actual:");
         System.out.println(tablero);
         pausa();
         cambioTurno();
      }
      tablero.indicarGanador();
   }

Para decidir al azar si eliminar o no un Rectángulo del rival, pues he pensado en un método que la mayoría de las veces decida NO eliminar.
Para ello lo que hago es generar un int entre 0 y 100. Y solo si el número generado es múltiplo de 5, intento eliminar Rectángulos, lo cuál puede que de todos modos no sea posible debido a que el rival no tiene ahora mismo Rectángulos para eliminar.

Citar
private static boolean decidirEliminarRectangulo(int jugador) {
      Random aleatorio = new Random();
      int num = aleatorio.nextInt(100)+1;
      if (num % 5 == 0) {
         return tablero.eliminarRectangulo(jugador);
      }
      else
         return false;
   }

Y creo que no queda nada más importante por explicar.

Clase GR
Código: [Seleccionar]
//https://aprenderaprogramar.com/foros/index.php?topic=7311.0
package dibujaRectangulos;

import java.io.IOException;
import java.util.Random;

public class Gr {

static Tablero tablero;
static Dados dados;
static int jugador;
static int intentos;

public static void main(String[] args) {

tablero = new Tablero(20, 20);
dados = new Dados();
jugador = 1;
intentos = 0;

while (intentos < 4) { //Limite dos intentos seguidos fallidos por cada jugador
System.out.println("\nTurno Jugador: " + jugador);

if (!decidirEliminarRectangulo(jugador)) {

dados.tirarDados();
Rectangulo rect = new Rectangulo(dados.getDado1(), dados.getDado2(), jugador);
System.out.println(rect);
pausa();

if (tablero.insertarRectangulo(rect)) {
System.out.printf("Rectángulo insertado en %d,%d\n",
rect.getPosiciones()[0], rect.getPosiciones()[1]);
intentos = 0;
}
else {
System.out.println("No se pudo insertar Rectángulo");
intentos++; //Contamos intento fallido
}
}

System.out.println("Tablero actual:");
System.out.println(tablero);
pausa();
cambioTurno();
}
tablero.indicarGanador();
}

private static void cambioTurno() {
if (jugador == 1)
jugador = 2;
else
jugador = 1;
}

private static boolean decidirEliminarRectangulo(int jugador) {
Random aleatorio = new Random();
int num = aleatorio.nextInt(100)+1;
if (num % 5 == 0) {
return tablero.eliminarRectangulo(jugador);
}
else
return false;
}

private static void pausa() {
System.out.println("\nPulse INTRO para continuar...\n");
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}

}


Pruébalo, comprueba que funciona como debe y pregunta lo que no entiendas o tengas dudas.
Creo que cumple con lo que se pide y que ya he depurado todos los posibles fallos.

Un saludo.



798
Quiero explicar de forma pormenorizada los métodos de la clase Tablero.

Aunque ya incorporan comentarios, a ver si puedo explicarlos mejor.

El primero es el método insertarRectangulo()
Recibe un Rectángulo he intentará insertarlo en el Tablero.

Lo primero que hace es comprobar a que jugador pertenece este Rectángulo.
Si es del Jugador 1, buscará donde insertarlo comenzando desde la esquina superior izquierda del tablero, es decir, las coordenadas 0,0 de la matriz.

Si es del Jugador 2, comenzará a buscar hueco por el lado opuesto, por la esquina inferior derecha.

Los bucles que recorren la matriz ya descartan las últimas posiciones donde es evidente que no va a caber, por eso en las condiciones se tiene en cuenta el alto y ancho del Rectángulo.

Por cada posible coordenada válida, se graba dentro del objeto Rectángulo y este se transfiere a un segundo método de apoyo llamado insertar() a quien además le indicamos si el Jugador tiene ya Rectángulos en el tablero o no.
Indicar esto es importante porque si el Jugador no tiene Rectángulos (porque este es el primero o porque el rival se los ha eliminado todos) entonces no hay que comprobar si el Rectángulo queda anexionado a un "hermano".
De lo contrario, si habrá que hacer esta comprobación.

Si la inserción ha tenido éxito, se guarda este Rectángulo en el ArrayList del jugador ys e retorna true.
Si no hay éxito, se vuelve a repetir el proceso con las siguientes coordenadas disponibles.

Si se agotan todas las coordenadas sin éxito, se retornara false para que el programa principal sepa que no hay forma de insertar este Rectángulo.

Citar
public boolean insertarRectangulo(Rectangulo rect) {
      
      if (rect.getJugador() == 1) {
         //Intentarmos inserciones comenzando por la esquina superior izquierda del tablero
         for (int a = 0; a <= (tablero.length - rect.getAltura()); a++)
            for (int l = 0; l <= (tablero[0].length - rect.getAncho()); l++) {
               //Establecemos las posiciones que ha de ocupar el rectangulo en el tablero
               int[] posiciones = new int[] {a, l};
               rect.setPosiciones(posiciones);
               //Intentamos insercion
               if (insertar(rect, rectangulosJug1.isEmpty())) {
                  rectangulosJug1.add(rect);
                  return true;
               }
            }
         //Bucles finalizados sin insercion exitosa. No se puede insertar este Rectángulo
         return false;
      }
      else { //Se trata del jugador 2
         //Intentaremos inserciones comenzando por la esquina inferior derecha del tablero
         for (int a = (tablero.length - rect.getAltura()); a >= 0; a--)
            for (int l = (tablero[0].length - rect.getAncho()); l >= 0; l--) {
               
               int[] posiciones = new int[] {a, l};
               rect.setPosiciones(posiciones);
               if (insertar(rect, rectangulosJug2.isEmpty())) {
                  rectangulosJug2.add(rect);
                  return true;
               }
            }
         return false;
      }
      
   }

Vamos a ver ahora ese método de apoyo llamado insertar()

Lo primero que hace es crear una copia del Tablero. Como dije antes, los intentos de inserción se hacen en una copia del Tablero original.
Luego preguntamos al Rectángulo que posiciones (coordenadas) se le han asignado y ahí es donde intentaremos la inserción.

Este proceso consiste en recorrer el area que va a ocupar el Rectángulo según sus coordenadas y dimensiones.
Si encontramos valores 0 en dicha area, es que de momento no se está solapando y podemos ocuparla.
En cuanto encontremos un valor distinto de 0, esto implica que el area está ocupada por otro Rectángulo, así que retornamos false para indicar que aquí estamos solapandonos y se tendrá que intentar en otro sitio.

Si conseguimos colocar el Rectángulo sin solaparnos, aún queda otra comprobación que hacer.
Si este Rectángulo es el "único" que el Jugador tiene en el Tablero, no hay que comprobar nada, de lo contrario, hay que comprobar si Rectángulo ha quedado anexionado a un "hermano" para validar la inserción.
Esta comprobación la hacen otros métodos que veremos después.

Si la inserción queda validada, la copia del Tablero donde hemos hecho los cambios pasa a ser el original. Si no, dicha copia simplemente quedará descartada.

Por último destacar la importancia de usar try catch en este proceso, ya que hay riesgo de que intentemos escrituras fuera de los limites de la matriz y esto provocaría una excepción que pondría fin a nuestro programa.

Citar
private boolean insertar(Rectangulo rect, boolean unicoEnTablero) {
      hacerCopia(); //Hacemos copia de seguridad tablero actual
      int[] pos = rect.getPosiciones();
      try {
         for (int a = pos[0]; a < pos[0] + rect.getAltura(); a++)
            for (int l = pos[1]; l < pos[1] + rect.getAncho(); l++) {
               if (copiaTablero[a][l] == 0) //Espacio disponible
                  copiaTablero[a][l] = rect.getJugador();
               else  //Espacio ocupado, imposible insertar
                  return false;
            }
         /*
          * Ha sido insertado sin solapamientos. Pero si este NO es el único
          * Rectángulo del jugador, hay que comprobar además si ha quedado anexo a otro
          * Rectángulo del mismo jugador
          */
         if (!unicoEnTablero)
            if (!comprobarAnexion(rect)) { //No ha quedado anexo
               return false;
            }
      } catch(Exception e) { //Hay riesgo de salirnos de las dimensiones del tablero
         return false;
      }
      //Si se han completado los bucles, es que la insercion ha sido correcta
      restaurarCopia(); //Pasamos la copia sobre la que hemos trabajado al tablero principal
      return true;
   }

El método que comprueba la anexión, lo que hace es ver qué informan otros cuatro submétodos. Cada uno de estos submétodos comprueba si hay un Rectángulo "hermano" alrededor del Rectángulo que estamos intentado insertar.
Si alguno de ellos devuelve true, la inserción quedará validada:

Citar
private boolean comprobarAnexion(Rectangulo rect) {
      return (compruebaDerecha(rect) || compruebaIzquierda(rect) ||
            compruebaArriba(rect) || compruebaAbajo(rect));
   }

Vamos a ver ahora uno de esos 4 métodos, que servirá para entenderlos todos.
Lo que hacen es pedir al Rectángulo sus posiciones asignadas (ya dije que este atributo era muy útil) y a partir de estas coordenadas comprobarán que valores hay en los costados adyacentes.

En el caso de la Izquierda, lo que hace es recorrer la Altura del Rectángulo, pero desviado una posicion a la izquierda.

Si un Rectángulo en por ejemplo las coordenadas 4,7 y un Altura de valor 5...
Lo que hará será recorrer las coordenadas:
  • 4,6
  • 5,6
  • 6,6
  • 7,6
  • 8,6

Y comprobar si alguna de esas coordenadas tiene un valor coincidente con el número de Jugador "dueño" de este Rectángulo.
Si encuentra al menos uno, se devolverá true confirmando la correcta anexión a un Rectángulo "hermano".
Si no encuentra ninguno, retornará false para informar que, al menos por este costazo izquierdo, no hay ningún "hermano" al que anexionarse.

De nuevo es importante usar try cactch. Y esto me ha traido de cabeza, porque la posible Excepción en realidad sería capturada desde el método insertar() evitando que el programa se detenga.
Pero si no se pone aquí también, en caso de excepción este método termina abruptamente sin terminar de comprobar todas las posibles posiciones.
Esto hace que durante el juego el Tablero no colocase Rectángulos en algunas zonas cercanas a los limites de la matriz, a pesar de que en realidad si era posible una inserción exitosa.

Esto como digo me traía de cabeza hasta que me he dado cuenta de que podía ser importante capturar la excepción dentro de este método para hacer que continue con el reto de posiciones posibles.

Citar
private boolean compruebaIzquierda(Rectangulo rect) {
      int[] posi = rect.getPosiciones();
      for (int a = posi[0]; a < posi[0] + rect.getAltura(); a++)
         try {
            if (copiaTablero[a][posi[1]-1] == rect.getJugador())
               return true; //Hay al menos una celda contigua a un Rectángulo "hermano"
         }catch(Exception e) {
            //Nada que hacer aquí, seguiremos probando en otras posiciones
         }
      return false; //No se encontraron celdas "hermanas"
   }

El método eliminarRectángulo()
Parece que tiene mucho código, pero es fácil de entender. Si el rival tiene Rectángulos insertados (por lo tanto, guardados en su ArrayList) pues escogemos uno al azar, usamos sus coordenadas para llenar su area de valor 0 y lo eliminamos del ArrayList.
Listo, Rectángulo eliminado.

Citar
   public boolean eliminarRectangulo(int jugador) {
      if (jugador == 1) { //Jugador1 elimina rectangulo de Jugador2
         if (rectangulosJug2.isEmpty())
            return false; //No hay rectangulos para eliminar
         else {
            int azar = (int)(Math.random() * rectangulosJug2.size());
            int[] posi = rectangulosJug2.get(azar).getPosiciones();
            for (int a = posi[0]; a < posi[0] + rectangulosJug2.get(azar).getAltura() ; a++)
               for (int l = posi[1]; l < posi[1] + rectangulosJug2.get(azar).getAncho(); l++)
                  tablero[a][l] = 0;
            rectangulosJug2.remove(azar);
            System.out.printf("Jugador %d ha eliminado un Rectangulo en %d,%d de su oponente\n",
                  jugador, posi[0], posi[1]);
            return true;
         }
      }
      else {
         if (rectangulosJug1.isEmpty())
            return false; //No hay rectangulos para eliminar
         else {
            int azar = (int)(Math.random() * rectangulosJug1.size());
            int[] posi = rectangulosJug1.get(azar).getPosiciones();
            for (int a = posi[0]; a < posi[0] + rectangulosJug1.get(azar).getAltura(); a++)
               for (int l = posi[1]; l < posi[1] + rectangulosJug1.get(azar).getAncho(); l++)
                  tablero[a][l] = 0;
            rectangulosJug1.remove(azar);
            System.out.printf("Jugador %d ha eliminado un Rectangulo en %d,%d de su oponente\n",
                  jugador, posi[0], posi[1]);
            return true;
         }
      }
   }


Por último vemos el método indicarGanador()

No tiene mucho misterio. Sumamos las areas de los Rectángulos de cada Jugador y el que sume más, es el ganador.
Para ser más informativos de cara al usuario, hacemos un cálculo del porcentaje de Tablero que ha logrado ocupar cada Jugador.

Citar
public void indicarGanador() {
      
      double areaTablero = tablero.length * tablero[0].length;
      double areaJug1 = 0;
      double areaJug2 = 0;
      
      for (Rectangulo rect: rectangulosJug1)
         areaJug1 += rect.getArea();
      
      for (Rectangulo rect: rectangulosJug2)
         areaJug2 += rect.getArea();
      
      areaJug1 = areaJug1 * 100 / areaTablero;
      areaJug2 = areaJug2 * 100 / areaTablero;
      
      System.out.printf("\nArea ocupada por Jugador1: %.2f\n", areaJug1);
      System.out.printf("Area ocupada por Jugador2: %.2f\n", areaJug2);
      
      if (areaJug1 == areaJug2)
         System.out.println("Los JUGADORES han empatado.");
      else if (areaJug1 > areaJug2)
         System.out.println("Ha ganado el Jugador 1");
      else
         System.out.println("Ha ganado el Jugador 2");
   }

Falta por ver la última clase, la que tiene el main.
La vemos en el siguiente post

799
Bueeeno.
Pues ya lo tengo.

El enfoque que comenté en el mensaje anterior.... fue descartado. Mientras lo desarrollaba efectivamente me parecía demasiado complicado y se me ocurrió otro enfoque algo más sencillo.

En lugar de pedir al Rectángulo que nos diera TODAS las posibles posiciones a su alrededor y empezar a probar una por una, he preferido hacer que el Tablero, una vez insertado el Rectángulo en un sitio donde no haya solapamiento, compruebe si a su alrededor hay un Rectángulo "hermano" con el que quedar anexionado. Si no lo hay, no puede quedarse en ese espacio y ha de probar en otro.
Así es más sencillo de programar.

A ver, voy a poner por aquí las clases que he usado. En realidad son las mismas que has usado tú, pero las he reescrito.

Comenzamos por la básica.
Clase Dados
No creo que haya que explicar nada.
Dos atributos int que cada uno representa un dado, quienes reciben un entero aleatorio cada vez que se invoca el método tirarDados()
Código: [Seleccionar]
import java.util.Random;

public class Dados {

private int dado1;
private int dado2;

public void tirarDados() {
Random aleatorio = new Random();
dado1 = aleatorio.nextInt(6)+1;
dado2 = aleatorio.nextInt(6)+1;
}

public int getDado1() {
return dado1;
}

public int getDado2() {
return dado2;
}

}

Clase Rectangulo
Los Rectangulos se irán generando con los atributos ancho y alto según el resultado de tirar los dados.
Otro atributo es el int jugador, sirve para determinar a quien pertenece este Rectangulo, si al Jugador1 o si al Jugador2.
De este modo cuando haya que "dibujarlo" en el Tablero, se sabrá facilmente si hay que rellenarlo con 1 o con 2.

El cuatro atributo, es un array llamado posiciones con 2 valores int. Ahí se guardan las coordenadas donde fue colocado este Rectángulo en el tablero.
Guardar esta información resulta útil para varias cosas, especialmente para cuando un Jugador decide eliminar un Rectángulo del otro jugador.
Teniendo estas coordenadas, sabremos donde debemos situarnos para eliminarlo del Tablero.

En cuanto a métodos, tiene algunos básicos getters y setters. Quizás se pueda destacar es que devuelve el area del rectángulo. Esto servirá al final del programa para decidir quien ha ganado, pues el Jugador cuyos Rectángulo sumen mayor area, será el ganador.

Código: [Seleccionar]
public class Rectangulo {

private int altura;
private int ancho;
private int[] posiciones;
private int jugador;


public Rectangulo(int a, int l, int jug){
altura = a;
ancho = l;
jugador = jug;
}

public void setPosiciones(int[] pos) {
posiciones = pos;
}

public int[] getPosiciones() {
return posiciones;
}

public int getAltura() {
return altura;
}


public int getAncho() {
return ancho;
}


public int getJugador() {
return jugador;
}

public int getArea() {
return altura * ancho;
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder("Rectángulo de jugador " + jugador +": \n");
for (int a = 0; a < altura; a++) {
for (int l = 0; l < ancho; l++)
sb.append("| " + jugador + " |");
sb.append("\n");
}
return sb.toString();
}

}

Clase Tablero
Esta es la clase más importante de todas, es quien lleva todo el peso de la lógica del juego.

Tiene 4 atributos.
Una matriz que representa el tablero, y una segunda matriz que se usará como duplicado de la primera como copia de apoyo.

Esto es para facilitar el proceso de inserción de Rectángulos. Los Rectángulos básicamente se van a insertar por el método de "ensayo y error". Es decir, donde encuentre una posicion disponible en la matriz (con valor 0) se comenzará a insertar el Rectángulo escribiendo 1 si es del Jugador 1 o escribiendo 2 si es el segundo Jugador.
Durante este proceso, puede que el Tablero descubra que no hay sitio para el Rectángulo en esa posicion, o que no queda anexionado a un Rectángulo "hermano".
Entonces, ha de deshacer lo que ha escrito y probar en otro sitio.

Deshacer lo que se ha escrito puede ser complicado de programar, así que lo que hago es intentar las inserciones en la copia del Tablero, si la inserción fracasa pues se descarta esta copia y ya está.
Y si hemos tenido éxito, entonces la copia pasa a ser el nuevo Tablero.

Así todo queda guay de forma sencilla.

Los otros atributos son dos ArrayList donde se van a ir guardando los Rectángulos que cada Jugador ha conseguido insertar.
Guardarlos es necesario, sirve para luego poder elegir Rectángulos a eliminar cuando los Jugadores lo pidan y también para al final del juego, sumar areas de Rectángulos y determinar un ganador.

Pego aquí la clase y sigo en otro post:

Código: [Seleccionar]
import java.util.ArrayList;

public class Tablero {

/*
* El tablero tendrá tres posibles valores en sus matrices:
* 0 - espacio libre
* 1 - area rectangulo jugador 1
* 2 - area rectangulo jugador 2
*/

private int[][] tablero;
private int[][] copiaTablero;
private ArrayList<Rectangulo> rectangulosJug1;
private ArrayList<Rectangulo> rectangulosJug2;

public Tablero(int x,int y){
tablero=new int[x][y];
copiaTablero=new int[x][y];
rectangulosJug1 = new ArrayList<Rectangulo>();
rectangulosJug2 = new ArrayList<Rectangulo>();
}

/**
* Recibe un Rectángulo e intenta insertarlo en el Tablero.<br>
* La inserción se considera exitosa si el Rectángulo puede colocarse
* sin solaparse con otros y además queda anexionado a otro Rectángulo
* "hermano" que pertenezca al mismo Jugador.<br>
* Si consigue insertarse, guardará el Rectángulo en el ArrayList correspondiente
* y grabará en que coordenadas fue insertado.
* @param rect <b>Rectangulo</b> que vamos a insertar.
* @return <b>true</b> si la inserción fue posible, <b>false</b> en caso contrario.
*/
public boolean insertarRectangulo(Rectangulo rect) {

if (rect.getJugador() == 1) {
//Intentarmos inserciones comenzando por la esquina superior izquierda del tablero
for (int a = 0; a <= (tablero.length - rect.getAltura()); a++)
for (int l = 0; l <= (tablero[0].length - rect.getAncho()); l++) {
//Establecemos las posiciones que ha de ocupar el rectangulo en el tablero
int[] posiciones = new int[] {a, l};
rect.setPosiciones(posiciones);
//Intentamos insercion
if (insertar(rect, rectangulosJug1.isEmpty())) {
rectangulosJug1.add(rect);
return true;
}
}
//Bucles finalizados sin insercion exitosa. No se puede insertar este Rectángulo
return false;
}
else { //Se trata del jugador 2
//Intentaremos inserciones comenzando por la esquina inferior derecha del tablero
for (int a = (tablero.length - rect.getAltura()); a >= 0; a--)
for (int l = (tablero[0].length - rect.getAncho()); l >= 0; l--) {

int[] posiciones = new int[] {a, l};
rect.setPosiciones(posiciones);
if (insertar(rect, rectangulosJug2.isEmpty())) {
rectangulosJug2.add(rect);
return true;
}
}
return false;
}

}

/**
* Invoca a los 4 métodos que comprueban si el Rectángulo ha quedado
* anexionado a un Rectángulo "hermano" por algunos de sus cuatro costados.<br>
* Basta con que uno de esos métodos devuelva <b>true</b> para dar por buena
* la anexión.
* @param rect <b>Rectangulo</b> que ha de quedar anexionado.
* @return <b>true</b> si hubo éxito, <b>false</b> en caso contrario.
*/
private boolean comprobarAnexion(Rectangulo rect) {
return (compruebaDerecha(rect) || compruebaIzquierda(rect) ||
compruebaArriba(rect) || compruebaAbajo(rect));
}

/**
* Comprueba si el Rectángulo tiene a un "hermano" por su costado izquierdo.
* @param rect <b>Rectangulo</b> que se ha de anexionar.
* @return <b>true</b> si hubo éxito, <b>false</b> en caso contrario.
*/
private boolean compruebaIzquierda(Rectangulo rect) {
int[] posi = rect.getPosiciones();
for (int a = posi[0]; a < posi[0] + rect.getAltura(); a++)
try {
if (copiaTablero[a][posi[1]-1] == rect.getJugador())
return true; //Hay al menos una celda contigua a un Rectángulo "hermano"
}catch(Exception e) {
//Nada que hacer aquí, seguiremos probando en otras posiciones
}
return false; //No se encontraron celdas "hermanas"
}

/**
* Comprueba si el Rectángulo tiene a un "hermano" por su costado derecho.
* @param rect <b>Rectangulo</b> que se ha de anexionar.
* @return <b>true</b> si hubo éxito, <b>false</b> en caso contrario.
*/
private boolean compruebaDerecha(Rectangulo rect) {
int[] posi = rect.getPosiciones();
for (int a = posi[0]; a < posi[0] + rect.getAltura(); a++)
try {
if (copiaTablero[a][posi[1] + rect.getAncho()] == rect.getJugador())
return true;
}catch(Exception e) {

}
return false;
}

/**
* Comprueba si el Rectángulo tiene a un "hermano" por su costado superior.
* @param rect <b>Rectangulo</b> que se ha de anexionar.
* @return <b>true</b> si hubo éxito, <b>false</b> en caso contrario.
*/
private boolean compruebaArriba(Rectangulo rect) {
int[] posi = rect.getPosiciones();
for (int l = posi[1]; l < posi[1] + rect.getAncho(); l++)
try {
if (copiaTablero[posi[0]-1][l] == rect.getJugador())
return true;
}catch(Exception e) {

}
return false;
}

/**
* Comprueba si el Rectángulo tiene a un "hermano" por su costado inferior.
* @param rect <b>Rectangulo</b> que se ha de anexionar.
* @return <b>true</b> si hubo éxito, <b>false</b> en caso contrario.
*/
private boolean compruebaAbajo(Rectangulo rect) {
int[] posi = rect.getPosiciones();
for (int l = posi[1]; l < posi[1] + rect.getAncho(); l++)
try {
if (copiaTablero[posi[0] + rect.getAltura()][l] == rect.getJugador())
return true;
}catch(Exception e) {

}
return false;
}

/**
* Intentará insertar un rectangulo en las posiciones indicadas.<br>
* @param rect Rectángulo que hay que insertar
* @param unicoEnTablero Informa de si se está insertando el único Rectángulo del jugador
* actual en el Tablero.<br>Si NO es el único Rectángulo, habrá que comprobar que la
* inserción de este Rectángulo ha sido anexa a otro Rectángulo del mismo jugador.
* @return <b>true</b> si tuvo éxito la inserción, <b>false</b> en caso contrario.
*/
private boolean insertar(Rectangulo rect, boolean unicoEnTablero) {
hacerCopia(); //Hacemos copia de seguridad tablero actual
int[] pos = rect.getPosiciones();
try {
for (int a = pos[0]; a < pos[0] + rect.getAltura(); a++)
for (int l = pos[1]; l < pos[1] + rect.getAncho(); l++) {
if (copiaTablero[a][l] == 0) //Espacio disponible
copiaTablero[a][l] = rect.getJugador();
else  //Espacio ocupado, imposible insertar
return false;
}
/*
* Ha sido insertado sin solapamientos. Pero si este NO es el único
* Rectángulo del jugador, hay que comprobar además si ha quedado anexo a otro
* Rectángulo del mismo jugador
*/
if (!unicoEnTablero)
if (!comprobarAnexion(rect)) { //No ha quedado anexo
return false;
}
} catch(Exception e) { //Hay riesgo de salirnos de las dimensiones del tablero
return false;
}
//Si se han completado los bucles, es que la insercion ha sido correcta
restaurarCopia(); //Pasamos la copia sobre la que hemos trabajado al tablero principal
return true;
}

/**
* Intentará eliminar un Rectángulo del jugador opuesto.<br>
* El Rectángulo a eliminar se elige al azar.
* @param jugador Jugador que quiere eliminar un Rectángulo del rival.
* @return <b>true</b> al eliminar un Rectángulo,
* <b>false</b> si el rival no tiene Rectángulos para eliminar
*/
public boolean eliminarRectangulo(int jugador) {
if (jugador == 1) { //Jugador1 elimina rectangulo de Jugador2
if (rectangulosJug2.isEmpty())
return false; //No hay rectangulos para eliminar
else {
int azar = (int)(Math.random() * rectangulosJug2.size());
int[] posi = rectangulosJug2.get(azar).getPosiciones();
for (int a = posi[0]; a < posi[0] + rectangulosJug2.get(azar).getAltura() ; a++)
for (int l = posi[1]; l < posi[1] + rectangulosJug2.get(azar).getAncho(); l++)
tablero[a][l] = 0;
rectangulosJug2.remove(azar);
System.out.printf("Jugador %d ha eliminado un Rectangulo en %d,%d de su oponente\n",
jugador, posi[0], posi[1]);
return true;
}
}
else {
if (rectangulosJug1.isEmpty())
return false; //No hay rectangulos para eliminar
else {
int azar = (int)(Math.random() * rectangulosJug1.size());
int[] posi = rectangulosJug1.get(azar).getPosiciones();
for (int a = posi[0]; a < posi[0] + rectangulosJug1.get(azar).getAltura(); a++)
for (int l = posi[1]; l < posi[1] + rectangulosJug1.get(azar).getAncho(); l++)
tablero[a][l] = 0;
rectangulosJug1.remove(azar);
System.out.printf("Jugador %d ha eliminado un Rectangulo en %d,%d de su oponente\n",
jugador, posi[0], posi[1]);
return true;
}
}
}

/**
* Hace una copia del Tablero original.<br>
* Todos los intentos de inserción se harán en la copia
* y será descartada en caso de fracaso.
*/
private void hacerCopia() {
for (int i = 0; i < tablero.length; i++)
for (int j = 0; j < tablero[0].length; j++)
copiaTablero[i][j] = tablero[i][j];
}

/**
* Si la insercion de un Rectángulo ha sido exitosa, la
* copia pasará a ser el Tablero original.
*/
private void restaurarCopia() {
for (int i = 0; i < tablero.length; i++)
for (int j = 0; j < tablero[0].length; j++)
tablero[i][j] = copiaTablero[i][j];
}

/**
* Determina quien ha sido el ganador del juego.<br>
* Para determinar ganador, se calcula el area total
* de la suma de los Rectángulos que cada jugador ha conseguido colocar.
*/
public void indicarGanador() {

double areaTablero = tablero.length * tablero[0].length;
double areaJug1 = 0;
double areaJug2 = 0;

for (Rectangulo rect: rectangulosJug1)
areaJug1 += rect.getArea();

for (Rectangulo rect: rectangulosJug2)
areaJug2 += rect.getArea();

areaJug1 = areaJug1 * 100 / areaTablero;
areaJug2 = areaJug2 * 100 / areaTablero;

System.out.printf("\nArea ocupada por Jugador1: %.2f\n", areaJug1);
System.out.printf("Area ocupada por Jugador2: %.2f\n", areaJug2);

if (areaJug1 == areaJug2)
System.out.println("Los JUGADORES han empatado.");
else if (areaJug1 > areaJug2)
System.out.println("Ha ganado el Jugador 1");
else
System.out.println("Ha ganado el Jugador 2");
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder("Estado del Juego: \n");
for (int a = 0; a < tablero.length; a++) {
for (int l = 0; l < tablero[0].length; l++)
sb.append("| " + tablero[a][l] + " |");
sb.append("\n");
}
return sb.toString();
}

}

800
Comunidad / Re:Presentación
« en: 19 de Mayo 2019, 20:35 »
Hola y bienvenido.  :D

Puedes plantear tus dudas en este subforo: https://aprenderaprogramar.com/foros/index.php?board=2.0
Intenta aportar tanta información como puedas a tu pregunta.

Yo poco podré ayudarte porque me muevo más por Java y algo de C++ (también autodidacta), pero quizás otro participante del foro pueda echarte una mano.

Además, puedes echarle un ojo al Curso Visual Basic desde 0 en la web.
No se si quizás tu vás algo más avanzado, pero nunca está de más revisarlo.

Un saludo.


Páginas: 1 ... 35 36 37 38 39 [40] 41 42 43 44 45 ... 50

Sobre la educación, sólo puedo decir que es el tema más importante en el que nosotros, como pueblo, debemos involucrarnos.

Abraham Lincoln (1808-1865) Presidente estadounidense.

aprenderaprogramar.com: Desde 2006 comprometidos con la didáctica y divulgación de la programación

Preguntas y respuestas

¿Cómo establecer o cambiar la imagen asociada (avatar) de usuario?
  1. Inicia sesión con tu nombre de usuario y contraseña.
  2. Pulsa en perfil --> perfil del foro
  3. Elige la imagen personalizada que quieras usar. Puedes escogerla de una galería de imágenes o subirla desde tu ordenador.
  4. En la parte final de la página pulsa el botón "cambiar perfil".