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
Aprender a programar desde cero / Re: Ayuda en C++
« en: 14 de Enero 2021, 01:22 »
No se si responderá porque fue alguien que publicó sin registrarse en el foro.

Pero sí, al decir subprogramas, se refiere a las funciones.
También se les llama (o antaño se les solía llamar..) subrutinas.

Funciones y procedimientos son como pequeños programas, dentro del programa principal, por eso también reciben nombres como los que hemos mencionado.

542
Comunidad / Re: Agradecimiento.
« en: 12 de Enero 2021, 11:23 »
Bienvenido.
Cualquier cosa que puedas aportar siempre se agradecerá.

Un saludo.

543
Si ejecutas tu código, verás que no funciona.
Al margen de unas pequeñas correciones necesarias en la sintaxis, cuando el programa inicia solo conseguimos un bucle infinito.
El for ni siquiera funciona, porque usas la variable n como límite, la cuál no tiene ningún valor inicial, por lo que toma valor 0 así que el for no llega a activarse.

Pero es que de todos modos no necesitas ningún bucle for.

Solo necesitas un bucle do while, que es el que se te repite infinitamente por la condición que has puesto:
Código: [Seleccionar]
while (opcion[0])
¿Qué significa esa condición?
Las condiciones de los bucles han de ser expresiones que den como resultado true o false.
opcion[0] es simplemente el primer char que contiene el array llamado opcion,

Eso no te sirve como condición del while. Y no necesitas ningún array de char para nada.


Fíjate en la condición que puse yo en mi bucle while:
Código: [Seleccionar]
while (calificacion != 0);
"Mientras calificación sea distinto de 0".

¿Por qué esa condición?
Porque eso es lo que pide el enunciado:
Citar
programa que lea calificaciones del 5 al 10, hasta que se le capture un 0
Es decir, leemos calificaciones (en una variable llamada calificación) y MIENTRAS lo leído sea distinto de 0, seguiremos pidiendo calificaciones.

Tampoco necesitas ninguna función para calcular el promedio.
Quizás me equivoco, pero parece que hayas encontrado el código de esa función por algún sitio y lo has copiado con la esperanza de que te sirva.

No uses nunca código que no entiendas. Porque viendo el código de esa función es evidente que no es aplicable para este ejercicio.

Mira, añado al código que puse las líneas necesarias para calcular el promedio.
¡¡Solo he necesitado añadir 3 líneas!! (Las resalto en color azul)

Citar
#include <iostream>

using namespace std;

int main()
{
    int calificacion = 0; //Para leer calificaciones por teclado
    int contador = 0; //Para contar las calificaciones introducidas
    int suma = 0; //Aquí sumamos calificaciones para luego calcular promedio.

    do {
        cout << "\nIntroduzca calificación: ";
        cin >> calificacion;

        if (calificacion != 0) {
            if (calificacion >= 5 && calificacion <= 10) {
                //Calificación es válida
                contador++; //Contamos la calificación introducida
                suma = suma + calificacion; //Sumamos la calificación
            }
            else
                cout << "Calificación no es válida, solo valores entre 5 y 10\n";
        }
        else //Ha introducido 0, fin del bucle
            cout << "\nFin de lectura de calificaciones\n";
    } while (calificacion != 0);

    //Resultados
    cout << "\nCalificaciones introducidas: " << contador;
    cout << "\nNota promedio: " << (suma/(float)contador);

    cout << "\n\t\tFIN DE PROGRAMA";
}

Puedes verlo y ejecutarlo desde este enlace:
https://www.online-ide.com/9nyPO5lxRG

Fíjate que no ha sido necesario usar ningún array (por ahora), ni ninguna función.

Analiza el código que he compartido. Asegúrate de que entiendes cada línea y que puedes seguir el flujo que sigue el programa.
Te dejo un diagrama de flujo (no se me dan muy bien) para que se entienda mejor la lógica del programa.

Cuando tengas todo claro, podemos pasar a ver como informar en la salida de resultados cuál ha sido la calificación MAYOR introducida.


544
Mira, por si te sirve para arrancar.

Dejo un código que pide calificaciones hasta que se introduzca 0.
Si está entre 5 y 10, es válida y la cuenta.
Si no, informa de que no es valida y no se cuenta.

Al terminar el bucle, informa de cuántas calificaciones válidas se han introducido.

Desde este enlace puedes probar el código online, e incluso modificarlo:
https://www.online-ide.com/5vThQyewI2


Código: [Seleccionar]
#include <iostream>

using namespace std;

int main()
{
    int calificacion = 0;
    int contador = 0;

    do {
        cout << "\nIntroduzca calificación: ";
        cin >> calificacion;

        if (calificacion != 0) {
            if (calificacion >= 5 && calificacion <= 10) {
                //Calificación es válida
                contador++; //Contamos la calificación introducida
            }
            else
                cout << "Calificación no es válida, solo valores entre 5 y 10\n";
        }
        else //Ha introducido 0, fin del bucle
            cout << "\nFin de lectura de calificaciones\n";
    } while (calificacion != 0);

    //Resultados
    cout << "\nCalificaciones introducidas: " << contador;

    cout << "\n\t\tFIN DE PROGRAMA";
}


Paso siguiente sería añadir una variable "suma" e ir acumulando la suma de todas las calificaciones válidas.
Luego al final del programa, se podría calcular el promedio dividiendo la suma entre contador.

545
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.

546
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.

547
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.

548
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");
   }
}

549
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.

550
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

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

552
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





553
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.

554
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.


555
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.

556
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

557
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

558
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.

559
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

560
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.

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".