Páginas

lunes, 26 de agosto de 2013

El juego de la vida

En Java y en Python + Tkinter


Es un autómata celular y fué inventado por el matemático John Conway en 1970.

Este tipo de autómatas son inventados para simular sistemas en el mundo real.

Contiene las siguientes reglas:


Para una casilla habitada
       Cada celda con 1 o 0 vecinos muere. (Soledad)
       Cada celda con 4 o más vecinos muere. (Sobrepoblación)
       Cada celda con 2 o 3 vecinos sobrevive.

Para una casilla no habitada
       Cada celda con 3 vecinos se convierte en habitada.    


El juego de la vida en Python
El siguiente código se puede descargar desde aquí:

https://dl.dropboxusercontent.com/u/41478854/TheGameOfLife.py

import random
from Tkinter import *

#Medidas de la matrix
largo = 55
ancho = 55

#Matrix
matrix = [[0 for i in xrange(ancho)] for i in xrange(largo)] #matrix principal contenedora
bufferx = [[0 for i in xrange(ancho)] for i in xrange(largo)] #matrix de intercambio 

#Campos
separacion = 6 #Separacion entre coordenadas del ovalo a dibujar como punto
constante = 10 #Constante de separacion entre los puntos
probabilidad = 6 #valor de probabilidad para la generacion random

def dibujarRandom():
    for i in range(4,ancho-4):
        for j in range(4,ancho-4):
            r = random.randint(1,10)
            if r <= probabilidad : #Si el num random <= probabilidad, la casilla se rellena
                matrix[i][j] = 1        

def linea(): #dibuja una linea en la matrix
    matrix[4][2] = 1
    matrix[4][3]=1
    matrix[4][4]=1
    matrix[4][5]=1


def recorrerMatrix():
    for i in range(largo):
        for j in range(ancho):
            identificarVecinos(i,j)
            
def identificarVecinos(fila,col):
    contador=0
    if fila==0 or col==0 or fila==ancho-1 or col == largo-1:
        return

    for i in range(fila-1,fila+2):
        for j in range(col-1,col+2):
            if (i != fila or j != col) and matrix[i][j] == 1: #Si los hay algun vecino de los 8 posibles
                    contador += 1 #contador++
                                
    aplicarRegla(contador,fila,col, matrix[fila][col]) 
            
def aplicarRegla(vecinos,x,y,celda):

    if celda==1 and (vecinos == 2 or vecinos == 3):
        bufferx[x][y] = 1 
        
    if celda == 1 and vecinos > 3:
        bufferx[x][y] = 0 
        
    if celda == 1 and (vecinos == 1 or vecinos == 0):
        bufferx[x][y]=0 
        
    if celda == 0 and vecinos==3:
        bufferx[x][y] = 1 
        

def actualizarMatrix():
    global matrix,bufferx
    matrix = bufferx
    bufferx = [[0 for i in xrange(ancho)] for i in xrange(largo)]


def dibujarPuntos(w):        
    for i in range(largo):
        margen = (i+1)*constante #Margen superior que tendra el punto eje Y
        for j in range(ancho):
            referencia = (j+1)*constante #Punto de referencia para iniciar el punto X
            #Dibujo ovalos de color azul o blanco para cada punto de la matriz
            if matrix[i][j] == 1:
                w.create_oval(referencia,margen,referencia+separacion,margen+separacion,fill="blue",outline="blue")
            else:
                w.create_oval(referencia,margen,referencia+separacion,margen+separacion,fill="white",outline="white")
        
    w.update_idletasks()#Actualiza los puntos en la matriz


def cerrarVentana():
    master.destroy()

def TheGameOfLife():  
    dibujarRandom()
    #linea()
    while 1:
        dibujarPuntos(w)
        recorrerMatrix()
        actualizarMatrix()



master = Tk() #Variable de ventana TK
w = Canvas(master, width=600, height=600) #Para dibujar cosas

def main():

    master.wm_title("The Game Of Life") 
    master.geometry("563x563+450+100")#Dimensiones + margenes
    master.protocol("WM_DELETE_WINDOW", cerrarVentana)#Listener para cerrar ventana    
    w.pack()
    TheGameOfLife()
    mainloop()#Mantener la ventana abierta

    
main()#Ejecutar la funcion Main

Por cuestiones de comodidad y falta de tiempo, NO realicé muchas validaciones e inclusive tengo un enorme BUG como el listener para cerrar la ventana debido a que pusé en un ciclo infinito el juego de la vida, también decidí establecer con valores fijos los tamaños de la matriz para no tener que redimensionar constantemente. Otra cosa que no tuve más tiempo para solucionar fué que el programa se vuelve muy lento despues de un X número de iteraciones al momento de re-dibujar en el Canvas. Es la primera vez que utilizo Tkinter y nunca había hecho la gran cosa en Python anteriormente.


Por otra parte, en lugar de enfocarme en la interacción con el usuario me enfoqué en la lógica del algoritmo y en cómo utilizar Tkinter en python para dibujar.

Básicamente lo que hice fué lo siguitente:

1) Crear la matriz principal y una matriz buffer
2) Crear un dibujo random en la matriz principal
3) Se dibujan los puntos en el Canvas según la matriz principal
4) Recorrer cada celda de la matriz principal en busca de vecinos
5) Aplicar las reglas del juego para cada celda en la matriz principal
6) La nueva matriz crearla en el buffer a medida que aplicamos la regla
7) El buffer se convierte en la matriz principal
8) Se actualiza el dibujo en el Canvas según la matriz principal

