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 ... 23 24 25 26 27 [28] 29 30 31 32 33 ... 50
541
Pues lo dicho, comienza tú un código y llega hasta donde puedas.

Ves paso a paso, divide el programa en pequeños objetivo sen lugar de intentar resolver el TODO de golpe.

Por ejemplo, comienza haciendo simplemente un bucle do while() que pida calificaciones hasta que se introduzca un 0.
No hagas nada con las calificaciones, solo pedir y pedir.., hasta que se introduzca 0.

Cuando eso funcione, intenta hacer que cada vez que se introduce una calificación, compruebe si está entre 5 y 10 inclusive.
Si lo está, que lance mensaje tipo "La calificación es válida".
Si no lo está, mensaje "La calificación no es válida".

Después, añade una variable contador para contar las calificaciones válidas. Es decir, cada vez que la calificación sea válida, incremente el contador en +1.
Y cuando termine el bucle, tras haberse introducido por teclado valor 0 para la calificación, pues entonces informe en pantalla de cuántas calificaciones ha contado.

Luego añade una variable para sumar las calificaciones.
Y al terminar el bucle, que indique también el promedio (suma / contador).

Y así con el resto de cosas que pide.
Esa es la fórmula, ir haciendo lo que pide de uno en uno.

542
Variables que puedes necesitar:
- calificacion, para leer calificaciones que introduce el usuario.
- contador, para contar cuantas calificaciones se llegan a introducir.
- suma, para sumar las calificaciones y al final del programa calcular el promedio (dividiendo entre el valor de contador).
- mayor, para guardar la calificación más alta introducida.
- menor, para guardar la calificación más baja introducida.

Faltaría otra para la calificación más frecuente, sin embargo, para obtener este dato creo que no va a bastar con una simple variable.
Tal vez se debería usar un array de 6 elementos tipo int y cada elemento se asocia a una de las 6 posibles calificaciones.
Según la calificación introducida, se incrementa uno de los elementos de ese array.

Si por ejemplo se introduce un 5, que es la calificación más baja que podemos admitir, pues se puede incrementar el primer elemento del array (la posición 0)
Si se introduce un 6, pues el segundo elemento (posición 1)
Etc...

Así luego al final del programa se comprueba cuál de los 6 elementos es que más se ha incrementado y sabremos la calificación más frecuente.


Se puede usar un bucle que se repita mientras el valor introducido para calificación sea distinto de 0.
Dentro del bucle, se pide el dato y se comprueba si está dentro del rango aceptado, ente 5 y 10.
Si lo está, operamos con él contándolo, sumándolo, comparando con los valores de mayor y menor, e incrementando el elemento correspondiente del array de frecuentes.
Si no está dentro del rango admitido, y no es 0, entonces se lanza mensaje de error al usuario.

Cuando el bucle termine, se calcula promedio, se comprueba el array de frecuentes y se muestran los datos.

No pongo ejemplo de código, porque no indicas en que lenguaje estás trabajando. En cualquier caso, comienza tú un código, llega hasta donde puedas y te ayudamos a completarlo.

543
Hola.
Siempre podemos combinar ambas opciones.

Insertar el código en el foro con sus etiquetas [ CODE ] correspondientes, que es verdad que presenta un aspecto un poco "espartano", pero tiene la ventaja de que pervive en el tiempo.

Y además un enlace a una de estas páginas que permiten compartir el código con la sintaxis coloreada e incluso algunas permiten compilar y correr el programa en línea.
La desventaja es que el enlace puede caducar con el tiempo, a veces en muy poco tiempo...

Pero bueno, si se combinan ambos, en un lado u otro estará siempre el código disponible.

544
Hola.
Tu error está en el constructor.
Cuando inicias el JTextArea txtS que tienes declarado como atributo, por error en realidad estás declarando un JTextArea nuevo.
Tienen el mismo nombre, pero son distintos.

Citar
JTextArea txtS = new JTextArea();
      txtS.setBackground(Color.WHITE);
      scrollPane.setViewportView(txtS);

Entonces, cuando pulsas el botón, el método intenta agregar texto al "verdadero" txtS, al declarado como atributo de clase. Pero como no se ha llegado a iniciar, porque por error has inicializado otro distinto, te da error de NullPointerException, que viene a significar que el "verdadero" txtS está en estado null, porque no se ha inicializado.

Basta con quitar el nombre de clase JTextArea en el constructor, para que ahora sí se inicialice el "verdadero" txtS.

Citar
txtS = new JTextArea();
      txtS.setBackground(Color.WHITE);
      scrollPane.setViewportView(txtS);

A veces, para evitar estos fallos tontos pero que luego puede costar mucho descubrirlos, puede venir bien usar la palabra reservada this para asegurarte de que estás inicializando el atributo de tu clase principal, y no "otra cosa" por error.

Citar
this.txtS = new JTextArea();
      txtS.setBackground(Color.WHITE);
      scrollPane.setViewportView(txtS);

Sobre optimizar...
Bueno, en principio no necesitas tres métodos para redondear.
Te bastaría con uno, al que llamarías tres veces pasándole una nota distintas cada vez.
Pero es que de todos modos esos métodos lo que hacen es retornar el resultado que proporciona un método de la clase Math, así que en realidad directamente puedes llamarle a él sin necesitar de crear un método intermedio.
Lo mismo con las lecturas de los campos de texto.

Así ahorras líneas de código innecesarias:

Citar
public class Catorce extends JDialog {

   /**
    *
    */
   private static final long serialVersionUID = 2409303291509567329L;
   private JTextField textField_Nota1;
   private JTextField textField_Nota2;
   private JTextField textField_Nota3;
   private JTextArea txtS;
   
