Autor Tema: Juego de tablero código Java Ghosts fantasmas turno matriz arreglo bidimensional  (Leído 1257 veces)

MRWHITE

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 2
    • Ver Perfil
Hola buenas noches, queria saber si alguien me podía ayudar a realizar un juego de GHOST (FANTASMAS) que tengo como proyecto y es que la verdad no sé cómo hacerlo. El proyecto pide lo siguiente:

Ghosts es un juego detablero para 2 jugadores.

El juego consiste en una matriz (arreglo bidimensional) de 6 x 6 que representan un castillo embrujado.Cada jugador tiene 8 fantasmas, 4 buenos y 4 malos.
Los mismos se ponen en las primeras 2 filas del lado del lugador, dejando libres las esquinas (como muestra la imagen) ya que representan la salida del castillo. El jugador contrario NO SABRÁ cual es el fantasma bueno o malo de su oponente hasta que se lo coma (Si se fijan en la imagen, los puntos verdes son los buenos y los rojos los malos, PERO, los puntos estan de espalda, así que el contrario no sabe que son). Estos pueden ser colocados por el jugador en el orden en que mas gusten.

El juego se va desarrollando en turnos y cada jugador puede mover un fantasma solo una casilla ya sea horizontal o vertical. Pueden comer otro fantasma si se mueven a la casilla donde está.

Como se gana?Puede suceder en los siguientes escenarios, lo que suceda primero:

1. Aquel  que SE  HA  COMIDOo  capturado todos  los fantasmas  buenos de  su oponente.

2. Aquel al cual LE HAN COMIDO todos susfantasmas malos.

3. Si se logra llevar un fantasma bueno por la salida del castillo del lado contrario.
El programa inicia mostrando el siguiente menú: Menú de Inicio

1 -Login. Se  pide  del  teclado  el  username  y  el  password,  si  este  se  encuentra  dentro  dela colección de players, se carga el MENÚ PRINCIPAL, sino, se muestra en pantalla el error y se vuelve a desplegar este MENÚ DE lNICIO.

2- Crear Player. Se permite con esta opción crear un nuevo player. Se pide que se ingrese los datos de Username (Se validaque sea UNICO) y password. El player tendra tambien un atributo de puntos que inicialmente se le asignara 0. Si todo se hace bien se crea el player para guardarlo en la colección. Al momento de crearlo bien se va al MENÚ PRINCIPAL, sino, se informa el problema y se carga el MENÚ DE lNICIO.

 3- Salir. La aplicación se cierra

Menú Principal

El programa debe contener un menú que contiene lo siguiente:

1-Jugar Ghosts

2-Configuracióna.Dificultad b.Modo de Juego c.Regresar al Menú Principal

3-Reportes
a. Descripción de mis ultimos 10 juegos.
b.Ranking de Jugadoresc.
Regresar al Menú principal

4-Mi Perfil
a.Ver mis Datos 
b.Cambiar Password 
c.Eliminar mi cuenta 
d.Regresar al Menú Principal

5-Salir El jugador debe seleccionar una opción para proceder.

1-JUGARGHOSTS Primeramente se pide que se ingrese el username del jugador 2, este debe existir y no es necesario pedir su password. El jugador 1 es el usuario que esta LOGGED IN.

Luego viene el proceso de colocación de fantasmas dependiente del modo de juego y la dificultad (Vease dicha sección).En la siguiente figura se muestra que al momento de jugar, no se mira cual es el bueno y cual es el malo, pero internamente se debe saber. Tablero Propuesto:
   1  2  3  4  5  6
 6  _   F2  F2  F2  F2  _
 5  _  F2  F2  F2  F2  _ 
 4  _   _    _    _    _   _
 3  _   _    _    _    _   _
 2  _  F1  F1  F1  F1   _
 1  _  F1  F1  F1  F1   _ 

Los jugadores comenzando desde el jugador 1 se estarán turnando moviendo fantasmas, para ello se pide primerola coordenada de selección, ingresandola fila y la columna del teclado del fantasma que quiere mover (Se valida que sea su propio fantasma y que la coordenada este sobre el rango,  si no cumple, se pide que ingrese otra coordenada).

Luego se ingresa la coordenada de movimiento, del teclado se ingresa la fila y la columna donde se quiere mover. Esta coordenada seleccionada debe cumplir con los requisitos de movimientos, es decir, solo 1 casilla y dicha casilla destino NO puede estar ocupada por un fantasma propio.

Si no se cumple los requisitos se muestran 2 opciones:

1.Ingresar Otra Coordenada de movimiento.

2.Ingresar Otra Coordenada de Selección. Para poder escoger otro fantasma.

