Autor Tema: Java juego dibujar rectangulos unidos matriz dados tablero turnos dimensiones  (Leído 5627 veces)

bestias93

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 3
    • Ver Perfil
Hola a todos. Necesito modelar un tad RG que me pide lo siguiente :

Un RG se inicializa con un tablero de dimensiones preestablecidas (N y M). Las posiciones en el tablero serán los lugares donde los jugadores pondrán sus rectángulos por turnos hasta que se acabe el espacio disponible para uno nuevo, regidos con las siguientes reglas1:

a) Cada jugador juega alternadamente en su turno. Cuando le toca su turno, el jugador arroja dos dados y deberá, de ser posible, dibujar un rectángulo en el tablero, tal que:
a.1) La altura (A) del rectángulo estará determinada por el dado1
a.2) El ancho (L) del rectángulo estará determinado por el dado2

b) El rectángulo de dimensiones A x L se ubicará en el tablero respetando las reglas c y d. Si es posible dibujarlo, incrementará el área ocupada por el jugador.

c) El rectángulo entra en el tablero si tiene espacio disponible, de manera que no se solape con otros rectángulos.

d) El rectángulo que se agrega al tablero debe estar pegado por lo menos por espacio con otro rectángulo del mismo jugador. Excepto el 1er rectángulo de cada jugador, que no puede cumplir esta regla.

e) Si un jugador no puede dibujar su rectángulo, debe pasar el turno al otro jugador.

f) Si durante dos turnos consecutivos no se puede dibujar, termina el juego, inclusive si queda espacio libre en el tablero.

g) El jugador que tiene el turno, puede elegir eliminar un rectángulo del otro jugador, en lugar de agregar uno nuevo. En ese caso se eliminará un rectángulo al azar del mismo.

El jugador que ocupa más área al final es el ganador.

El juego es automático, no se va a realizar una interfaz de usuario, se simulan las jugadas de cada jugador (¡esto no significa que se deba diseñar un TAD jugador solo por este requerimiento!)

Ademas, debe usarse al menos una vez: - StringBuilder, cuyo uso debe basarse en la necesidad de modificar su capacidad y  Iteradores, para recorrer las colecciones de Java y para alguna de las clases de usuario que implemente.

Intentamos hacerlo con una matriz pero estamos atascados en la parte de ubicar un nuevo rectángulo que este pegado a otro a continuación les dejo las clases que implementamos y las salidas del programa .

las salidas son las siguientes :

| 1 || 1 || 1 |  este es el ultimo rectángulo del j1
| 1 || 1 || 1 |
| 1 || 1 || 1 |

Estado del Juego:
| 1 || 1 || 1 || 1 || 1 || 6 || 6 || 6 || 0 || 0 |  aquí pusimos seis porque en el método de   
| 1 || 1 || 1 || 1 || 1 || 6 || 6 || 6 || 0 || 0 |  ubicar nuevo rec1 al querer dibujarlo con 1
| 1 || 1 || 1 || 1 || 1 || 6 || 6 || 6 || 0 || 0 |  no no deja pero con otro valor si jejeje.
| 1 || 1 || 1 || 1 || 1 || 0 || 0 || 0 || 0 || 0 |
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0 |
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0 |
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 2 || 2 || 2 |
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 2 || 2 || 2 |
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 2 || 2 || 2 |
| 0 || 0 || 0 || 0 || 0 || 0 || 0 || 2 || 2 || 2 |


Clase Gr

Código: [Seleccionar]
public class Gr {
    Tablero tablero;
    Rectangulo ultRec;
    Dados d;

    public Gr (int x, int y){
        this.tablero= new Tablero (x,y);

    }
   
    public void jugar(){
        this.d=new Dados();
        Rectangulo j1=new Rectangulo (d.aleatoriX(),d.aleatoriY());
        j1.rec1();
        tablero.ubicar1erRec1(j1);
        //ultRec=j1;

        Rectangulo j2=new Rectangulo (d.aleatoriX(),d.aleatoriY());
        j2.rec2();
        tablero.ubicar2doRec2(j2);

        Rectangulo j3=new Rectangulo (d.aleatoriX(),d.aleatoriY());
        j3.rec1();
        tablero.ubicarNuevoRec1(j3);
        ultRec=j3;
        ;
    }

    public void eliminarRect(){     }
   
    public Rectangulo ultimoRectangulo(){
        return this.ultRec;
    }