   /**
    * Launch the application.
    */
   public static void main(String[] args) {
      EventQueue.invokeLater(new Runnable() {
         public void run() {
            try {
               Catorce dialog = new Catorce();
               dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
               dialog.setVisible(true);
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      });
   }

   /**
    * Create the dialog.
    */
   public Catorce() {
      getContentPane().setFont(new Font("Tahoma", Font.PLAIN, 14));
      setTitle("Reportes");
      setBounds(100, 100, 450, 300);
      getContentPane().setLayout(null);
     
      JLabel lblNota_1 = new JLabel("Nota 1");
      lblNota_1.setFont(new Font("Tahoma", Font.PLAIN, 13));
      lblNota_1.setBounds(10, 11, 46, 14);
      getContentPane().add(lblNota_1);
     
      JLabel lblNota_2 = new JLabel("Nota 2");
      lblNota_2.setFont(new Font("Tahoma", Font.PLAIN, 13));
      lblNota_2.setBounds(10, 36, 46, 14);
      getContentPane().add(lblNota_2);
     
      JLabel lblNota_3 = new JLabel("Nota 3");
      lblNota_3.setFont(new Font("Tahoma", Font.PLAIN, 13));
      lblNota_3.setBounds(10, 61, 46, 14);
      getContentPane().add(lblNota_3);
     
      textField_Nota1 = new JTextField();
      textField_Nota1.setBounds(83, 9, 86, 20);
      getContentPane().add(textField_Nota1);
      textField_Nota1.setColumns(10);
     
      textField_Nota2 = new JTextField();
      textField_Nota2.setBounds(83, 34, 86, 20);
      getContentPane().add(textField_Nota2);
      textField_Nota2.setColumns(10);
     
      textField_Nota3 = new JTextField();
      textField_Nota3.setBounds(83, 59, 86, 20);
      getContentPane().add(textField_Nota3);
      textField_Nota3.setColumns(10);
     
      JScrollPane scrollPane = new JScrollPane();
      scrollPane.setBounds(10, 92, 414, 158);
      getContentPane().add(scrollPane);
     
      this.txtS = new JTextArea();
      txtS.setBackground(Color.WHITE);
      scrollPane.setViewportView(txtS);
     
      JButton btnProcesar = new JButton("Procesar");
      btnProcesar.addActionListener(new ActionListener() {
         

         public void actionPerformed(ActionEvent arg0) {
            double nota1 = 0, nota2 = 0, nota3=0;
            long rpt1 = 0, rpt2 = 0, rpt3 = 0 ;
            int suma;
            nota1 = Double.parseDouble(textField_Nota1.getText());
            nota2 = Double.parseDouble(textField_Nota2.getText());
            nota3 = Double.parseDouble(textField_Nota3.getText());
            rpt1 = Math.round(nota1);
            rpt2 = Math.round(nota2);
            rpt3 = Math.round(nota3);

            suma = Suma( rpt1, rpt2, rpt3);
            mostrarResultado(rpt1, rpt2, rpt3, suma);
         }
      });
      btnProcesar.setBounds(322, 8, 89, 23);
      getContentPane().add(btnProcesar);
   }
   
   private int Suma(long rpt1, long rpt2, long rpt3){
      //Se añade int para transformar de long a int
       return (int)((rpt1 + rpt2 + rpt3)/3);
      }
   private void mostrarResultado(long rpt1, long rpt2, long rpt3, int suma){
      txtS.setText("Reporte\n");
      txtS.append("Nota 1 es: " + rpt1 + "\n");
      txtS.append("Nota 2 es: " + rpt2 + "\n");
      txtS.append("Nota 3 es: " + rpt3 + "\n");
      txtS.append("Promedio: " + suma+ "\n");
   }
}

545
Mmmhhh.. pues posiblemente sí, aunque puede ser algo más complicado.

Si la interfaz la implementa la clase Jugador, y por tanto esta clase tendría esas constantes dentro de su ámbito, entonces si deberían poder usarse como regla de comparación.

Si la interfaz la implementase las clases hijas, y no Jugador, la cosa se complicaría más.
Porque los objetos que están el <Jugador>TreeSet en ese momento son objetos de clase Jugador, que no implementa esa interfaz y por tanto las constantes están fuera de su ámbito. Vamos, que no tiene ni idea de que constantes son.

Habría que complicar un poco el código preguntando en cada caso que tipo de clase hija pertenece con la palabra reservada instanceof y hacer un casting para convertirlo a la clase hija a la que realmente pertenece dicho objeto, y ahora sí se podrían consultar esas constantes.

Si compartes todo tu código, podemos intentar ver cuál sería la mejor solución.

546
Hola,
se debe a que no has completado el método compareTo()

Verás, el TreeSet tiene una peculiaridad y es que SOLO admite elementos que NO estén repetidos.
Pero claro, él por si solo, no es capaz de discernir cuándo un elemento se repite o no.

En este caso trabajamos con objetos Jugador., ¿Cuándo un Jugador es igual a otro?
¿Cuando tienen el mismo nombre?
¿Cuando tiene el mismo ID?
¿Cuando coinciden tanto el nombre como el ID?

Esto TreeSet no lo puede decidir, lo decide el programador
Una forma de hacerlo, es implementando la interfaz Comparable a la clase que está creando.

A Jugador, le has implementado dicha interfaz, bién,  pero no has completado el método compareTo().
Así que no has decidido todavía cómo se han de comparar los objetos Jugador.

Has dejado el código que viene por defecto, que hace un return 0.

Código: [Seleccionar]
@Override
public int compareTo(Jugador o) {
// TODO Auto-generated method stub
return 0;
}
Y cuando compareTo() retorna 0, significa que los objetos que se comparan son iguales.

Por tanto, el TreeSet confía ciegamente en lo que este método le dice, y le está diciendo que todos los objetos Jugador que le estás pasando, son iguales.
Y por tanto, el TreeSet solo admite el primero y el resto los rechaza.

Esto se puede comprobar muy fácilmente. Cuando añadimos un objeto al TreeSet, este retorna true si lo admite o false si lo rechaza.
Si pedimos mostrar en pantalla qué es lo que retorna cada vez que hacemos un add()...
Citar
   public static void main(String[] args) {
      
      TreeSet <Jugador> listaJugadores = new TreeSet<Jugador>();
         Jugador j1 = new Jugador("jA","pepe");
         Jugador j2 = new Jugador("jB","juan");
         Jugador j3 = new Jugador("jC","maria");
         System.out.println(listaJugadores.add(j1));
         System.out.println(listaJugadores.add(j2));
         System.out.println(listaJugadores.add(j3));

        
         for(Jugador j: listaJugadores) {
            j.mostrarDatos();
         }

   }

...veremos que solo admite el primero:
Citar
true
false
false
ID: jA
Nombre: pepe

Podemos hacer otro experimento, cambiamos el método compareTo() para que retorne algo que no sea 0:
Citar
   @Override
   public int compareTo(Jugador o) {
      // TODO Auto-generated method stub
      return 1;
   }

Cuando retorna un entero positivo, como el 1, significa que el objeto que recibe como argumento para compararse, es mayor. Por tanto, no son iguales, así que ahora TreeSet si admitirá los tres objetos que le pasamos (aunque en realidad aún no hemos establecido una regla de comparación que sea útil)
Si volvemos a ejecutar el programa, veremos que los tres objetos añadidos retornan true y podemos listarlos en pantalla:
Citar
true
true
true
ID: jA
Nombre: pepe
ID: jB
Nombre: juan
ID: jC
Nombre: maria

Aprovechemos la ocasión para hacer otro experimento. Volvemos a cambiar el retorno de compareTo(), esta vez, vamos a pedir que retorne un valor negativo.
Citar
   @Override
   public int compareTo(Jugador o) {
      // TODO Auto-generated method stub
      return -1;
   }

Un valor negativo significa que el objeto que recibe como argumento para compararse, es menor.
¿Qué pasa entonces si volvemos a ejecutar el programa?
Citar
true
true
true
ID: jC
Nombre: maria
ID: jB
Nombre: juan
ID: jA
Nombre: pepe

¡¡Voilá!!
Los tres Jugadores se han añadido y además se han ordenado de forma inversa a la que los hemos insertado.
Esto es porque ahora compareTo() le está diciendo a TreeSet que todas las comparaciones resultan en que el nuevo objeto recibido es menor respecto a los que ya hay insertados.

Esta es otra peculiaridad de TreeSet, no solo evita que se repitan elementos, si no que siempre los tiene ordenados de menor a mayor.

La verdad es que este tipo de Set es un encanto  :-* je je.
Pero claro, para que lo haga bien, nosotros tenemos que escribir correctamente el método compareTo() y decidir cuáles son las reglas de comparación para nuestra clase.

La pregunta es:
¿Cuándo podemos considerar un Jugador menor o mayor que otro?

Puede depende de cada caso y del criterio del programador, por supuesto..

En este caso, comos los jugadores tienen un ID, que se supone ha de ser único, pues puede ser ese el atributo que consideremos fundamental para comparar Jugadores.

Ahora hay que escribir un código para decidir como compara ese atributo.
Puesto que se trata de un String, y la clase String ya tiene implementada su propia regla de comparación, pues podemos simplemente establecer que se retorne el resultado de comparar los ID, tal cuál establece la clase String:
Citar
   @Override
   public int compareTo(Jugador o) {
      return this.id.compareTo(o.id);
   }

Los String hacen una comparación lexicográfica, es decir, que la letra a se considera menor que la b, la b es menor que la c, etc..

Vamos, que ordena alfabéticamente de la a a la z, y según cuantas letras tiene cada String.

Si volvemos a ejecutar el programa, y cambiamos los id para insertarlos desordenados alfabéticamente:

Citar
   public static void main(String[] args) {
      
      TreeSet <Jugador> listaJugadores = new TreeSet<Jugador>();
         Jugador j1 = new Jugador("jB","pepe");
         Jugador j2 = new Jugador("jC","juan");
         Jugador j3 = new Jugador("jA","maria");
         System.out.println(listaJugadores.add(j1));
         System.out.println(listaJugadores.add(j2));
         System.out.println(listaJugadores.add(j3));
        
         for(Jugador j: listaJugadores) {
            j.mostrarDatos();
         }

   }

Veremos que al mostrarlos, se muestran bien ordenados:
Citar
true
true
true
ID: jA
Nombre: maria
ID: jB
Nombre: pepe
ID: jC
Nombre: juan

547
Comunidad / Re: GRACIAS POR ACEPTARME
« en: 27 de Diciembre 2020, 12:45 »
Feliz Navidad y bienvenido.  ;)

548
Me acabo de dar cuenta de un pequeño fallo, los fantasmas salen todos como F1 en pantalla para ambos jugadores.
Podemos arreglarlo cambiando el método colocarManual() para que reciba un tercer parámetro, el String que queremos representar en pantalla.
Este String lo usamos al crear un Fantasma con la Coordenada elegida

Citar
private void colocarManual(Player jugador, ArrayList<Coordenada> coordenadas, String fp) {
      int colocados = 0;
      boolean fantasmaBueno = true;
      while (colocados < dificultad) {

         System.out.println("\nJugador " + jugador.getNombre() + " coloca tus Fantasmas.");
         System.out.println("Fantasmas colocados: " + colocados + "/" + dificultad);
         System.out.println(this);
         System.out.println("Coordenadas elegibles [fila,columna]:");
         for (Coordenada coor: coordenadas)
            System.out.print(coor + " "); //Listamos las coordenadas en pantalla
         Coordenada elegida = new Coordenada(0, 0);
         //Pedimos FILA y COLUMNA hasta obtener una coordenada válida
         do {
            System.out.print("\nIntroduce coordenadas para un Fantasma ");
            System.out.println(fantasmaBueno?"BUENO":"MALO");
            System.out.println("Fila: ");
            int fila = Integer.parseInt(teclado.nextLine());
            System.out.println("Columna:");
            int columna = Integer.parseInt(teclado.nextLine());
            elegida = new Coordenada(fila-1, columna-1);
            if (!coordenadas.contains(elegida))
               System.out.println("Coordenadas no son válidas....\n");
         }while(!coordenadas.contains(elegida));
         //Tenemos Coordenada válida, colocamos Fantasma
         tablero[elegida.fila][elegida.columna] = new Fantasma(fantasmaBueno, fp);
         fantasmaBueno = !fantasmaBueno; //Alternamos entre bueno y malo, para el siguiente fantasma
         //Eliminamos la coordenada escogida para la siguiente selección
         coordenadas.remove(elegida);
         //Contamos fantasmas colocado
         colocados++;
      }
   }

Y aquí es donde decidimos que String pasarle a este método:
Citar
   private void fantasmasManual() {
      //Nos aseguramos de que el tablero está vacío
      tablero = new Fantasma[6][6];

      //Coordenadas válidas para fantasmas player1
      ArrayList<Coordenada> coordF1 = new ArrayList<Coordenada>(
            Arrays.asList(Coordenada.coordenadasInicioF1()));
      //Coordenadas válidas para fantasmas player2
      ArrayList<Coordenada> coordF2 = new ArrayList<Coordenada>(
            Arrays.asList(Coordenada.coordenadasInicioF2()));

      //Primero coloca Player1
      colocarManual(player1, coordF1, "F1");
      System.out.println("\n\t\t----Siguiente Jugador----\n");
      //Después coloca Player2
      colocarManual(player2, coordF2, "F2");
   }



El código completo se puede descargar pulsando el link de descarga que aparece debajo de este mensaje (para acceder a él es necesario estar registrado en los foros) y que contiene los siguientes archivos (clases java):

- Castillo.java
- Coordenadas.java
- Fantasma.java
- GestorPlayers.java
- Ghosts.java
- Player.java





549
Colocar Fantasmas de modo manual...

Ahora que ya podemos configurar el juego en modo manual, vamos a ver como colocar los Fantasmas en este modo.

Estaría bien que durante este proceso, le mostremos al usuario cuáles son las coordenadas válidas para colocar sus fantasmas.
Ya hicimos que la clase Coordenada nos proporcione esta lista para cada jugador, así que solo hay que mostrarla en pantalla.
El jugador ya luego tecleará la fila y columna que quiera.

Con esta fila y columna crearemos una Coordenada y comprobaremos si existe en el ArrayList de las coordenadas válidas. En caso afirmativo, colocamos fantasmas y retiramos esa coordenada de la lista pues ya ha sido ocupada.

Para hacer esto, vamos a querer modificar la clase Coordenada añadiendo el método equals() y el método toString().

equals() nos facilitará el comprobar si la coordenada elegida por el usuario está entre las coordenadas válidas.

Y toString() nos facilitará el mostrar las coordenadas en pantalla. Además, recordemos que el tablero internamente es una matriz de 6x6, es decir, sus indices van de 0 a 5.
Pero en pantalla estamos mostrando las coordenadas de 1 a 6.
Esto es importante tenerlo en cuenta.
Si el usuario elige por ejemplo la coordenada [1, 1], internamente en la matriz, en realidad será la coordenada [0, 0]

Esta diferencia hay que tenerla en cuenta al mostrar la coordenada en pantalla, pero también al leer la coordenada por teclado cuando la introduzca el usuario.

Así, modificamos la clase Coordenada sobrescribiendo esos dos métodos:
Código: [Seleccionar]
@Override
public boolean equals(Object obj) {
if (obj instanceof Coordenada) {
Coordenada otraCoor = (Coordenada)obj;
return otraCoor.fila == fila && otraCoor.columna == columna;
}
else
return false;
}

@Override
public String toString() {
//Sumamos +1 porque el valor de la coordenada en pantalla difiere del valor real interno
return String.format("[%d,%d]", fila+1, columna+1);
}


Hecho esto nos vamos a la clase Castillo.
Aquí tenemos un método fantasmasAzar() que coloca los fantasmas en el tablero al azar.

Ahora queremos algo parecido, pero que los jugadores elijan donde colocarlos.
Para ello, lo primero es añadir un Scanner a esta clase, para poder leer por teclado.
Y asegurarnos de que tenemos métodos para setear a ambos Player

Citar
public class Castillo {

   private Fantasma[][] tablero;
   private Player player1;
   private Player player2;
   private int dificultad; //NORMAL–8 fantasmas, EXPERT–4 fantasmas, GENIUS–2 fantasmas
   private int modo; // 1-Aleatorio, 2-Manual
   
   private Scanner teclado;

   public Castillo() {
      tablero = new Fantasma[6][6];
      dificultad = 8; //NORMAL por defecto
      modo = 1; //Aleatorio por defecto
      teclado = new Scanner(System.in);
   }

   public int getDificultad() {
      return dificultad;
   }