Si es movimiento válido y la casilla esta ocupada por un fantasma contrario, se come y se imprime en pantalla:“ TE HAS COMIDO UN FANTASMA (BUENO/MALO) DE (Nombre Jugador contrario! “Ademásen todo momento se muestra en pantalla la cantidad de fantasmas buenos y malos que tengan disponiblecada jugador. Y tienes que idear la manera de saber si el fantasma es bueno o malo (Ya con las clases esto puede ser más facil)

El tablero se va actualizando en cada turno para ir reflejando todas las actualizaciones del juego.  El juego terminara cuando alguien logre cumplir los escenarios expuestos al principio del documento.

Al finalizar el mismo se muestra un mensaje y automáticamente se mostrara de  nuevo  el MENÚ PRINCIPAL, Los  mensajes  pueden  ser  por  ejemplo  (Estos  mismos mensajes se utilizarán en el reporte de Mis últimos 10 juegos):1.“Patito triunfo sobre Pollito porque capturó todos sus fantasmas buenos!”2.“Patito triunfo porque Pollito le capturó todos sus fantasmas malos!” 3.“Patito triunfo al sacar del castillo un fantasma bueno!”El triunfador recibe 3 ptos.

RETIRO  Un jugador se puede retirar en cualquier momento si ingresa
-1 tanto en la parte de filas como de columnasen la coordenada de selección, no sin antes preguntarle se de verdad se desea salir, si el jugador confirma que si se desea salir el juego termina y el otro jugador triunfo por retiro del contario.

2. CONFIGURACION  a. DIFICULTADES en la parte de dificultad el usuario podrá determinar cuántos fantasmas por juego un jugador debe colocar.

Las opciones son (NORMAL –8fantasmas, EXPERT –4fantasmas y GENIUS –2fantasma), recordar que una mitad son buenos y la otra malos. Luego de modificar la dificultad, el programa vuelve a cargar el SUB MENU DE CONFIGURACION. Nota: Por default el juego DEBE estar en modo NORMAL..b. MODO DE JUEGOEn esta opción se selecciona como se va jugar el juego. Hay 2 opciones:1.ALEATORIO: Si esta opción se escoge, al momento de jugar, todos los fantasmas se colocan aleatoriamente en las primeras 2 filas de cada lado del jugador. En este modo, ni los propios jugadores saben cual es el bueno y cual es el malo!

2.MANUAL: Cada jugador coloca sus fantasmas en la casilla que quieran. Primero ingresaráel jugador 1 sus fantasmas y luego el jugador2.Para colocarlos, se pide que vayan ingresando la fila y la columna de donde lo quieren poner (Validar que este en las primeras 2 filas sin contar las salidas y que no este ocupado). El ingreso es intercalado, es decir, primero se coloca un fantasma bueno y luego uno malo y asi sucesivamente.Nota: Por default el juego DEBE estarenmodo ALEATORIO.


3-REPORTES   a.Descripción De mis Últimos 10JuegosAl seleccionar esta opción se debe mostrar que fue lo que paso en los últimos 10 juegosdel jugador que esta LOGGED IN(Mensajes iguales o parecidos al que se muestra cuando termina un juego). Luego de imprimir el listado el programa vuelve a mostrar el SUB MENU DE REPORTES. Un ejemplo podría ser lo siguiente:  NOTA: De primero siempre DEBE estar el registro del último juego realizado.b.Ranking de Jugadores Al seleccionar esta opción muestra ellistado total de jugadores inscritos en el juego. Se muestran TODOS sus datos.El listado imprimejugadores ordenados del jugador que tiene MAS PUNTOS al que MENOS tiene. Luego el programa vuelve  a mostrar el SUB MENU DE REPORTES  4-MI PERFIL
a.Ver Mis Datos.  Muestra TODOS los datos del jugador que esta LOGGED IN. Luego el programa vuelve a mostrar el SUB MENU DE MI PERFIL. b .Cambiar Password Me permite modificar el password del jugador que esta LOGGED IN. Luego el programa vuelve a mostrar el SUB MENU DE MIPERFIL. c.Eliminar Cuenta Elimina la cuenta del jugador LOGGED IN de la colección de jugadores. El jugador YA NO EXISTIRA MAS. Luego el programa muestra el MENU DE INICIO.


5-CERRAR SESIONCierra la sesión del jugador LOGGED IN y se vuelve a mostrar el MENU DE INICIO


De antemano quedo muy agradecido por su atención y su ayuda!

Saludos!
« Última modificación: 09 de Abril 2021, 19:56 por Ogramar »

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 725
    • Ver Perfil
Re: JUEGO DE TABLERO “GHOSTS” (FANTASMAS)
« Respuesta #1 en: 10 de Diciembre 2020, 01:17 »
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.
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 725
    • Ver Perfil
Re: JUEGO DE TABLERO “GHOSTS” (FANTASMAS)
« Respuesta #2 en: 12 de Diciembre 2020, 01:04 »
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  __  __
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 725
    • Ver Perfil
Re: JUEGO DE TABLERO “GHOSTS” (FANTASMAS)
« Respuesta #3 en: 15 de Diciembre 2020, 00:02 »
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.
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 725
    • Ver Perfil
Re: JUEGO DE TABLERO “GHOSTS” (FANTASMAS)
« Respuesta #4 en: 15 de Diciembre 2020, 12:17 »
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.
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 725
    • Ver Perfil
Re: JUEGO DE TABLERO “GHOSTS” (FANTASMAS)
« Respuesta #5 en: 19 de Diciembre 2020, 11:59 »
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.
« Última modificación: 19 de Diciembre 2020, 12:02 por Kabuto »
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 725
    • Ver Perfil
Re: JUEGO DE TABLERO “GHOSTS” (FANTASMAS)
« Respuesta #6 en: 25 de Diciembre 2020, 15:07 »
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.
« Última modificación: 09 de Abril 2021, 20:00 por Ogramar »
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 725
    • Ver Perfil
Re: JUEGO DE TABLERO “GHOSTS” (FANTASMAS)
« Respuesta #7 en: 25 de Diciembre 2020, 15:15 »
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




« Última modificación: 09 de Abril 2021, 20:08 por Ogramar »
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

 

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