    public String toString(){
        return this.tablero.toString();
    }

    public static void main(String[] args) {
        Gr g=new Gr (10,10);
        g.jugar();
        System.out.println(g.ultimoRectangulo());
        System.out.println(g.toString());
    }
}


Clase Dados

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

public class Dados {
int x;
int y;

public int aleatoriX(){
Random aleatorio = new Random();
int n= aleatorio.nextInt(5)+1;
this.x=n;
return this.x;

}
public int aleatoriY(){
Random aleatorio = new Random();
int n= aleatorio.nextInt(5)+1;
this.y=n;
return this.y;
}
}


Clase Rectangulo

Código: [Seleccionar]
public class Rectangulo {
    int [][]rec;
    int x;
    int y;

    public Rectangulo(int x,int y){
        this.rec=new int [x][y];
        this.x=x;
        this.y=y;

    }
    public void rec1(){
        for (int i = 0; i < rec.length; i++) {
            for (int j = 0; j < rec[i].length; j++) {

                this.rec[i][j]=1;
            }
        }
    }

    public void rec2(){
        for (int i = 0; i < rec.length; i++) {
            for (int j = 0; j < rec[i].length; j++) {

                rec[i][j]=2;
            }
        }
    }

    public String toString(){
        String salida="";
        for (int f=0;f<this.rec.length;f++){
            for (int c=0; c<this.rec[f].length;c++){
                salida+="| "+rec[f][c]+" |";
            }
            salida+="\n";
        }
        return salida;
    }
}


Clase Tablero

Código: [Seleccionar]
public class Tablero {
    int [][] tab;

    public Tablero(int x,int y){
        this.tab=new int [x][y];
        /*for (int i = 0; i < tab.length; i++) {
        for (int j = 0; j < tab[i].length; j++) {

        tab[i][j]=0;
        }
        }
         */
    }

    public void ubicar1erRec1(Rectangulo rec){
        for (int i =0; i < rec.rec.length; i++) {
            for (int j =0; j < rec.rec[i].length; j++) {

                tab[i][j]=rec.rec[i][j];
            }
        }}

    public void ubicar2doRec2(Rectangulo rec){

        for (int  x=0; x < rec.rec.length; x++) {
            for (int z=0; z < rec.rec[x].length; z++)
                for (int i =tab.length-1; i >=tab.length-rec.rec.length; i--) {
                    for (int j =tab[i].length-1; j >=tab[i].length-rec.rec[x].length ; j--){
                        {
                            tab[i][j]=rec.rec[x][z];}
                    }
                }
        }

    }
    public void ubicarNuevoRec1(Rectangulo rec){
        for (int i = 0; i < tab.length; i++) {
            for (int j = 0; j < tab[i].length; j++){
                if(this.tab[i][j]==1 && tab[i][j+1]==0){
                    for (int x =i; x < rec.x; x++){
                        for (int y = j+1; y < j+1+rec.y; y++){
                            tab[x][y]=5;}
                    }
                }
            }
        }



    }

    public void ubicarNuevoRec2(Rectangulo rec){
        for (int i = 0; i < tab.length; i++) {
            for (int j = 0; j < tab[i].length; j++){
                if(this.tab[i][j]==2 && tab[i][j-1]==0){
                    for (int x =i+(tab.length-rec.x); x < tab.length-1; x++){
                        for (int y = j-rec.y; y < y-1+rec.y; y++){
                            tab[x][y]=8;}
                    }
                }
            }
        }

    }

    public String toString(){
        String salida="Estado del Juego: \n";
        for (int f=0;f<this.tab.length;f++){
            for (int c=0; c<this.tab[f].length;c++){
                salida+="| "+tab[f][c]+" |";
            }
            salida+="\n";
        }
        return salida;
    }

    public static void main(String[] args) {
        Tablero tab;
        tab=new Tablero(5,5);
        System.out.println(tab.toString());
    }
}
« Última modificación: 06 de Junio 2020, 14:33 por Ogramar »

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 988
    • Ver Perfil
Hola.
Estoy echándole un vistazo, y la verdad, es más difícil de lo que parece.
Cuando empiezas a tener en cuenta todas las posibles situaciones que pueden darse y si se quiere cumplir correctamente con las reglas de inserción de rectangulos...., te das cuenta de que esto no se va a resolver simplemente recorriendo la matriz en busca de posiciones con 0 donde intentar meter el rectángulo.