   public void setDificultad(int dif) {
      dificultad = dif;
   }

   public int getModo() {
      return modo;
   }

   public void setModo(int mod) {
      modo = mod;
   }

   public void setPlayer1(Player jugador1) {
      player1 = jugador1;
   }

   public void setPlayer2(Player jugador2) {
      player2 = jugador2;

   }

Tras esto, añadimos un nuevo método llamado fantasmasManual() que lo que hará será reiniciar el tablero, preparar las lista de coordenadas válidas y hacer que los jugadores puedan colocar fantasmas.
Primero coloca jugador 1 y luego colocaría jugador 2

Este proceso de colocar fantasmas, requiere cierta cantidad de código y sería engorroso tener que repetirlo dos veces, una por cada jugador.
Por eso, este proceso lo realizaremos en otro método preparado para recibir como referencia el jugador que coloca y su lista de coordenadas asociada.
Así el mismo código servirá para ambos jugadores y no habrá que escribirlo dos veces.
Código: [Seleccionar]
private void fantasmasManual() {
//Nos aseguramos de que el tablero está vacío
tablero = new Fantasma[6][6];

//Coordenadas válidas para fantasmas player1
ArrayList<Coordenada> coordF1 = new ArrayList<Coordenada>(
Arrays.asList(Coordenada.coordenadasInicioF1()));
//Coordenadas válidas para fantasmas player2
ArrayList<Coordenada> coordF2 = new ArrayList<Coordenada>(
Arrays.asList(Coordenada.coordenadasInicioF2()));

//Primero coloca Player1
colocarManual(player1, coordF1);
System.out.println("\n\t\t----Siguiente Jugador----\n");
//Después coloca Player2
colocarManual(player2, coordF2);
}

Ahora viene el método que hace el "trabajo duro", el método colocarManual()

Usaremos un bucle donde iremos contando los fantasmas que se colocan correctamente y el bucle finalizará cuando se alcance el número de fantasmas que indique el nivel de dificultad.
En cada repetición mostraremos el tablero en su estado actual, las coordenadas válidas y pediremos fila y columna al jugador.
Esta petición se repetirá en otro bucle hasta que nos den una coordenada aceptable.

Cada vez que se coloque un fantasma, se actualiza la lista de coordenadas, se cuenta el fantasma colocado y con un boolean alternamos entre fantasmas buenos y malos para la siguiente colocación.

Código: [Seleccionar]
private void colocarManual(Player jugador, ArrayList<Coordenada> coordenadas) {
int colocados = 0;
boolean fantasmaBueno = true;
while (colocados < dificultad) {

System.out.println("\nJugador " + jugador.getNombre() + " coloca tus Fantasmas.");
System.out.println("Fantasmas colocados: " + colocados + "/" + dificultad);
System.out.println(this);
System.out.println("Coordenadas elegibles [fila,columna]:");
for (Coordenada coor: coordenadas)
System.out.print(coor + " "); //Listamos las coordenadas en pantalla
Coordenada elegida = new Coordenada(0, 0);
//Pedimos FILA y COLUMNA hasta obtener una coordenada válida
do {
System.out.print("\nIntroduce coordenadas para un Fantasma ");
System.out.println(fantasmaBueno?"BUENO":"MALO");
System.out.println("Fila: ");
int fila = Integer.parseInt(teclado.nextLine());
System.out.println("Columna:");
int columna = Integer.parseInt(teclado.nextLine());
elegida = new Coordenada(fila-1, columna-1);
if (!coordenadas.contains(elegida))
System.out.println("Coordenadas no son válidas....\n");
}while(!coordenadas.contains(elegida));
//Tenemos Coordenada válida, colocamos Fantasma
tablero[elegida.fila][elegida.columna] = new Fantasma(fantasmaBueno, "F1");
fantasmaBueno = !fantasmaBueno; //Alternamos entre bueno y malo, para el siguiente fantasma
//Eliminamos la coordenada escogida para la siguiente selección
coordenadas.remove(elegida);
//Contamos fantasmas colocado
colocados++;
}
}

Con esto, ya quedaría resuelto el poder colocar Fantasmas de forma manual.

Para comprobar su funcionamiento, vamos a ir comenzando el método más importante de todos, el método jugar().
Aún no se como resolver el proceso de juego, pero de momento podemos hacer que se coloquen fantasmas según el modo escogido y se muestre el tablero en pantalla.
Así que por ahora, a la clase Castillo le añadimos este método y ya veremos más adelante como evolucionará:

Código: [Seleccionar]
public void jugar() {
//Colocar según modo
if (modo == 1)
fantasmasAzar();
else
fantasmasManual();

System.out.println("\nTablero preparado\n\n" + this);
System.out.println("\n\t\t----Proceso de juego en construcción----");
}

Y en la clase main Ghosts, creamos este otro método que se encargará de loguear a un segundo Player y pedirle a Castillo que empiece el juego:

Código: [Seleccionar]
public static void jugarGhosts() {
System.out.println("\nLOGIN Player 2...");
System.out.print("Nombre: ");
String nombre = teclado.nextLine();
System.out.print("Contraseña: ");
String password = teclado.nextLine();
Player player2 = gestorPlayers.loginPlayer(nombre, password);
if (player2 == null) {
System.out.println("\nRegresando a Menu Principal...");
pausa();
}
else {
castillo.setPlayer1(logueado);
castillo.setPlayer2(player2);
castillo.jugar();
}
}

Y lo activamos en el método main() para poder escogerlo:
Citar
   public static void main(String[] args) {
      int opcion = 0;
      
      do {
         opcion = menuInicio();
         switch(opcion) {
         case 1:
            login();
            break;
         case 2:
            crearPlayer();
            break;
         case 3:
            System.out.println("\n\n\t\tFIN DE PROGRAMA");
            break;
         default:
            System.out.println("\nOpción inválida\n");
         }
         
         //Esta parte solo se ejecuta si hay un usuario logueado
         while(logueado != null) {
            switch(menuPrincipal()) {
            case 1:
               jugarGhosts();
               break;
            case 2:
               while(configuracion() != 3);
               break;
            case 3:
               //Reportes
               break;
            case 4:
               //Mi perfil
               break;
            case 5:
               logueado = null; //Para desloguear y terminar este menu, basta con dar valor null
               break;
            default:
               System.out.println("\nOpción inválida\n");
            }
         }//Fin del menu principal
         
      }while (opcion != 3);
      
   }


A continuación hago una prueba, cambiando las opciones a modo MANUAL y dificultad GENIUS (para colocar solo 2 Fantasmas).
Y logueo a un segundo Player que ya había registrado anteriormente:

Citar
Buscando datos de Jugadores en disco...
Datos de Jugadores registrados cargados...



      MENU INICIO
      ---- ------

(1) - Login
(2) - Crear Player
(3) - SALIR
Escoja opción: 1

LOGIN....
Nombre: Kabuto
Contraseña: 1234


      MENU PRINCIPAL
      ---- ---------

(1) - Jugar GHOSTS
(2) - Configuración
(3) - Reportes
(4) - Mi Perfil
(5) - SALIR
Escoja opción: 2

   CONFIGURACIÓN de GHOSTS
(1) - Dificultad
(2) - Modo de Juego
(3) - Volver a Menú Principal
Escoja opción: 1

   Configurar Dificultad
(1) - NORMAL - 8 Fantasmas por jugador
(2) - EXPERT - 4 Fantasmas por jugador
(3) - GENIUS - 2 Fantasmas por jugador
(4) - Volver a Menú Configuración
Escoja opción: 3

Dificultad establecida a GENIUS

      Pulse ENTER para continuar...


   Configurar Dificultad
(1) - NORMAL - 8 Fantasmas por jugador
(2) - EXPERT - 4 Fantasmas por jugador
(3) - GENIUS - 2 Fantasmas por jugador
(4) - Volver a Menú Configuración
Escoja opción: 4

Volviendo a Menu Configuración...

   CONFIGURACIÓN de GHOSTS
(1) - Dificultad
(2) - Modo de Juego
(3) - Volver a Menú Principal
Escoja opción: 2

   Configurar Modo Juego
(1) - ALEATORIO - Fantasmas se colocan al azar
(2) - MANUAL - Los Jugadores colocan Fantasmas
(3) - Volver a Menú Configuración
Escoja opción: 2

Modo establecido a MANUAL

      Pulse ENTER para continuar...


   Configurar Modo Juego
(1) - ALEATORIO - Fantasmas se colocan al azar
(2) - MANUAL - Los Jugadores colocan Fantasmas
(3) - Volver a Menú Configuración
Escoja opción: 3

Volviendo a Menu Configuración...

   CONFIGURACIÓN de GHOSTS
(1) - Dificultad
(2) - Modo de Juego
(3) - Volver a Menú Principal
Escoja opción: 3

Volviendo a Menu Principal...


      MENU PRINCIPAL
      ---- ---------

(1) - Jugar GHOSTS
(2) - Configuración
(3) - Reportes
(4) - Mi Perfil
(5) - SALIR
Escoja opción: 1

LOGIN Player 2...
Nombre: Casper
Contraseña: casper

Jugador Kabuto coloca tus Fantasmas.
Fantasmas colocados: 0/2
    1   2   3   4   5   6
 1 __  __  __  __  __  __

 2 __  __  __  __  __  __

 3 __  __  __  __  __  __

 4 __  __  __  __  __  __

 5 __  __  __  __  __  __

 6 __  __  __  __  __  __


Coordenadas elegibles [fila,columna]:
[1,2] [1,3] [1,4] [1,5] [2,2] [2,3] [2,4] [2,5]
Introduce coordenadas para un Fantasma BUENO
Fila:
1
Columna:
3

Jugador Kabuto coloca tus Fantasmas.
Fantasmas colocados: 1/2
    1   2   3   4   5   6
 1 __  __  F1  __  __  __

 2 __  __  __  __  __  __

 3 __  __  __  __  __  __

 4 __  __  __  __  __  __

 5 __  __  __  __  __  __

 6 __  __  __  __  __  __


Coordenadas elegibles [fila,columna]:
[1,2] [1,4] [1,5] [2,2] [2,3] [2,4] [2,5]
Introduce coordenadas para un Fantasma MALO
Fila:
2
Columna:
2

      ----Siguiente Jugador----


Jugador Casper coloca tus Fantasmas.
Fantasmas colocados: 0/2
    1   2   3   4   5   6
 1 __  __  F1  __  __  __

 2 __  F1  __  __  __  __

 3 __  __  __  __  __  __

 4 __  __  __  __  __  __

 5 __  __  __  __  __  __

 6 __  __  __  __  __  __


Coordenadas elegibles [fila,columna]:
[5,2] [5,3] [5,4] [5,5] [6,2] [6,3] [6,4] [6,5]
Introduce coordenadas para un Fantasma BUENO
Fila:
5
Columna:
5

Jugador Casper coloca tus Fantasmas.
Fantasmas colocados: 1/2
    1   2   3   4   5   6
 1 __  __  F1  __  __  __

 2 __  F1  __  __  __  __

 3 __  __  __  __  __  __

 4 __  __  __  __  __  __

 5 __  __  __  __  F1  __