¿Cómo saber si funciona o no?

Primero lo que hice fue poner una función para dibujar una línea pequeña:
def linea(): #dibuja una linea en la matrix
    matrix[4][2] = 1
    matrix[4][3]=1
    matrix[4][4]=1
    matrix[4][5]=1
1era iteración
2da iteración

3era iteración


4ta iteración

Así comprobamos que se estan aplicando correctamente las reglas.

Dibujando una matriz random podemos ver algo similar a esto:

def dibujarRandom():
    for i in range(4,ancho-4):
        for j in range(4,ancho-4):
            r = random.randint(1,10)
            if r <= probabilidad : #Si el num random <= probabilidad, la casilla se rellena
                matrix[i][j] = 1        





El juego de la vida en Java

Con respecto a Java utilizé como salida de información la línea de comandos.

Descargar aquí:


Código
import java.util.Random;

public class GameOfLife{

    private int largo;
    private int ancho;
    private char[][] cadena;//matriz original
    private char[][] buffer;//matriz buffer

    GameOfLife(){
	largo = 10;
	ancho = 10;
	inicializar(largo,ancho);
    }

    GameOfLife(int x, int y){
	largo = y;
	ancho = x;
	inicializar(largo,ancho);
    }

    public void inicializar(int largo, int ancho){
	cadena = new char[largo][ancho];
	limpiar();
	buffer = new char[largo][ancho];
	limpiarBuffer();
    }

    public void imprimir(){
	for(int i=0; i < largo;i++){
	    for(int j=0; j < ancho; j++){
		 System.out.print(cadena[i][j]);
	    }
	    System.out.println();
	}
    }
    public void imprimirBuffer(){
	for(int i=0; i  < largo;i++){
	    for(int j=0; j < ancho; j++){
 		System.out.print(buffer[i][j]);
	    }
	    System.out.println();
	}
    }

    public void limpiar(){
	
	for(int i=0;i<largo; i++){
	    for(int j=0;j<ancho;j++){
 		cadena[i][j]='-';    
	    }
	}
	
    }//end of clean


    public void limpiarBuffer(){
	
	for(int i=0;i<largo; i++){
	    for(int j=0;j<ancho;j++){
 		buffer[i][j]='-';    
	    }
	}
	
    }//end of clean

    public void poblacionRandom(){

	Random numR = new Random();

	for(int i=4; i < largo-3; i++){
	    for(int j=4;j<ancho-3;j++){
		if( numR.nextInt(10) &lt 6 ){
		     cadena[i][j]='X';
		}
	    }
	}
    }

    public void recorrerMatriz(){
	for(int i=0;i<largo;i++){
	    for(int j=0; j<ancho;j++){
 		identificarVecinos(i,j);
		
	    }

	}
    }

    public void identificarVecinos(int fila, int col){
	 int contador=0;
 	if(fila==0 || col==0 || fila==ancho-1 || col == largo-1){
	     return;
	 }
 	if(cadena[fila-1][col-1]=='X') contador++;
 	if(cadena[fila-1][col]=='X') contador++;
 	if(cadena[fila-1][col+1]=='X') contador++;
 	if(cadena[fila][col-1]=='X') contador++;
 	if(cadena[fila][col+1]=='X') contador++;
 	if(cadena[fila+1][col-1]=='X') contador++;
 	if(cadena[fila+1][col]=='X') contador++;
 	if(cadena[fila+1][col+1]=='X') contador++;
	
 	aplicarRegla(contador,fila,col, cadena[fila][col]);
	
    }

    public void aplicarRegla(int vecinos, int x, int y, char caracter){
 	if(caracter=='X' && (vecinos == 2 || vecinos == 3) ){
 	    buffer[x][y] = 'X';
	}
	 if(caracter != 'X' && vecinos==3){
	    buffer[x][y] = 'X';
	}
	if(vecinos > = 4){
	    buffer[x][y] = '-';

	}
	if(vecinos<=1){
	    buffer[x][y]='-';
	}
    }

    public void copiarA(){
	for(int i=0; i <largo;i++){
	    for(int j=0; j <ancho;j++){
		cadena[i][j]=buffer[i][j];
	    }

	}
    }

    public static void main(String args[]){
	
	int lolargo = Integer.parseInt(args[0]);
	int loancho = Integer.parseInt(args[1]);

	GameOfLife game = new GameOfLife(lolargo, loancho);
	game.poblacionRandom();

	while(true){

	    
	    game.imprimir();
	    System.out.printf("\n\n");
	    game.recorrerMatriz();
	    
	    
	    game.limpiar();
	    game.copiarA();
	    
	   
	}
	    
	
    }//end of main
}//end of class




Necesita 2 argumentos desde la línea de comandos. 
Por lo pronto la preparé para que sea una matriz cuadrada.
Para poder apreciar la salida del programa podemos mandarla a un archivo de texto:

Abriendo el archivo de texto salida.txt podremos ver las iteraciones realizadas:
1era iteración
 2da iteración
 3era iteración
Enésima iteración

Referencias:
http://www.bitstorm.org/gameoflife/
http://www.math.com/students/wonders/life/life.html

1 comentario:

  1. Revisado. Se ven bastante bien el código y las capturas de pantalla. Faltaron las conclusiones del ejercicio. También recuerda que las referencias electrónicas llevan un cierto formato; checa, por ejemplo, el de APA http://owl.english.purdue.edu/owl/resource/560/10/

    ResponderBorrar