Creo que habrá que buscar otro enfoque, a ver si se me ocurre algo.
Un saludo.
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

bestias93

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 3
    • Ver Perfil
hola muchísimas gracias por fijarse , si hay otra forma de hacerlo le estaría muy agradecido

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 988
    • Ver Perfil
Además las reglas no me quedan del todo claro..

Supongamos que el jugador 1 es el color azul y el jugador 2 es el verde.

¿La colocación de los rectangulos tal que un rectángulo azul está en la parte superior izquierda y un rectángulo verde está a su derecha unido pero no en todo su lateral y luego otro rectángulo azul debajo unido al verde pero tocando también el lateral del azul sería correcto?
¿Basta que sean contiguos por solamente una pequeña parte de un lado?

« Última modificación: 06 de Junio 2020, 14:36 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

bestias93

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 3
    • Ver Perfil
Tendría que quedar algo así:

Ejemplo para un tablero de 11x10. El jugador uno (azul) tira los dados y sale (2,3) por lo que coloca su rectángulo en algún lugar ocupando seis celdas. El jugador dos (rojo) tira los dados y sale (4,4) por lo que coloca su rectángulo en otro lugar, sin tocar al azul, ocupando 16 celdas. El jugador uno tira los dados y sale (3,1) por lo que pone un rectángulo que toca al menos en parte a su anterior rectángulo y ocupa tres celdas en vertical. El jugador 2 tira los dados y sale (2,4) por lo que pone un rectángulo de 2 de alto y 4 de ancho que toca al menos en parte a su anterior rectángulo.

« Última modificación: 06 de Junio 2020, 14:46 por Ogramar »

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 988
    • Ver Perfil
Hola.

Solo comentar que sigo con este ejercicio, porque la verdad me parece divertido.

Creo que he dado con una solución y hoy que tengo algo de tiempo libre intentaré desarrollarla.
No se si es quizás demasiado compleja (soy propenso a dar con soluciones más difíciles de lo necesario) pero es la única que se me ha ocurrido.

Básicamente consiste en dotar a la clase Rectángulo unos métodos para que sea capaz de proporcionarnos todas las posibles posiciones adyacentes a él donde podríamos intentar colocar otro Rectángulo.

Así cuando el Jugador 1 quiera insertar un nuevo Rectángulo, lo que hacemos es mirar cuáles otros Rectángulos tiene ya en el tablero y a cada uno pedirle sus posiciones adyacentes.
Que nos digan que posiciones tienen a su derecha, a su izquierda, por debajo y por arriba. Y con ellas ir probando a insertar el nuevo Rectángulo hasta encontrar un sitio donde encaje correctamente.

Luego intentaré explicarlo mejor, quizás con algún dibujo, porque no es fácil je je.

Saludos.
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: 988
    • Ver Perfil
Bueeeno.
Pues ya lo tengo.

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

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

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

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

public class Dados {

private int dado1;
private int dado2;

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

public int getDado1() {
return dado1;
}

public int getDado2() {
return dado2;
}

}

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

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

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

Código: [Seleccionar]
public class Rectangulo {

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


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

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

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

public int getAltura() {
return altura;
}


public int getAncho() {
return ancho;
}


public int getJugador() {
return jugador;
}

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

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

}

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

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

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

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

Así todo queda guay de forma sencilla.

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

Pego aquí la clase y sigo en otro post:

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

public class Tablero {

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

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

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

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

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

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

}

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

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

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

}
return false;
}

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

}
return false;
}

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

}
return false;
}

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

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

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

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

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

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

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

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

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

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

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

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

}
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: 988
    • Ver Perfil
Quiero explicar de forma pormenorizada los métodos de la clase Tablero.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


Por último vemos el método indicarGanador()

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

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

Falta por ver la última clase, la que tiene el main.
La vemos en el siguiente post
« Última modificación: 20 de Mayo 2019, 01:47 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: 988
    • Ver Perfil
Clase GR

Es la clase que pone en marcha el juego.

Tiene 4 atributos/variables globales:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

public class Gr {

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

public static void main(String[] args) {

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

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

if (!decidirEliminarRectangulo(jugador)) {

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

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

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

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

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

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

}


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

Un saludo.


« Última modificación: 06 de Junio 2020, 14:56 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: 988
    • Ver Perfil
Hola de nuevo.

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

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

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

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

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

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

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

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

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

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

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

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

Un saludo
« Última modificación: 06 de Junio 2020, 14:56 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".