 6 __  __  __  __  __  __


Coordenadas elegibles [fila,columna]:
[5,2] [5,3] [5,4] [6,2] [6,3] [6,4] [6,5]
Introduce coordenadas para un Fantasma MALO
Fila:
6
Columna:
5

Tablero preparado

    1   2   3   4   5   6
 1 __  __  F1  __  __  __

 2 __  F1  __  __  __  __

 3 __  __  __  __  __  __

 4 __  __  __  __  __  __

 5 __  __  __  __  F1  __

 6 __  __  __  __  F1  __



      ----Proceso de juego en construcción----


      MENU PRINCIPAL
      ---- ---------

(1) - Jugar GHOSTS
(2) - Configuración
(3) - Reportes
(4) - Mi Perfil
(5) - SALIR
Escoja opción: 5


      MENU INICIO
      ---- ------

(1) - Login
(2) - Crear Player
(3) - SALIR
Escoja opción: 3


      FIN DE PROGRAMA


El proceso parece funcionar correctamente. En la próxima pensaremos como ha de ser la lógica del juego.

El código completo puede verse en la siguiente respuesta.

550
Vaale, ahora sí lo he entendido.

Y de momento solo puedo decir que quién se le haya ocurrido proponer este ejercicio.., es una mala persona  ;D

Nunca había visto este ejercicio y ahora mismo no sabría como resolverlo, al menos no de forma eficiente.
Este es de esos ejercicios que no solo exige saber programar, si no también tener ingenio para decidir "qué es lo que hay que programar"...

Así a bote pronto, se me ocurre una forma, pero creo que podría requerir bastante código y por tanto no se si sería buena solución o existe una más ingeniosa, simple y eficiente.

A mí lo que se me ocurre es "desplazar" una posición los elementos de uno de los arrays y ver si tras desplazar coincide con el otro array.
Si no coincide, se vuelve a desplazar y se vuelve a comprobar.. y así sucesivamente hasta que coincida, o hasta que los elementos hayan dado una vuela completa.

Es decir, tenemos estos arrays:
1,6,3,9,1
3,9,1,1,6

Cogemos el primero y desplazamos una posición todos sus elementos:
1,1,6,3,9
3,9,1,1,6

¿Coinciden? No, desplazamos otra vez..
9,1,1,6,3
3,9,1,1,6

¿Coinciden? No, desplazamos otra vez..
3,9,1,1,6
3,9,1,1,6

¿Coinciden?, Sí, pues entonces es que sí cumplen la secuencia cíclica.

Si tras hacer 5 desplazamientos (porque son 5 elementos) nunca coincide con el otro array, es que NO cumplen la secuencia cíclica.


De este modo se puede solucionar, pero quizás existe otra forma más eficiente...aunque a mi no se me ocurra.
De hecho, esto que propongo es básicamente lo que hacemos nosotros mentalmente para discenir a simple vista si cumplen la secuencia o no.., desplazar números hasta comprobar que llega un momento en que ambos coinciden.


Programar esto puede ser algo engorroso, así que sería importante "modular" el código en al menos un par de funciones.
Una función que desplace los elementos de un array, y otra función que compare dos arrays y diga si son iguales o no.


551
De todo un poco... / Re: Consejo Como Empezar A Aprender A Programar
« en: 22 de Diciembre 2020, 00:44 »
Hombre, si realmente quieres dedicarte a esto, podrías plantearte acceder y cursar un ciclo formativo de grado superior, ya sea por pública o privada (ojo, puede ser muchísimo más caro)

Un DAM (Desarrollo de Aplicaciones Multiplataforma) son solo 2 años, se comienza desde cero y es bastante completo.
No solo es programación, también desarrollo de bases de datos, tipos de infraestructura informática de una empresa, lenguaje de marcas (XML y HTML),...

También está el DAW (Desarrollo de Aplicaciones Web), similar pero enfocado a programación web. De hecho, el primer año es igual para ambos, así que si terminas DAM, luego puedes cursar el segundo año de DAW y en tres años tendrías ambas titulaciones.

Esto suele haber opciones de cursarse presencialmente, o semipresencial o a distancia.
Ya depende de la oferta de tu ciudad, eso tendrías que investigarlo.

Esto, en el caso de quieras tener una formación reglada.


Lo cierto es que se puede aprender a programar por uno mismo, la web está rebosante de cursos, ejercicios, ejemplos, etc....
Y, aunque es realmente muy satisfactorio..., requiere más esfuerzo y la educación obtenida no será tan completa.
A parte de que no tendrás ningún título oficial que pueda avalar tus conocimientos..

Incluso cursando un ciclo formativo presencialmente, va a ser muy importante que uno mismo practique y sepa ampliar por sí mismo los conocimientos por la red.


Sobre que lenguaje aprender.., lo importante en realidad es aprender a programar, es decir, aprender "Metodología de la programación", ya que esta es común a todos los lenguajes.
Pero claro, para programar se necesita un lenguaje, así que indudablemente hay que escoger uno, aunque solo sea para iniciarse.

No soy ningún experto, así que solo puedo dar mi opinión basada en mi (escasa) experiencia.
Pero creo que el lenguaje Java tiene un buen equilibrio entre facilidad de uso, potencia y demanda laboral.

Hay lenguajes más potentes pero menos sencillos, otros más sencillos pero menos potentes, otros más cómodos de escribir por tener menos reglas sintácticas pero que pueden inducir a acostumbrarse a malas prácticas para cuando el programador tenga que moverse en otros lenguajes menos permisivos con la sintaxis...

Yo creo que Java tiene ese puntito dulce entre todos esos aspectos. Y una vez que se ha aprendido lo que es la metodología de la programación(bucles, variables, arrays, procedimientos, funciones, clases, objetos, herencias...) luego es fácil ir aprendiendo otros lenguajes, sean cuales sean...

Y además para Java, hay montones y montones de cursos. En esta misma web tienes uno y si prefieres algo más visual, en Youtube también encontrarás cientos de tutoriales.

552
Se me ocurre que una forma de solucionarlo podría ser ordenar los números, de menor a mayor por ejemplo.
Una vez ambos arrays estén ordenados, deberían ser exactamente iguales.

Es decir, en la posición
  • de ambos arrays estará el mismo número, en posición [1] también,... etc..


Si en alguna posición el valor de un array no coincide con el otro, entonces es que NO se cumple la secuencia cíclica

553
Comunidad / Re: Feliz Navidad y Próspero Año Nuevo para todos
« en: 20 de Diciembre 2020, 23:20 »
 :D Felices fiestas a todos, y tomad todas las precauciones posibles contra el COVID.
Ojo con las reuniones

554
Menú Configuración.

El usuario debe poder configurar la dificultad del juego y el modo.

De momento vamos a escribir el menú para estas opciones.

En la clase principal Ghosts podemos escribir este método:
Código: [Seleccionar]
private static int configuracion() {
int opcion = 0;
System.out.println("\n\tCONFIGURACIÓN de GHOSTS");
System.out.println("(1) - Dificultad");
System.out.println("(2) - Modo de Juego");
System.out.println("(3) - Volver a Menu Principal");
System.out.print("Escoja opción: ");
opcion = Integer.parseInt(teclado.nextLine());
switch(opcion) {
case 1:
//configDificultad();
break;
case 2:
//configModoJuego();
break;
case 3:
System.out.println("\nVolviendo a Menu Principal...");
break;
default:
System.out.println("\nOpción inválida\n");
}

return opcion;
}

Similar a los anteriores, el método se encarga de mostrar el menú y retornar la opción que escoja el usuario.
De momento solo funciona la opción de SALIR.
Este método lo vamos a invocar en el método main(), dentro del switch del menú principal, es decir, el menú que se muestra cuando hay un usuario logueado:

Citar
         //Esta parte solo se ejecuta si hay un usuario logueado
         while(logueado != null) {
            switch(menuPrincipal()) {
            case 1:
               //Jugar GHOSTS
               break;
            case 2:
               while(configuracion() != 3);
               break;
            case 3:
               //Reportes
               break;
            case 4:
               //Mi perfil
               break;
            case 5:
               logueado = null; //Para desloguear y terminar este menu, basta con dar valor null
               break;
            default:
               System.out.println("\nOpción inválida\n");
            }
         }//Fin del menu principal

Con esa línea haremos que el menú se repita hasta que el usuario teclee el valor 3, que es la opción de SALIR.

Bien, si ejecutamos el programa veremos que ya podemos accede al Menú de Configuración, pero como hemos dicho, aún no tiene sus opciones habilitadas.

Dichas opciones, van a requerir cada una mostrar un submenú para que el usuario escoja entre las tres posibles dificultades y entre los dos posibles modos de juego.

La Dificultad y el Modo de Juego, son atributos de la clase Castillo, que es la clase que representa el tablero y que seguramente (quizás no, quien sabe..) haremos que sea el "motor" del juego.
Y esto nos lleva a tener que tomar ahora una decisión de diseño.

Ahora mismo, la clase principal Ghosts no "conoce" a la clase Castillo. No tiene ninguna referencia ni instancia a dicha clase.
Aunque pidamos ahora al usuario datos para configurar el juego desde la clase principal Ghosts (que es quien muestra menús), no vamos a saber que hacer con esos datos porque no tenemos ningún Castillo.

La clase Castillo la escribimos con un constructor pensado para recibir directamente los dos Players y que empezase el juego, es decir, con la idea de instanciar en la clase principal un Castillo para cada partida, en cuanto esta estuviese lista para comenzar.
Y cuando la partida terminase, el objeto Castillo dejaría de existir y ya se instanciará otro nuevo en la próxima partida.

Pero ahora vemos que queremos poder configurar los atributos de Castillo sin que vaya a comenzar la partida todavía. Parece que nos va a resultar más útil tener un Castillo ya instanciado siempre disponible para poder trabajar sobre él, se vaya a jugar o no.

Así que en la clase Ghosts, vamos a añadir otra variable de ámbito global, y será una referencia a un objeto Castillo que estará permanentemente instanciado:
Citar
public class Ghosts {
   
   private static Player logueado; //Jugador actualmente logueado
   private static GestorPlayers gestorPlayers = new GestorPlayers(); //Gestión de Jugadores
   private static Castillo castillo = new Castillo();
   private static Scanner teclado = new Scanner(System.in);

El constructor de Castillo nos exige proporcionarle dos objetos Player, pero lo vamos a cambiar ya que queremos poder instanciarlo sin esa obligación.

Así que vamos a la clase Castillo a modificar su constructor, y a asegurarnos de que tenemos atributos para "dificultad" y "modo de juego", así como getters y setters para ellos.

Además como ahora no recibiremos los Player por constructor, también necesitaremos al menos unos setters para poder recibirlos mediante métodos.

Citar
public class Castillo {
   
   private Fantasma[][] tablero;
   private Player player1;
   private Player player2;
   private int dificultad; //NORMAL–8 fantasmas, EXPERT–4 fantasmas, GENIUS–2 fantasmas
   private int modo; // 1-Aleatorio, 2-Manual
   
   public Castillo() {
      tablero = new Fantasma[6][6];
      dificultad = 8; //NORMAL por defecto
      modo = 1; //Aleatorio por defecto
   }
   
