Vida
Vida es un programa en Java (Eclipse neon.3) que permite jugar al juego de la vida de John H. Conway.
Descargar código fuente
Descargar Vida.jar ejecutable
Referencia del Juego de la Vida en Wikipedia
Esquema.java
package vida;
/**
* Representa un patrón con su posición en la matriz
* @author Horacio Bono
*/
public class Esquema {
public final int fil;
public final int col;
public final Patron patron;
public Esquema(int fil, int col, Patron patron) {
this.fil = fil;
this.col = col;
this.patron = patron;
}
public boolean mismaPosicion(Esquema otro) {
return this.fil == otro.fil && this.col == otro.col;
}
public boolean mismoPatron(Esquema otro) {
return this.patron.equalsTo(otro.patron);
}
public boolean excedido() {
return fil < 0 || col < 0 ||
fil + patron.alto > Ventana.FIL ||
col + patron.ancho > Ventana.COL;
}
}
/**
* Representa un patrón de puntos independientemente
* de su posición en la matriz. Los puntos son
* guardados como bits en un vector de bytes.
* @author Horacio Bono
*/
class Patron {
public final int alto, ancho;
private final byte[] bytes;
short mascara;
int entrada;
int salida;
public Patron(int alto, int ancho) {
this.alto = alto;
this.ancho = ancho;
int tam = (alto * ancho + 7) / 8;
bytes = new byte[tam];
for(int i = 0; i < bytes.length; i++) {
bytes[i] = 0;
}
entrada = 0;
iniciarBits();
}
public void setBit(boolean bit) {
if(entrada < bytes.length) {
if(mascara == 0) {
mascara = 0x80;
}
if(bit) {
bytes[entrada] |= mascara;
}
mascara >>= 1;
if(mascara == 0) {
entrada++;
}
}
}
public final void iniciarBits() {
mascara = 0;
salida = 0;
}
public boolean getBit() {
boolean bit;
if(salida < bytes.length) {
if(mascara == 0) {
mascara = 0x80;
}
bit = (bytes[salida] & mascara) != 0;
mascara >>= 1;
if(mascara == 0) {
salida++;
}
} else {
iniciarBits();
bit = false;
}
return bit;
}
public boolean equalsTo(Patron otro) {
boolean igual;
igual = otro != null &&
this.alto == otro.alto &&
this.ancho == otro.ancho;
if(igual) {
int i;
for(i = bytes.length - 1;
i >=0 && this.bytes[i] == otro.bytes[i]; i--);
igual = i < 0;
}
return igual;
}
}
Descargar código fuente
Descargar Vida.jar ejecutable
Referencia del Juego de la Vida en Wikipedia
Esquema.java
package vida;
/**
* Representa un patrón con su posición en la matriz
* @author Horacio Bono
*/
public class Esquema {
public final int fil;
public final int col;
public final Patron patron;
public Esquema(int fil, int col, Patron patron) {
this.fil = fil;
this.col = col;
this.patron = patron;
}
public boolean mismaPosicion(Esquema otro) {
return this.fil == otro.fil && this.col == otro.col;
}
public boolean mismoPatron(Esquema otro) {
return this.patron.equalsTo(otro.patron);
}
public boolean excedido() {
return fil < 0 || col < 0 ||
fil + patron.alto > Ventana.FIL ||
col + patron.ancho > Ventana.COL;
}
}
/**
* Representa un patrón de puntos independientemente
* de su posición en la matriz. Los puntos son
* guardados como bits en un vector de bytes.
* @author Horacio Bono
*/
class Patron {
public final int alto, ancho;
private final byte[] bytes;
short mascara;
int entrada;
int salida;
public Patron(int alto, int ancho) {
this.alto = alto;
this.ancho = ancho;
int tam = (alto * ancho + 7) / 8;
bytes = new byte[tam];
for(int i = 0; i < bytes.length; i++) {
bytes[i] = 0;
}
entrada = 0;
iniciarBits();
}
public void setBit(boolean bit) {
if(entrada < bytes.length) {
if(mascara == 0) {
mascara = 0x80;
}
if(bit) {
bytes[entrada] |= mascara;
}
mascara >>= 1;
if(mascara == 0) {
entrada++;
}
}
}
public final void iniciarBits() {
mascara = 0;
salida = 0;
}
public boolean getBit() {
boolean bit;
if(salida < bytes.length) {
if(mascara == 0) {
mascara = 0x80;
}
bit = (bytes[salida] & mascara) != 0;
mascara >>= 1;
if(mascara == 0) {
salida++;
}
} else {
iniciarBits();
bit = false;
}
return bit;
}
public boolean equalsTo(Patron otro) {
boolean igual;
igual = otro != null &&
this.alto == otro.alto &&
this.ancho == otro.ancho;
if(igual) {
int i;
for(i = bytes.length - 1;
i >=0 && this.bytes[i] == otro.bytes[i]; i--);
igual = i < 0;
}
return igual;
}
}
Creador.java
package vida;
/**
* Clase responsable de crear un nuevo patrón a partir de uno anterior
* @author Horacio Bono
*/
public class Creador {
private boolean modoEdicion;
private Punto primero;
private Esquema esquema;
public Creador() {
primero = null;
esquema = null;
modoEdicion = false;
}
public Esquema getEsquema() {
return esquema;
}
public void setModoEdicion(boolean modo) {
if(modoEdicion != modo) {
modoEdicion = modo;
if(modoEdicion) {
primero = null;
} else {
crearPatron();
}
}
}
/**
* Agrega ordenadamente a la lista un punto
* de coordenadas fil, col o lo elimina si ya existe.
* Retorna true si lo agregó o false si lo eliminó;
* Este método se ejecuta privadamente al crear una nueva
* generación a partir de la actual o bien públicamente
* para crear manualmente un patrón de puntos.
*/
public boolean definirPunto(int fil, int col) {
boolean ret;
if(primero == null || primero.compareTo(fil, col) > 0) {
primero = new Punto(fil, col, primero);
ret = true;
} else if(primero.compareTo(fil, col) < 0) {
Punto p = primero;
while(p.sig != null && p.sig.compareTo(fil, col) < 0) {
p = p.sig;
}
if(p.sig == null || p.sig.compareTo(fil, col) > 0) {
p.sig = new Punto(fil, col, p.sig);
ret = true;
} else {
p.sig = p.sig.sig;
ret = false;
}
} else {
primero = primero.sig;
ret = false;
}
return ret;
}
/**
* Crea un patrón de puntos a partir de la lista de puntos.
*/
private void crearPatron() {
if(primero == null) {
// Si no hay puntos genera un patrón nulo
esquema = null;
} else {
// Si hay puntos busca los límites superior, inferior,
// izquierdo y derecho
int arr = primero.fil;
int izq = primero.col;
int aba = arr;
int der = izq;
int alto, ancho;
// Recorre la lista buscando coordenadas máximas y mínimas
Punto p = primero.sig;
while(p != null) {
if(p.col < izq) {
izq = p.col;
} else if(p.col > der) {
der = p.col;
}
if(p.fil > aba) {
aba = p.fil;
}
p = p.sig;
}
// Calcula las dimensiones del patrón según las
// coordenadas extremas halladas en la lista de puntos
// y crea un nuevo patrón
alto = aba - arr + 1;
ancho = der - izq + 1;
Patron patron = new Patron(alto, ancho);
// Recorre toda la lista
p = primero;
for(int f = arr, i = 0; i < alto; i++, f++) {
for(int c = izq, j = 0; j < ancho; j++, c++) {
// Si hay un punto en esa posición
if(p != null && p.compareTo(f, c) == 0) {
patron.setBit(true); // pone el bit en 1
p = p.sig; // avanza al siguiente punto
} else {
patron.setBit(false); // si no, lo pone en 0
}
}
}
// Finalmente crea el nuevo esquema
esquema = new Esquema(arr, izq, patron);
}
}
/**
* Genera un nuevo patrón a partir del actual copiándolo primero
* a una matriz temporaria para luego analizarla e ir generando
* una lista de coordenadas de puntos allí donde debe haber un
* punto en el nuevo patrón. Finalmente crea el nuevo patrón a
* partir de esa lista de puntos.
*/
public void nuevaGeneracion() {
if(modoEdicion || esquema == null) return;
int fil = esquema.fil;
int col = esquema.col;
Patron patron = esquema.patron;
// Toma las dimensiones del patrón actual
int alto = patron.alto;
int ancho = patron.ancho;
// Crea una matriz temporaria ampliada en dos celdas por
// lado para trasladar el patrón actual y generar la lista
// de puntos del nuevo patrón.
boolean[][] matriz = new boolean[alto + 4][ancho + 4];
patron.iniciarBits();
for(int f = 2, i = 0; i < alto; i++, f++) {
for(int c = 2, j = 0; j < ancho; j++, c++) {
matriz[f][c] = patron.getBit();
}
}
// Corrige la posición relativa del extremo superior izquierdo
// del patrón, amplía el sector de búsqueda en una celda por
// lado e inicia una nueva lista de puntos
fil -= 2;
col -= 2;
alto += 2;
ancho += 2;
primero = null;
// Recorre las posiciones de la matriz exceptuando las
// de los bordes para evitar desbordamiento
for(int f = 1, i = 0; i < alto; i++, f++) {
for(int c = 1, j = 0; j < ancho; j++, c++) {
// De cada celda cuenta los vecinos
int n = contarVecinos(matriz, f, c);
// Si corresponde que haya un punto lo pone en la lista
// teniendo en cuenta la posición inicial
if(n == 3 ||(matriz[f][c] && n == 2)) {
definirPunto(fil + f, col + c);
}
}
}
// Con los puntos reunidos crea un nuevo patrón
crearPatron();
}
private int contarVecinos(boolean[][] matriz, int fil, int col) {
int arr = fil - 1;
int izq = col - 1;
int aba = fil + 1;
int der = col + 1;
int cont = 0;
if(matriz[arr][izq]) cont++;
if(matriz[arr][col]) cont++;
if(matriz[arr][der]) cont++;
if(matriz[fil][izq]) cont++;
if(matriz[fil][der]) cont++;
if(matriz[aba][izq]) cont++;
if(matriz[aba][col]) cont++;
if(matriz[aba][der]) cont++;
return cont;
}
}
/**
* La clase Punto es un nodo de lista enlazada ordenada
* por fil, col que permite almacenar las coordenadas
* de los puntos vivos de un patrón.
*/
class Punto {
public final int fil;
public final int col;
public Punto sig;
public Punto( int fil, int col, Punto sig) {
this.fil = fil;
this.col = col;
this.sig = sig;
}
public int compareTo(int fil, int col) {
int ret = this.fil - fil;
if(ret == 0) {
ret = this.col - col;
}
return ret;
}
}
Ventana.java
package vida;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
/**
* Ventana del juego
* @author Horacio Bono
*/
public class Ventana extends JFrame {
public static final int FIL = 50;
public static final int COL = 50;
private final Celda[][] grilla = new Celda[FIL][COL];
private final Creador creador = new Creador();
private final ArrayList<Esquema> esquemas = new ArrayList<>();
private int generacion;
private boolean enEdicion;
private boolean enPausa;
private boolean grillaVacia;
private boolean patronActivo;
private boolean patronRepetido;
private JButton btnDibujar;
private JButton btnIniciar;
private JButton btnPausar;
private JButton btnLimpiar;
private JLabel lblGeneraciones;
private Timer timer;
public Ventana() {
super("Juego de la vida");
crearPantalla();
crearEventos();
limpiarGrilla();
enEdicion = enPausa = false;
}
private void crearPantalla() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().setLayout(new GridBagLayout());
Dimension dim = new Dimension(90, 30);
btnDibujar = new JButton("Dibujar");
btnDibujar.setPreferredSize(dim);
btnDibujar.setEnabled(true);
btnLimpiar = new JButton("Limpiar");
btnLimpiar.setPreferredSize(dim);
btnLimpiar.setEnabled(false);
btnPausar = new JButton("Pausar");
btnPausar.setPreferredSize(dim);
btnPausar.setEnabled(false);
btnIniciar = new JButton("Iniciar");
btnIniciar.setPreferredSize(dim);
btnIniciar.setEnabled(false);
lblGeneraciones = new JLabel("0", JLabel.CENTER);
lblGeneraciones.setPreferredSize(dim);
lblGeneraciones.setOpaque(true);
lblGeneraciones.setBackground(Color.lightGray);
JLabel vidas = new JLabel("Generaciones", JLabel.CENTER);
vidas.setPreferredSize(dim);
vidas.setOpaque(true);
vidas.setBackground(Color.lightGray);
JPanel der = new JPanel();
der.setBackground(Color.lightGray);
der.setLayout(new GridLayout(FIL, COL));
for(int i = 0; i < FIL; i++)
for(int j = 0; j < COL; j++) {
Celda celda = new Celda(i, j);
der.add(celda);
grilla[i][j] = celda;
}
GridBagConstraints constraints = new GridBagConstraints();
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weighty = 1.0;
constraints.anchor = GridBagConstraints.CENTER;
this.getContentPane().add (btnDibujar, constraints);
constraints.gridx = 0;
constraints.gridy = 1;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weighty = 1.0;
constraints.anchor = GridBagConstraints.CENTER;
this.getContentPane().add (btnIniciar, constraints);
constraints.gridx = 0;
constraints.gridy = 2;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weighty = 1.0;
constraints.anchor = GridBagConstraints.CENTER;
this.getContentPane().add (btnPausar, constraints);
constraints.gridx = 0;
constraints.gridy = 3;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weighty = 1.0;
constraints.anchor = GridBagConstraints.CENTER;
this.getContentPane().add (btnLimpiar, constraints);
constraints.gridx = 0;
constraints.gridy = 4;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weighty = 1.5;
constraints.anchor = GridBagConstraints.SOUTH;
this.getContentPane().add (vidas, constraints);
constraints.gridx = 0;
constraints.gridy = 5;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weighty = 0.5;
constraints.anchor = GridBagConstraints.NORTH;
this.getContentPane().add (lblGeneraciones, constraints);
constraints.gridx = 1;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 6;
constraints.fill = GridBagConstraints.BOTH;
constraints.weightx = 1.0;
constraints.weighty = 1.0;
this.getContentPane().add (der, constraints);
}
private void crearEventos() {
ActionListener clicBoton = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == btnLimpiar) {
btnIniciar.setEnabled(false);
btnDibujar.setEnabled(true);
btnPausar.setEnabled(false);
btnLimpiar.setEnabled(false);
limpiarGrilla();
} else if(source == btnIniciar) {
btnIniciar.setEnabled(false);
btnDibujar.setEnabled(false);
btnPausar.setEnabled(true);
btnPausar.setText("Pausar");
timer.start();
} else if(source == btnPausar) {
enPausa = !enPausa;
if(enPausa) {
btnPausar.setText("Reanudar");
timer.stop();
} else {
btnPausar.setText("Pausar");
timer.start();
}
} else if (source == btnDibujar) {
enEdicion = !enEdicion;
creador.setModoEdicion(enEdicion);
Color color;
if(enEdicion) {
btnDibujar.setText("Fin dibujo");
color = Color.blue;
limpiarGrilla();
} else {
btnDibujar.setText("Dibujar");
color = Color.lightGray;
procesarPatron();
}
btnDibujar.setEnabled(grillaVacia);
btnIniciar.setEnabled(!grillaVacia);
btnLimpiar.setEnabled(!grillaVacia);
for(int i = 0; i < FIL; i++) {
for(int j = 0; j < COL; j++) {
grilla[i][j].setBorder(BorderFactory.createLineBorder(color));
}
}
}
}
};
MouseListener clicCelda = new MouseListener() {
@Override
public void mousePressed(MouseEvent e) {
if(enEdicion) {
Celda celda = (Celda)e.getSource();
celda.cambiarEstado();
creador.definirPunto(celda.fil, celda.col);
}
}
@Override
public void mouseExited(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseReleased(MouseEvent e) {}
@Override
public void mouseClicked(MouseEvent e) {}
};
ActionListener nuevaGeneracion = new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
creador.nuevaGeneracion();
procesarPatron();
patronActivo = timer.isRunning();
btnLimpiar.setEnabled(patronActivo);
btnDibujar.setEnabled(!patronActivo);
btnPausar.setEnabled(patronActivo);
}
};
btnDibujar.addActionListener(clicBoton);
btnIniciar.addActionListener(clicBoton);
btnPausar.addActionListener(clicBoton);
btnLimpiar.addActionListener(clicBoton);
timer = new Timer(1000, nuevaGeneracion);
for(int i = 0; i < FIL; i++) {
for(int j = 0; j < COL; j++) {
grilla[i][j].addMouseListener(clicCelda);
}
}
}
private void procesarPatron() {
String estado = "";
Esquema esquema = creador.getEsquema();
grillaVacia = esquema == null;
if(grillaVacia) {
timer.stop();
estado = "Vacío";
} else {
int i;
// Busca hacia atrás un patrón igual en la lista
// de esquemas anteriores
for(i = esquemas.size()-1;
i >= 0 && !esquemas.get(i).mismoPatron(esquema); i--);
patronRepetido = i >= 0;
// Si encuentra uno igual
if(patronRepetido) {
// El patrón está realmente repetido si tiene además
// la misma posición en la grilla
patronRepetido = esquemas.get(i).mismaPosicion(esquema);
if(patronRepetido) {
timer.stop();
estado = "Repetido";
} else {
estado = "Desplazado";
}
}
}
// Agrega el nuevo esquema a la lista
esquemas.add(esquema);
if(esquema.excedido()) {
timer.stop();
estado = "Excedido";
} else {
if(generacion > 0) {
// Borra el último patrón si existe
dibujarPatron(esquemas.get(generacion-1));
dibujarPatron(esquema);
}
}
lblGeneraciones.setText(generacion++ + " " + estado);
}
private void limpiarGrilla() {
timer.stop();
esquemas.clear();
grillaVacia = true;
generacion = 0;
lblGeneraciones.setText("0");
for (int i = 0; i < FIL; i++) {
for (int j = 0; j < COL; j++) {
grilla[i][j].setEstado(false);
}
}
}
private void dibujarPatron(Esquema esquema) {
if(esquema == null) return;
Patron p = esquema.patron;
int h = p.alto;
int w = p.ancho;
p.iniciarBits();
for(int f = esquema.fil, i = 0; i < h; i++, f++) {
for(int c = esquema.col, j = 0; j < w; j++, c++) {
if(p.getBit()) {
grilla[f][c].cambiarEstado();
}
}
}
}
}
/**
* Representa una celda de la matriz
* @author Horacio Bono
*/
class Celda extends JLabel{
public final int fil;
public final int col;
private boolean estado;
public Celda(int fil, int col) {
super();
this.fil = fil;
this.col = col;
setOpaque(true);
setBorder(BorderFactory.createLineBorder(Color.lightGray));
setEstado(false);
}
public void cambiarEstado() {
setEstado(!estado);
}
public final void setEstado(boolean estado) {
this.estado = estado;
setBackground(estado? Color.black: Color.white);
}
}
Vida.java
package vida;
/**
*
* @author Horacio Bono
*/
public class Vida {
public static void main(String[] args) {
Ventana v = new Ventana();
v.setBounds(200,100,700,600);
v.setVisible(true);
}
}

Comentarios
Publicar un comentario