   public int getDificultad() {
      return dificultad;
   }

   
   public void setDificultad(int dif) {
      dificultad = dif;
   }

   
   public int getModo() {
      return modo;
   }

   
   public void setModo(int mod) {
      modo = mod;
   }

   
   public void setPlayer1(Player jugador1) {
      player1 = jugador1;
   }

   
   public void setPlayer2(Player jugador2) {
      player2 = jugador2;
   }

   
   /**
    * Coloca Fantasmas en posiciones al azar en el tablero para iniciar el juego.
    * Solo hay un número determinado de posiciones válidas donde
    * poder colocar y estas posiciones nos las proporciona la clase Coordenada.
    * La cantida de Fantasmas a colocar depende del nivel de dificultad.
    */
   public void fantasmasAzar() {
      //Nos aseguramos de que el tablero está vacío
      tablero = new Fantasma[6][6];
      
      //Coordenadas válidas para fantasmas player1
      ArrayList<Coordenada> coordF1 = new ArrayList<Coordenada>(
            Arrays.asList(Coordenada.coordenadasInicioF1()));
      /*
       * Iremos seleccionando coordenadas al azar para colocar Fantasmas,
       * alternando entre buenos y malos.
       * Cada coordenada seleccionada, será eliminada de la lista
       * para que en la siguiente selección al azar no vuelva a repetirse.
       * La cantidad de fantasmas a colocar depende de la dificultad
       */
      boolean fantasmaBueno = true;
      Random azar = new Random();
      for (int i = 0; i < dificultad; i++) {
         int posRandom = azar.nextInt(coordF1.size()); //Limite random depende de cuantas coordenadas hay
         Coordenada coord = coordF1.remove(posRandom); //Retiramos una coord al azar y la recogemos
         //Creamos fantasma en la coordenada random
         tablero[coord.fila][coord.columna] = new Fantasma(fantasmaBueno, "F1");
         fantasmaBueno = !fantasmaBueno; //Alternamos entre bueno y malo, para el siguiente fantasma
      }
      
      //Repetimos el mismo proceso, para el player2
      ArrayList<Coordenada> coordF2 = new ArrayList<Coordenada>(
            Arrays.asList(Coordenada.coordenadasInicioF2()));
      fantasmaBueno = true;
      for (int i = 0; i < dificultad; i++) {
         int posRandom = azar.nextInt(coordF2.size());
         Coordenada coord = coordF2.remove(posRandom);
         tablero[coord.fila][coord.columna] = new Fantasma(fantasmaBueno, "F2");
         fantasmaBueno = !fantasmaBueno;
      }
   }
   
   @Override
   public String toString() {
      //Primera línea, coordenadas horizontales
      StringBuilder sb = new StringBuilder("    1   2   3   4   5   6\n");
      for (int fila = 0; fila < 6; fila++) {
         sb.append(" " + (fila + 1)); //Cada fila comienza con coordinada vertical
         for (int col = 0; col < 6; col++) {
            if (tablero[fila][col] == null)
               sb.append(" __ ");
            else
               sb.append(tablero[fila][col]);
         }
         sb.append("\n\n");
      }
      
      return sb.toString();
   }

}


Con esto, ahora la clase Ghosts ya sí "conoce" a Castillo, pues ya es un atributo suyo.

Pues en Ghosts, ya podemos crear sendos métodos para...

Configurar Dificultad:
Código: [Seleccionar]
private static int configDificultad() {
int opcion = 0;
System.out.println("\n\tConfigurar Dificultad");
System.out.println("(1) - NORMAL - 8 Fantasmas por jugador");
System.out.println("(2) - EXPERT - 4 Fantasmas por jugador");
System.out.println("(3) - GENIUS - 2 Fantasmas por jugador");
System.out.println("(4) - Volver a Menú Configuración");
System.out.print("Escoja opción: ");
opcion = Integer.parseInt(teclado.nextLine());
switch(opcion) {
case 1:
castillo.setDificultad(8);
System.out.println("\nDificultad establecida a NORMAL");
pausa();
break;
case 2:
castillo.setDificultad(4);
System.out.println("\nDificultad establecida a EXPERT");
pausa();
break;
case 3:
castillo.setDificultad(2);
System.out.println("\nDificultad establecida a GENIUS");
pausa();
break;
case 4:
System.out.println("\nVolviendo a Menu Configuración...");
break;
default:
System.out.println("\nOpción inválida\n");
}

return opcion;
}

Configurar Modo de Juego:
Código: [Seleccionar]
private static int configModoJuego() {
int opcion = 0;
System.out.println("\n\tConfigurar Modo Juego");
System.out.println("(1) - ALEATORIO - Fantasmas se colocan al azar");
System.out.println("(2) - MANUAL - Los Jugadores colocan Fantasmas");
System.out.println("(3) - Volver a Menú Configuración");
System.out.print("Escoja opción: ");
opcion = Integer.parseInt(teclado.nextLine());
switch(opcion) {
case 1:
castillo.setModo(1);
System.out.println("\nModo establecido a ALEATORIO");
pausa();
break;
case 2:
castillo.setModo(2);
System.out.println("\nModo establecido a MANUAL");
pausa();
break;
case 3:
System.out.println("\nVolviendo a Menu Configuración...");
break;
default:
System.out.println("\nOpción inválida\n");
}

return opcion;
}


Y una vez escritos, los invocamos dentro del switch del método configuracion()

Citar
   private static int configuracion() {
      int opcion = 0;
      System.out.println("\n\tCONFIGURACIÓN de GHOSTS");
      System.out.println("(1) - Dificultad");
      System.out.println("(2) - Modo de Juego");
      System.out.println("(3) - Volver a Menú Principal");
      System.out.print("Escoja opción: ");
      opcion = Integer.parseInt(teclado.nextLine());
      switch(opcion) {
      case 1:
         while(configDificultad() != 4);
         break;
      case 2:
         while(configModoJuego() != 3);
         break;
      case 3:
         System.out.println("\nVolviendo a Menu Principal...");
         break;
      default:
         System.out.println("\nOpción inválida\n");
      }
      
      return opcion;
   }


Y con esto ya queda cubierta la función de configurar las opciones del juego.

555
Hola y bienvenido.

En los métodos donde se van a modificar las cadenas, está bien usar StringBuilder porque seguramente vamos a seguir un proceso donde vamos a reconstruir una cadena haciendo la modificaciones que nos piden, por ejemplo, insertar asteriscos allá donde encontremos un diptongo.

Pero la cadena original sobre la que se va a trabajar no es necesario que sea StringBuilder.
Podría serlo, pero diría que es más correcto que sea un String normal.

La cadena original se supone que se ha de pedir al usuario por teclado, pero ahora mismo es más cómodo hacer pruebas con una cadena ya escrita en el código.
Lo de pedir cadena por teclado lo dejaría para el final, cuando todo lo demás esté terminado.

Sobre encontrar diptongos, te voy a proponer un código para resolverlo.
No es la única forma de hacerlo y de hecho seguramente hay formas más sencillas de hacerlo utilizando algunos métodos que ofrece la clase String como el replace() y/o usando expresiones regulares (no importa si no sabes lo que son, ya llegarás a ellas cuando toque)

Pero la forma que propongo, digamos que es un poco más "artesanal", es usando lógica creada por nosotros y no lógica que ya venga programada de serie por otros.


Lo primero es saber qué es un diptongo.
Un diptongo es cuando en una palabra encontramos dos vocales juntas, y una de ellas o ambas, es una vocal débil (Vocales débiles son : i, u)

Así que vamos a tener que recorrer la cadena original, letra a letra, buscando vocales.
Por tanto, para facilitar la tarea, nos vendría bien un método que nos diga si un carácter (una letra) es vocal o no.
Por ejemplo, este método:
Código: [Seleccionar]
private static boolean esVocal(char caracter) {
switch(caracter) {
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
return true;
default:
return false;
}
}
Es muy sencillo, si el caracter es a,e,i,o,u.. retorna TRUE.
Si es CUALQUIER otra cosa, retorna FALSE.

Bien, además de esto, nos puede interesar otro método que nos diga si el caracter que recibe, es una vocal débil o no.
Por ejemplo:
Código: [Seleccionar]
private static boolean esDebil(char caracter) {
if (caracter == 'i' || caracter == 'u')
return true;
else
return false;
}

Es tan sencillo que no requiere explicación.
Fíjate que son porciones de código muy simples, y casi podríamos pensar que para tan poca cosa, no vale la pena crear métodos.
Pero lo cierto es que esto luego ayudará a que el código que escribamos después, sea más sencillo de leer y de entender.

Y ahora vamos con el método diptongos()
Haremos un método que reciba un String con la cadena original,y retornará otro String nuevo, con las modificaciones que hayan sido necesarias hacer en la cadena original.

Estas modificaciones van a consistir en señalar con asteriscos los diptongos que pueda contener esa cadena.

La lógica a seguir será:
- Iniciar un StringBuilder

- Recorrer cada carácter de la cadena original con un bucle e ir añadiéndolos al StringBuilder, intercalando asteriscos en caso de encontrar diptongos.

- Al seleccionar un carácter, comprobamos si es vocal.
   Si no es vocal, no hacemos nada, lo añadimos al StringBuilder y pasamos al siguiente
   Pero si es vocal, entonces tenemos que seleccionar también el siguiente carácter.

- Si el siguiente carácter NO es vocal, tampoco hacemos nada. Lo ignoramos y añadimos la vocal seleccionada al StringBuilder.
  Pero, si el siguiente carácter SI es vocal, ahora tenemos que comprobar si una de estas vocales, o ambas, son DÉBILES.

- Si ninguna es débil, no hacemos nada. Ignoramos la vocal siguiente y añadimos la vocal actual al StringBuilder.
   Pero, si una o ambas son DÉBILES, entonces al StringBuilder añadimos un asterisco, luego las dos vocales, y por último otro asterisco, quedando así señalizado el diptongo.
   Además, en este caso, debemos hacer un incremento extra en el indice del bucle que recorre las letras, porque ya hemos comprobado y seleccionado la letra actual y la siguiente.
Así que el indice del bucle ha de hacer doble incremento para no que no seleccione el carácter siguiente al actual, ya que ambos ya han sido comprobados.

Este sería el código:
Código: [Seleccionar]
public static String diptongos(String cadena){

/*
* Un DIPTONGO es cuando se juntan una vocal fuerte y una débil,
* o bien dos vocales débiles.
* Así que buscaremos caracteres que sean VOCALES.
* Al encontrar una VOCAL, comprobaremos el carácter siguiente.
* Si también es VOCAL, comprobaremos si una de ellas,
* o ambas, son débiles, en cuyo caso tendremos un DIPTONGO
*/
StringBuilder aux = new StringBuilder();
char caracter = ' ';

//Comprobaremos todos los caracteres, uno a uno
try {
for (int i = 0 ; i < cadena.length(); i++){
//Cogemos un caracter de la cadena
caracter = cadena.toLowerCase().charAt(i);
//comprobamos si es vocal
if (esVocal(caracter)) {
//Es vocal, comprobemos siguiente caracter
char siguiente = cadena.toLowerCase().charAt(i+1);
if (esVocal(siguiente)) {
/*
* Si alguno de estos caracteres, o ambos,
* son vocales DÉBILES, entonces tenemos
* un DIPTONGO
*/
if (esDebil(caracter) || esDebil(siguiente)) {
//Es DIPTONGO, añadimos caracteres rodeados de asteriscos
aux.append('*');
aux.append(cadena.charAt(i));
aux.append(cadena.charAt(i+1));
aux.append('*');
/*
* Como ya hemos comprobado este caracter y el siguiente,
* aumentamos indice del bucle para evitar volver a comprobar
* el siguiente caracter
*/
i++;
}
else //No hay diptongo, agregamos caracter sin hacer nada
aux.append(cadena.charAt(i));
}
else //Segundo caracter no es vocal, añadimos primer caracter sin hacer nada
aux.append(cadena.charAt(i));
}
else //Primer caracter no es vocal, agregamos caracter sin hacer nada
aux.append(cadena.charAt(i));
}
}
catch(IndexOutOfBoundsException ex) {
/*
* Hemos intentado acceder a una posición de la cadena
* que está fuera de rango.
* Esto ocurrirá cuando el último carácter de la cadena es vocal
* y queremos comprobar el siguiente carácter para ver
* si hay diptongo, pero como ya estamos en el último
* no hay un caracter siguiente.
*
* En este caso, lo único que tenemos que hacer
* es agregar este último carácter al StringBuilder sin hacer nada
* más.
*/
aux.append(cadena.charAt(cadena.length() - 1 ));
}

//Retornamos el StringBuilder, convertido en un String "normal"
return aux.toString();
}
Parece que hay mucho código, pero es por los comentarios.
Si los quitas, verás que en realidad son unas pocas líneas y que además son muy legibles gracias a los dos métodos que hemos escrito previamente.

Ahora, vamos preparar el método main() para poner a prueba este método.

Como dije, mejor usar un String normal y además ya escrito en el código para agilizar las pruebas.
Interesa que primero se muestre la cadena original, y luego se vaya mostrando de nuevo sucesivamente cada vez que se le haga modificaciones.

Código: [Seleccionar]
public static void main(String []args){

// -- Pedir la cadena y guardar en CadenaUsuario

String cadenaUsuario = "Ejemplos de DIPTONGOS: piensa, reino, puesto, pausa";

if (isNull(cadenaUsuario) ){
System.out.println("La cadena es vacia");
}else{
System.out.println("Cadena original:");
System.out.println(cadenaUsuario);

System.out.println("\n- Busqueda de DIPTONGOS:");
cadenaUsuario = diptongos(cadenaUsuario);
System.out.println(cadenaUsuario);
}

}

En este caso, veremos la cadena original, y luego la volveremos a ver con los diptongos señalizados con asteriscos.
En pantalla sale esto:

Código: [Seleccionar]
Cadena original:
Ejemplos de DIPTONGOS: piensa, reino, puesto, pausa

- Busqueda de DIPTONGOS:
Ejemplos de DIPTONGOS: p*ie*nsa, r*ei*no, p*ue*sto, p*au*sa

Parece que funciona correctamente  :)

Analiza bien el código que he propuesto. Comprueba que entiendes cada línea de código, intenta discernir por ti mismo lo que te pueda resultar extraño, por ejemplo por qué estoy usando el método .toLowerCase(). para seleccionar caracteres...

Y lo que no entiendas, no dudes en preguntarlo.

Y una vez lo entiendas, a ver si te sirve de inspiración para realizar el resto de cosas que pide el enunciado.
Si no, también te ayudaremos, no te preocupes.

Un saludo

556
Vamos a dar posibilidad de crear Players.

Lo primero es dar a GestorPlayers un método para que pueda recibir un objeto Player y añadirlo al ArrayList, excepto si ya existe alguien con el mismo nombre.

Código: [Seleccionar]
public boolean nuevoPlayer(Player nuevo) {
//Si se acepta el Player, guardaremos datos en disco
if (!players.contains(nuevo)) {
players.add(nuevo);
guardarPlayers();
return true;
}
else //No se acepta, ya existe un Player con ese nombre
return false;
}

Para comprobar si existe un Player con mismo nombre, nos bastará con usar el método contains() que ofrece el propio ArrayList.
Este método funcionará gracias a que a la clase Player le sobreescribimos el método equals() de manera que considere que dos objetos Player son idénticos si los nombres coinciden.
Si no hubiéramos hecho esto, el método contains() no sabría como comparar los objetos que contiene para determinar si son iguales y no funcionaría correctamente.

Si se acepta el jugador, además se guardará en disco.


Ahora GestorPlayers sabe añadir o rechazar jugadores e informarnos de ello con true o false.
Vamos a la clase principal Ghosts y vamos a crear el método crearPlayer().
Pediremos nombre y contraseña, crearemos un Player y se lo daremos a GestorPlayer para que lo admita o rechace.

Código: [Seleccionar]
private static void crearPlayer() {
System.out.println("\nCrear nuevo PLAYER...");
System.out.print("Nombre: ");
String nombre = teclado.nextLine();
System.out.print("Contraseña: ");
String password = teclado.nextLine();
Player nuevoPlayer = new Player(nombre, password);
//Intentamos registrarlo
if (gestorPlayers.nuevoPlayer(nuevoPlayer)) {
System.out.println("\nNuevo PLAYER registrado...");
//Cuando se registra un nuevo jugador, además queda logueado
logueado = gestorPlayers.loginPlayer(nombre, password);
}
else
System.out.println("\nRegistro rechazado.\nYa existe un PLAYER con ese nombre");

pausa();
}

Si lo admite, además haremos login con este nuevo jugador y esto hará que podamos entrar en el bucle while que solo se ejecuta cuando hay un Player logueado.
Este bucle ha de mostrarnos el menu principal con 5 opciones posibles, así que ya podemos escribir también un método que muestre esas opciones y retorne la escogida por el usuario.

Código: [Seleccionar]
private static int menuPrincipal() {
System.out.println("\n\n\t\tMENU PRINCIPAL");
System.out.println("\t\t---- ---------\n");
System.out.println("(1) - Jugar GHOSTS");
System.out.println("(2) - Configuración");
System.out.println("(3) - Reportes");
System.out.println("(4) - Mi Perfil");
System.out.println("(5) - SALIR");
System.out.print("Escoja opción: ");
return Integer.parseInt(teclado.nextLine());
}

Esta opción retornada, de nuevo usaremos un SWITCH para evaluarla y actuar según la opción escogida.
Este SWITCH lo colocamos en el método main, donde ya teníamos puesto el bucle while que solo se ejecuta cuando hay alguien logueado.
De las 5 opciones, de momento solo funcionará la 5, la de hacer logout.
Para hacer logout, basta con devolver valor null al atributo "logueado".

Citar
   public static void main(String[] args) {
      int opcion = 0;
      
      do {
         opcion = menuInicio();
         switch(opcion) {
         case 1:
            login();
            break;
         case 2:
            crearPlayer();
            break;
         case 3:
            System.out.println("\n\n\t\tFIN DE PROGRAMA");
            break;
         default:
            System.out.println("\nOpción inválida\n");
         }
         
         //Esta parte solo se ejecuta si hay un usuario logueado
         while(logueado != null) {
            switch(menuPrincipal()) {
            case 1:
               //Jugar GHOSTS
               break;
            case 2:
               //configuracion
               break;
            case 3:
               //Reportes
               break;
            case 4:
               //Mi perfil
               break;
            case 5:
               logueado = null; //Para desloguear y terminar este menu, basta con dar valor null
               break;
            default:
               System.out.println("\nOpción inválida\n");
            }
         }//Fin del menu principal

         
      }while (opcion != 3);
      
   }


Con todo esto ya podemos probar a crear un nuevo jugador y loguearnos.
Pero antes, un inciso.
En el método crearPlayer() lo finalizo invocando a otro método llamado pausa()
Este método es para detener el programa hasta que el usuario pulse la tecla ENTER, para que pueda leer los mensajes en pantalla antes de que el programa siga mostrando más texto.
Esto lo hago usando el Scanner, haciendo una petición de teclado que no recojo en ningún sitio.
Es solo para que el programa espere a que el usuario pulse ENTER.

Código: [Seleccionar]
private static void pausa() {
System.out.println("\n\t\tPulse ENTER para continuar...");
teclado.nextLine();
}

Aclarado esto, vamos a ejecutar el programa a ver que sale en pantalla:
Citar
Buscando datos de Jugadores en disco...
No existe fichero:
C:\Users\orcok\JuegoGHOSTS\players.dat
El juego comenzará sin datos de Jugadores registrados


      MENU INICIO
      ---- ------

(1) - Login
(2) - Crear Player
(3) - SALIR
Escoja opción: 2

Crear nuevo PLAYER...
Nombre: Kabuto
Contraseña: 1234

Nuevo PLAYER registrado...

      Pulse ENTER para continuar...



      MENU PRINCIPAL
      ---- ---------

(1) - Jugar GHOSTS
(2) - Configuración
(3) - Reportes
(4) - Mi Perfil
(5) - SALIR
Escoja opción: 5


      MENU INICIO
      ---- ------

(1) - Login
(2) - Crear Player
(3) - SALIR
Escoja opción: 3


      FIN DE PROGRAMA

Vemos que, por ahora, seguimos sin tener ningún archivo en disco con jugadores.
Tras crear un nuevo jugador, se hace login porque ahora ya podemos ver el menu principal.
Y si pedimos SALIR (logout) volvemos al menú de inicio y termino el programa.

Si ahora lo volvemos a ejecutar, ya si deberíamos tener un archivo en disco con el jugador registrado, de modo que podré hacer login sin tener que volver a crearlo.

Citar
Buscando datos de Jugadores en disco...
Datos de Jugadores registrados cargados...



      MENU INICIO
      ---- ------

(1) - Login
(2) - Crear Player
(3) - SALIR
Escoja opción: 1

LOGIN....
Nombre: Kabuto
Contraseña: 1234


      MENU PRINCIPAL
      ---- ---------

(1) - Jugar GHOSTS
(2) - Configuración
(3) - Reportes
(4) - Mi Perfil
(5) - SALIR
Escoja opción: 5


      MENU INICIO
      ---- ------

(1) - Login
(2) - Crear Player
(3) - SALIR
Escoja opción: 3


      FIN DE PROGRAMA

¡Bien!
Nada más empezar el programa ya informa de que ha recuperado datos del disco.
Y efectivamente puedo hacer login con los datos del Player creado en la sesión anterior.

Ya hemos completado otro pasito más.

557
Podemos dejar de lado los fantasmas y pensar en los Player.
EL juego ha de mostrar un menú login al principio, así que hay gestionar una lista de Player para registrar, modificar, eliminar y comprobar si existen para admitir el login.

Así que podemos crear una clase que se encargue de esta gestión.
En un ArrayList podemos ir insertando o eliminando jugadores.

Los jugadores tienen nombres y contraseñas, puntos e incluso un historial de juego (que ya incluiremos más adelante).

Todo esto debería permanecer cuando cerramos el programa para que esté disponible en las sesiones siguientes. Si no, cada vez que arrancamos el programa habría que crear nuevos jugadores, y no tendrían ningún historial ni puntos.

Así que el gestor debería guardar en disco los Player que vayamos registrando.
Una forma fácil de hacerlo es "serializando" el ArrayList que va a contener los players, de forma que se guarde en forma de bytes en un archivo en disco.

La clase GestorPlayer podemos comenzarla así:
Código: [Seleccionar]
public class GestorPlayers{

private ArrayList<Player> players;
private final File ficheroPlayers; //Fichero en disco donde se guardarán los players

public GestorPlayers() {
//Guadaré los datos de jugadores en la carpeta personal del usuario que indique el S.O.
ficheroPlayers = new File(System.getProperty("user.home") + "/JuegoGHOSTS/players.dat");
players = new ArrayList<Player>();
cargarPlayers();
}


/**
* Intentará leer el ArrayList de Players guardado en disco
*/
private void cargarPlayers() {
System.out.println("Buscando datos de Jugadores en disco...");
if (ficheroPlayers.exists()) {
try {
ObjectInputStream lector = new ObjectInputStream(new FileInputStream(ficheroPlayers));
//Leemos datos del disco
Object datos = lector.readObject();
//Si es una
if (datos instanceof ArrayList) {
players = (ArrayList<Player>) datos;
System.out.println("Datos de Jugadores registrados cargados...\n");
}
lector.close();
} catch (FileNotFoundException e) {
System.out.println("\nNo se encuentra:\n" + ficheroPlayers.getAbsolutePath()+ "\n");
} catch (IOException e) {
System.out.println("\nNo se puede acceder a:\n" + ficheroPlayers.getAbsolutePath()+ "\n");
} catch (ClassNotFoundException e) {
System.out.println("Los datos de:\n" + ficheroPlayers.getAbsolutePath() +
"pertenecen a una clase desconocida.\n");
}
}
else {
System.out.println("No existe fichero:\n" + ficheroPlayers.getAbsolutePath());
System.out.println("El juego comenzará sin datos de Jugadores registrados");
}
}

/**
* Intentará guardar el ArrayList de Players en disco.
*/
public void guardarPlayers() {

try {
if (!ficheroPlayers.exists()) { //Si no existe...
//Creamos carpeta/directorio..
File carpeta = new File(System.getProperty("user.home") + "/JuegoGHOSTS");
carpeta.mkdir();
//Creamos fichero en disco..
ficheroPlayers.createNewFile();
}
ObjectOutputStream escritor = new ObjectOutputStream(new FileOutputStream(ficheroPlayers));
escritor.writeObject(players);
escritor.close();
} catch (Exception e) {
System.out.println("\nNo se encuentra o no se puede acceder a:\n" +
ficheroPlayers.getAbsolutePath()+ "\n");
System.out.println("No se ha podido guardar el registro de Jugadores.");
}
}


public Player loginPlayer(String nombre, String password) {

for (Player jugador: players) {
if (jugador.getNombre().equals(nombre)) {
if (jugador.getPassword().equals(password))
return jugador; //Login aceptado
else {
//Nombre correcto, pero no la contraseña
System.out.println("Contraseña errónea");
return null;
}
}
}
//Si el bucle ha finalizado su recorrido, es que no existe ese nombre
System.out.println("No existe Jugador con el nombre: " + nombre);
return null;
}

}
Tenemos dos atributos, el ArrayList de jugadores y un objeto File que es donde guadaremos/leeremos los datos en disco.

El constructor inicializa esos atributos. Al objeto File hay que darle nombre y ruta donde vamos a querer tenerlo. En mi caso, lo llamo "players.dat" y lo tendré en una carpeta dentro de mi directorio de usuario personal en mi sistema operativo.
Podemos ponerle cualquier otro nombre y ruta, pero la ruta conviene que no vayamos a tener problemas de permisos de escritura.

Tras inicializar esos atributos, el constructor llama a un método que va a intentar leer ese archivo para recuperar los datos que pueda contener.

Esto lo hace el método cargarPlayers().
Con la clase ObjectInputStream de Java intentará acceder a nuestro fichero de jugadores. Comprobará que lo que contiene es un ArrayList y si es así, lo seteará a nuestro atributo.

Si no es un ArrayList, o no tiene datos, o no existe, o no puede acceder.... pues el juego comenzará con el ArrayList vacío.


El siguiente método es guardarPlayers() quién se encarga de guardar el ArrayList de Player en disco.
Primero comprueba si existe  el archivo. Si no existe, creará su carpeta y luego crearé el archivo.
Y después hará en él la escritura de bytes que componen el ArrayList.

Y ya de paso ponemos también el método loginPlayer()
Este método (no definitivo), es el que usaríamos para loguear a un jugador.
Recibe un nombre y una contraseña, y comprueba en el ArrayList si existe un Player con esos datos.
Si existe, retorna dicho objeto Player, si no, retorna valor null.

Luego se irán añadiendo más métodos, pero de momento ya podemos leer y guardar datos en disco.

Aunque falta un detalle. En Java, para poder serializar objetos, tienen que implementar la interfaz Iterable

ArrayList ya la implementa, pero en realidad estamos guardando objetos de nuestra clase Player y para que puedan guardarse, tenemos que implementar dicha interfaz.
Para esto basta con añadir dos palabras a nuestra clase Player:

Citar
public class Player implements Serializable{
   
   private String nombre;
   private String password;
   private int puntos;

Ya está, nada más. Solo eso.
Los atributos de Player también deben implementar esa interfaz, pero String ya la trae de serie y el int al ser un tipo de dato primitivo, convertirlo a bytes es pan comido, porque eso es lo que es..., un conjunto de 4 bytes.


Bien, tras todo esto, la clase principal Ghosts ya podemos comenzar a construirla según pide el enunciado.
Ha de comenzar mostrando un menú con las opciones: login, crear player y salir.

Además, vamos a poner como variables de ámbito global un objeto Player llamado "logueado" donde tendremos al jugador que haya hecho login o bien tendrá valor null cuando no haya nadie logueado.

También un objeto GestorPlayers y un Scanner para las lecturas de teclado.

Haremos un método para mostrar el menu inicio y que retorne la opción que el usuario escoja.

Y también podemos escribir ya un método para hacer login. Este método pedirá nombre y contraseña para pasarselos al gestor de players, si los datos son correctos en el objeto "logueado" tendremos al Player correspondiente. Si no lo son, seguirá teniendo valor null

Código: [Seleccionar]
public class Ghosts {

private static Player logueado; //Jugador actualmente logueado
private static GestorPlayers gestorPlayers = new GestorPlayers(); //Gestión de Jugadores
private static Scanner teclado = new Scanner(System.in);

public static void main(String[] args) {
int opcion = 0;

do {
opcion = menuInicio();
switch(opcion) {
case 1:
login();
break;
case 2:
//crearPlayer();
break;
case 3:
System.out.println("\n\n\t\tFIN DE PROGRAMA");
}

//Esta parte solo se ejecuta si hay un usuario logueado
while(logueado != null) {
//menu Principal
}

}while (opcion != 3);


}

private static int menuInicio() {
System.out.println("\n\n\t\tMENU INICIO");
System.out.println("\t\t---- ------\n");
System.out.println("(1) - Login");
System.out.println("(2) - Crear Player");
System.out.println("(3) - SALIR");
System.out.print("Escoja opción: ");
return Integer.parseInt(teclado.nextLine());
}

private static void login() {
System.out.println("\nLOGIN....");
System.out.print("Nombre: ");
String nombre = teclado.nextLine();
System.out.print("Contraseña: ");
String password = teclado.nextLine();
//Si los datos son correctos, tendremos un Player en "logueado"
logueado = gestorPlayers.loginPlayer(nombre, password);
//Si no fueron correctos, "logueado" tendrá valor null
}

}

Si lo ejecutamos, lo primero que veremos serán mensajes del Gestor de Players buscando el archivo de registro de jugadores.
Como es la primera vez que lo ejecutamos, ese archivo no existe aún, y eso es lo que nos notificará.
Podemos intentar hacer login, pero como no hay players, no hay forma de loguearse..
Código: [Seleccionar]
Buscando datos de Jugadores en disco...
No existe fichero:
C:\Users\orcok\JuegoGHOSTS\players.dat
El juego comenzará sin datos de Jugadores registrados


MENU INICIO
---- ------

(1) - Login
(2) - Crear Player
(3) - SALIR
Escoja opción: 1

LOGIN....
Nombre: Kabuto
Contraseña: 1234
No existe Jugador con el nombre: Kabuto


MENU INICIO
---- ------

(1) - Login
(2) - Crear Player
(3) - SALIR
Escoja opción: 3


FIN DE PROGRAMA

Así que lo próximo que vamos a querer añadir, será el poder crear nuevos jugadores y así podremos comprobar si se guardan correctamente en disco o no.

558
Algo nuevo que podemos probar a ver como sale, sería lo de colocar Fantasmas al azar.

Estos fantasmas solo se pueden colocar en unas posiciones muy concretas, según correspondan al jugador 1 o al jugador 2.

Así que necesitamos un código que nos de valores al azar para las "filas" y "columnas" del tablero donde se puedan colocar fantasmas, y estas posiciones no se pueden repetir.

Se me ocurre que quizás esto sea más sencillo de hacer si creamos una clase, que podríamos llamar Coordenada, y que sus atributos sean dos valores int para la fila y la columna.
Así las posiciones en el tablero las podemos representar con objetos de esta clase.

Pedir valores random para fila y columna, y controlar que no se repitan,... puede ser un poco difícil y requerir más ciclos de computación.

En cambio, podemos crear de forma predefinida unos ArrayList con las coordenadas que son válidas para colocar Fantasmas al iniciar el juego.
Y de esos ArrayList (uno para jugador 1 y otro para jugador 2), será más sencillo seleccionar una Coordenada al azar, usarla para poner el Fantasma, y eliminarla del ArrayList para asegurarnos de que no se va a repetir esa Coordenada.

Así que esta podría ser la clase Coordenada.
Con sus dos atributos, fila y columna.

Y dos métodos estáticos, para poder invocarlos desde la clase, sin necesidad de instanciar un objeto, que nos devuelvan unos arrays con las coordenadas válidas.
Luego, a partir de esos arrays, crearemos los ArrayList para la selección de Coordenadas al azar.

Código: [Seleccionar]
public class Coordenada {

public int fila;
public int columna;

public Coordenada(int f, int c) {
fila = f;
columna = c;
}

/**
* Proporciona un array con las Coordenadas
* válidas en las que el jugador 1 puede
* colocar sus fantasmas.
* @return Array de Coordenadas.
*/
public static Coordenada[] coordenadasInicioF1() {

Coordenada[] coord = new Coordenada[8]; //Hay 8 Coordenadas válidas para colocar Fantasmas
int i = 0;
/*
* Creamos las coordenadas válidas donde
* de inicio se pueden colocar los fantasmas
* del jugador 1
*/

for (int f = 0; f <= 1; f++)
for (int c = 1; c <= 4; c++) {
coord[i] = new Coordenada(f, c);
i++;
}

return coord;
}

/**
* Proporciona un array con las Coordenadas
* válidas en las que el jugador 2 puede
* colocar sus fantasmas.
* @return Array de Coordenadas.
*/
public static Coordenada[] coordenadasInicioF2() {

Coordenada[] coord = new Coordenada[8];
int i = 0;

for (int f = 4; f <= 5; f++)
for (int c = 1; c <= 4; c++) {
coord[i] = new Coordenada(f, c);
i++;
}

return coord;
}

}

Esos métodos estáticos nos devolverán arrays con las 8 Coordenadas válidas para cada jugador.
Así, en la clase Castillo, podemos crear un método que selecciona al azar entre esas coordenadas y así colocar fantasmas.
Cuando la dificultad es NORMAL, cada jugador coloca 8 Fantasmas, así que se usarán todas las Coordenadas.
Pero hay otros modos de dificultad en los que se seleccionan menos Fantasmas, así que el método ha de poder saber el nivel de dificultad para seleccionar más o menos Coordenadas.

Por tanto, a la clase Castillo le vamos a añadir primero un atributo para controlar la dificultad.
Será un int que alternará entre tres valores posibles: 8 (Normal), 4(Expert) y 2(Genius)

Citar
public class Castillo {
   
   private Fantasma[][] tablero;
   private Player player1;
   private Player player2;
   private int dificultad;
   
   public Castillo(Player p1, Player p2) {
      tablero = new Fantasma[6][6];
      player1 = p1;
      player2 = p2;
      dificultad = 8; //NORMAL por defecto
   }

También dos métodos get y set, para retornar y cambiar la dificultad.
Código: [Seleccionar]
public int getDificultad() {
return dificultad;
}

public void setDificultad(int dif) {
dificultad = dif;
}

Y ahora sí, nos centramos en el método para colocar Fantasmas al azar, según la dificultad:

Código: [Seleccionar]
public void fantasmasAzar() {
//Nos aseguramos de que el tablero está vacío
tablero = new Fantasma[6][6];

//Coordenadas válidas para fantasmas player1
ArrayList<Coordenada> coordF1 = new ArrayList<Coordenada>(
Arrays.asList(Coordenada.coordenadasInicioF1()));
/*
* Iremos seleccionando coordenadas al azar para colocar Fantasmas,
* alternando entre buenos y malos.
* Cada coordenada seleccionada, será eliminada de la lista
* para que en la siguiente selección al azar no vuelva a repetirse.
* La cantidad de fantasmas a colocar depende de la dificultad
*/
boolean fantasmaBueno = true;
Random azar = new Random();
for (int i = 0; i < dificultad; i++) {
int posRandom = azar.nextInt(coordF1.size()); //Limite random depende de cuantas coordenadas hay
Coordenada coord = coordF1.remove(posRandom); //Retiramos una coord al azar y la recogemos
//Creamos fantasma en la coordenada random
tablero[coord.fila][coord.columna] = new Fantasma(fantasmaBueno, "F1");
fantasmaBueno = !fantasmaBueno; //Alternamos entre bueno y malo, para el siguiente fantasma
}

//Repetimos el mismo proceso, para el player2
ArrayList<Coordenada> coordF2 = new ArrayList<Coordenada>(
Arrays.asList(Coordenada.coordenadasInicioF2()));
fantasmaBueno = true;
for (int i = 0; i < dificultad; i++) {
int posRandom = azar.nextInt(coordF2.size());
Coordenada coord = coordF2.remove(posRandom);
tablero[coord.fila][coord.columna] = new Fantasma(fantasmaBueno, "F2");
fantasmaBueno = !fantasmaBueno;
}
}

Y para ponerlo a prueba, podemos mostrar tres tableros desde la clase principal, uno para cada nivel de dificultad:
Código: [Seleccionar]
public class Ghosts {

public static void main(String[] args) {

Castillo castillo = new Castillo(new Player("",""), new Player("",""));

System.out.println("Dificultad NORMAL");
castillo.fantasmasAzar();
System.out.println(castillo);

System.out.println("\n\nDificultad EXPERT");
castillo.setDificultad(4);
castillo.fantasmasAzar();
System.out.println(castillo);

System.out.println("\n\nDificultad GENIUS");
castillo.setDificultad(2);
castillo.fantasmasAzar();
System.out.println(castillo);

}

}

En pantalla tendremos el siguiente resultado, que certifica que los Fantasmas se colocan correctamente:

Código: [Seleccionar]
Dificultad NORMAL
    1   2   3   4   5   6
 1 __  F1  F1  F1  F1  __

 2 __  F1  F1  F1  F1  __

 3 __  __  __  __  __  __

 4 __  __  __  __  __  __

 5 __  F2  F2  F2  F2  __

 6 __  F2  F2  F2  F2  __




Dificultad EXPERT
    1   2   3   4   5   6
 1 __  F1  F1  F1  __  __

 2 __  __  __  __  F1  __

 3 __  __  __  __  __  __

 4 __  __  __  __  __  __

 5 __  F2  __  __  F2  __

 6 __  __  F2  F2  __  __




Dificultad GENIUS
    1   2   3   4   5   6
 1 __  __  __  F1  __  __

 2 __  __  __  F1  __  __

 3 __  __  __  __  __  __

 4 __  __  __  __  __  __

 5 __  __  __  __  __  __

 6 __  F2  __  F2  __  __

559
A ver, este proyecto es bastante extenso y se piden muchas cosas.
Podemos empezar creando algunas clases sencillas.

El código que voy a ir poniendo es algo preliminar, es decir, luego más adelante puede que las clases cambien atributos, o métodos, o incluso se descarten por completo, quien sabe...
No son ideas definitivas, así que me encantaría escuchar sugerencias, correcciones, etc.. y dar forma entre todos este proyecto.

Está claro que vamos a tener una clase Player, con nombre, password y puntos.
Y ya nos dicen que el nombre ha de ser único en la lista de jugadores registrados, así que con el método equals podemos determinar que dos Player con el mismo nombre, son iguales.
Así luego cuando almacenemos objetos Player, en un ArrayList tal vez, será más fácil determinar si ya existe un Player con ese mismo nombre.

Código: [Seleccionar]
public class Player {

private String nombre;
private String password;
private int puntos;

public Player(String userName, String pass) {
nombre = userName;
password = pass;
puntos = 0;
}

public String getNombre() {
return nombre;
}

public void setNombre(String userName) {
nombre = userName;
}

public String getPassword() {
return password;
}

public void setPassword(String pass) {
password = pass;
}

public int getPuntos() {
return puntos;
}

@Override
public boolean equals(Object obj) {
if (obj instanceof Player) {
Player otroJugador = (Player) obj;
//Dos players con el mismo nombre se consideran EL MISMO(iguales)
return nombre.equals(otroJugador.nombre);
}
else
return false;
}
}

En el enunciado se menciona algo de un reporte de los últimos 10 juegos de cada Player, así que supongo que la clase Player también debería tener algún array o ArrayList, que guarde los resultados de esos juegos.
Pero eso ya se añadirá más adelante.

Luego podríamos crear una clase Fantasma. Si realmente la llegamos a necesitar, creo que va a ser muy sencilla.

Hay fantasmas buenos y malos, esto podemos decidirlo con un atributo boolean.

Luego, los fantasmas se han de representar con "F1" si son del primer jugador, o "F2" si son del segundo. Esto podemos controlarlo con un atributo String, así cada fantasma será capaz de decirnos a que jugador pertenece y también como ha de mostrarse en pantalla.
Lo de mostrarse en pantalla, usaremos el método toString().

Código: [Seleccionar]
public class Fantasma {

public boolean esBueno;
private String idFantasma; //F1 o F2

public Fantasma(boolean bueno, String idF) {
esBueno = bueno;
idFantasma = idF;
}

@Override
public String toString() {
return " " + idFantasma + " ";
}

}

También tendremos un Castillo.
Esta clase supongo que acabará teniendo bastante código, pero de momento podemos hacer lo básico, crearle un "tablero" (matriz 6x6).
Este tablero/matriz será de tipo Fantasma, porque eso es lo que va a contener, fantasmas...

Podemos poner también como atributos de clase los dos Player que participarían en el juego. Puede que luego decidamos controlar estos objetos en otro sitio, pero por ahora los pondremos por aquí.

Y con un método toString() podemos decirle cómo ha de mostrarse en pantalla el tablero.
Las casillas null del tablero, es decir, las que no hay fantasmas, mostraremos un par de líneas de subrayado.
Y en las que hay fantasmas, ellos mismos ya saben si han de mostrar "F1" o "F2"
Pondremos unos números de coordenadas horizontal y vertical.

Código: [Seleccionar]
public class Castillo {

private Fantasma[][] tablero;
private Player player1;
private Player player2;

public Castillo(Player p1, Player p2) {
tablero = new Fantasma[6][6];
player1 = p1;
player2 = p2;
}

@Override
public String toString() {
//Primera línea, coordenadas horizontales
StringBuilder sb = new StringBuilder("    1   2   3   4   5   6\n");
for (int fila = 0; fila < 6; fila++) {
sb.append(" " + (fila + 1)); //Cada fila comienza con coordinada vertical
for (int col = 0; col < 6; col++) {
if (tablero[fila][col] == null)
sb.append(" __ ");
else
sb.append(tablero[fila][col]);
}
sb.append("\n\n");
}

return sb.toString();
}

}

Ya podemos crear una clase principal y hacer una prueba a ver como se muestra el tablero en pantalla.
Yo la he llamado Ghosts.
En el main creamos un Castillo con dos Players sin nombre, no importa, es una prueba...
Y mostramos en pantalla a ver como se ve.
Código: [Seleccionar]
public class Ghosts {

public static void main(String[] args) {


Castillo castillo = new Castillo(new Player("",""), new Player("",""));

System.out.println(castillo);

}

}
En pantalla veremos esto:
Código: [Seleccionar]
    1   2   3   4   5   6
 1 __  __  __  __  __  __

 2 __  __  __  __  __  __

 3 __  __  __  __  __  __

 4 __  __  __  __  __  __

 5 __  __  __  __  __  __

 6 __  __  __  __  __  __

No está mal, teniendo en cuenta las limitaciones de la consola de texto.
Podemos volver a la clase Castillo y en el constructor meter unos fantasmas de prueba, a ver como salen.
Citar
   public Castillo(Player p1, Player p2) {
      tablero = new Fantasma[6][6];
      player1 = p1;
      player2 = p2;
      //Fantasmas de prueba
      tablero[0][1] = new Fantasma(true, "F1");
      tablero[1][4] = new Fantasma(true, "F1");
      tablero[4][3] = new Fantasma(true, "F2");
      tablero[5][2] = new Fantasma(true, "F2");

   }

En pantalla se dibuja así:
Código: [Seleccionar]
    1   2   3   4   5   6
 1 __  F1  __  __  __  __

 2 __  __  __  __  F1  __

 3 __  __  __  __  __  __

 4 __  __  __  __  __  __

 5 __  __  __  F2  __  __

 6 __  __  F2  __  __  __

Bueno, ya tenemos un comienzo sobre el que ir desarrollando el proyecto.

560
Ahora que me fijo, en el tamaño prueba sin poner px, solo el valor numérico

Citar
echo "<img src='c:/imagenes/imagen.jpg' width='600' />";

Páginas: 1 ... 23 24 25 26 27 [28] 29 30 31 32 33 ... 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".