This graph shows how many times the word ______ has been mentioned throughout the history of the program.
Hace una semana subí al canal un vídeo reflexionando sobre el famoso juego de la vida y creo que
gracias a ese vídeo he conseguido motivar a muchas personas a querer saber más del
tema y a otras tantas a querer implementarlo en código, a implementar vuestro propio juego
de la vida.
La idea de hoy es simplemente sentarnos frente a nuestro editor de código favorito y poder
implementar, poder enseñaros a cómo hacer vuestro juego de la vida en tan solo 10 minutos.
Como digo vamos a estar trabajando con Python y específicamente con dos librerías que
tendremos que instalar.
La primera es Pygame que es una librería que nos aporta muchísimas funcionalidades
para crear juegos en Python y la otra es la famosísima Numpy que nos aporta funcionalidades
para trabajar con matrices y funciones matemáticas.
Una vez tengamos las librerías instaladas vamos a empezar creando la pantalla de nuestro
juego con Pygame.
Para eso lo que vamos a hacer es crear una pantalla de ancho y largo de 1000x1000 píxeles
y le vamos a asignar un color de fondo gris oscuro casi negro con una intensidad en cada
uno de los canales de color de 25.
A partir de aquí si queremos que nuestra pantalla se muestre de manera indefinida
lo único que tendremos que hacer será crear un bucle infinito.
Con esto lo ejecutamos y ya tendríamos el lienzo básico de nuestro juego.
El siguiente paso será crear cada una de las celdas de nuestro juego y para eso primero
tendremos que especificar cuántas celdas queremos tanto en el eje X como en el eje Y
y el ancho y alto de cada una de estas celdas vendrá dado por una simple división entre
el ancho y alto de la pantalla y el número de celdas que tenemos.
Con esto ahora iremos generando cada uno de los gráficos que se irán dibujando en cada
uno de los fotogramas y para ello lo que tendremos que hacer serán dos bucles for que recorran
tanto en el eje X como en el eje Y cada una de las celdas que hemos generado.
Para ello utilizaremos la función dropPolygon de Pygame que nos pide que le pasemos como
parámetro de entrada pues la pantalla donde vamos a dibujar, el color que va a utilizar
para dibujarse, en este caso un gris normal y los puntos que definan al polígono que
estamos dibujando.
En nuestro caso este polígono si lo pensamos vendrá definido por estos puntos de aquí.
Si la variable X e Y van a ir indexando cada una de las celdas de nuestra escena pues tiene
sentido delimitar las coordenadas de este polígono a partir de multiplicar estos índices
por el ancho y alto de cada una de las celdas.
Así ya tendríamos las coordenadas de nuestros rectángulos definidos y lo que vamos a hacer
ahora es especificarle un grosor de línea de un pixel para que así tenga apariencia
de cuadrícula.
Con esto ahora podríamos utilizar la función display para ir mostrando y actualizando los
fotogramas de nuestra escena en cada iteración del bucle.
Si lo ejecutamos veremos que ya tenemos una rejilla como esta.
Esta sería toda la lógica que nos implementaría la vista del juego, pero todavía nos faltaría
generar una estructura de datos donde contengamos todos los estados de las diferentes celdas
de nuestra simulación.
Para ello lo que vamos a hacer es crear una matriz de tamaño igual al número de celdas
que tenemos que esté completamente a cero.
Si una de las celdas estuviera con un valor a 1 significaría que esa celda está viva
y si tiene un valor a cero pues significaría que estaría muerta.
En realidad son 10 minutos, pero yo en la vida real me acabo de echar una cista de una
hora y media a gustísimo.
¿Por dónde llevamos?
Ah sí, si recuerdan en el juego de la vida el planteamiento era que podíamos obtener
comportamientos muy complejos a partir de aplicar una serie de sencillas reglas a cada
una de las celdas de nuestro lienzo.
Lo único que teníamos que tener era en cuenta cómo cada una de las celdas se relacionaba
con cada uno de sus vecinos.
Y para ello lo que tenemos que hacer es para cada una de las celdas, por eso lo hacemos
dentro de los dos bucles for, es ver cuántos vecinos vivos hay.
Esto significa que tenemos que computar la suma de la posición x-1 y-1, x-1 y x-1 y
más 1, etcétera, y así con las 8 casillas que rodean a la celda actual.
Claro, si estamos consultando cuáles son los vecinos de x-1, x-1 y-1 y más 1, tenemos
que tener en cuenta qué sucede en los bordes cuando superamos el límite.
Lo normal sería decir oye pues me da igual y simplemente que se ejecute sin tener en
cuenta lo que hay fuera de los bordes.
Pero hay una estrategia más interesante que sería la estrategia toroidal o la estrategia
del pacman, que es cuando tú te vas por un lado, apareces por el otro.
A eso se le llama la estrategia toroidal porque si lo piensas ir por arriba y aparecer por
abajo e ir por la derecha y aparecer por la izquierda, es una cosa que sucede sobre la
figura geométrica del toroide.
Esto es a efectos geométricos, pero a efectos prácticos realmente lo que queremos decir
es que si yo tengo una pantalla de 5 celdas, pues si los indices van del 0, 1, 2, 3, 4,
cuando llega a la posición 5, en vez de salirme del mapa, lo que quiero es que vuelva a la
posición 0.
Esto es algo que podemos computar fácilmente con la operación módulo.
Es por eso que lo que vamos a hacer es modificar el código para que cuando accedamos a cada
uno de los índices de nuestra tabla, realmente lo que estamos accediendo es al módulo del
tamaño de nuestra celda y así, en caso de salir, volveremos por el principio.
Y con esto faltaría ahora diseñar toda la lógica, todas las reglas que se van a aplicar
durante el juego.
Y por muy complejo que pueda sonar esto, en realidad es lo más sencillo del código porque
el trabajo ya lo tenemos hecho.
Si recuerdan eran dos reglas las que se aplicaban en el juego de la vida.
La primera es que si tú eres una celda que estás muerta, cuyo estado está a 0, y el
número de vecinos que tienes alrededor vivos es mayor a 3, lo que va a pasar con tu estado
es que va a revivir, va a cambiar de un 0 a un 1.
Esto lo hacemos fácilmente en el código con estas lineas aquí.
Por otro lado, la segunda regla es que si tú eres una celda que estás viva, pero el
número de vecinos que te rodean vivos es menor de 2 o mayor de 3, acabarás muriendo
por soledad o por superpoblación, algo que de nuevo podemos completar con esta regla
de aquí.
Ahora es importante darse cuenta de una cosa y es que aquí dentro de los bucles for, nosotros
estamos calculando este cambio, esta aplicación de las reglas a cada una de las celdas de
manera secuencial.
Eso significa que si yo he cambiado la celda anterior, ahora en el cálculo de la de ahora,
estoy teniendo en cuenta los cálculos anteriores y eso en realidad no debería de ser así,
sino que todos los cambios deberían de producirse al mismo tiempo en cada periodo de tiempo.
Entonces para solucionar eso lo que tenemos que hacer es en cada iteración realizar una
copia del estado actual del juego.
Serán esta copia donde iremos guardando cada una de las actualizaciones que realicemos
en cada celda del juego y la que utilicemos posteriormente para dibujar los cuadraditos
de colores que nos hagan falta.
Porque si recuerdas, en realidad lo que estamos representando ahora en nuestra pantalla es
simplemente una rejilla vacía donde cada cuadrado no tiene ningún relleno, sino simplemente
un borde de un pixel.
Esto lo podemos modificar en función del estado que hayamos guardado en New Game State.
Si el estado de la celda actual es igual a 1, entonces lo que queremos será dibujar
un cuadrado de color blanco y que no tenga ningún tipo de borde, sino que simplemente
su relleno sea igual a, pues completamente sólido.
Eso lo hacemos cambiando el parámetro Width a 0.
Hemos procesado y visualizado todo, lo único que tenemos que hacer es que Game State pasa
a ser el estado que hemos calculado ahora.
Ahora sí, aunque no lo parezca, el juego de la vida estaría completado.
Podemos coger y podemos inicializar algún estado del tablero inicial donde generemos
algún tipo de automata que reconozcamos, como por ejemplo el automata palo o el automata
este que se mueve por la pantalla.
Y con esto simplemente activando cada uno de sus píxeles, si ahora inicializamos la
ejecución veremos que la cosa funcione… vale, no nos funciona, casi funciona.
Y es que nos hemos dejado atrás en que cada iteración que nosotros actualizamos, el estado
de la pantalla, tenemos que limpiarla de información para que esto no se vaya superponiendo.
Esto lo hacemos simplemente volviendo a colorear toda la pantalla con el color de fondo que
habíamos elegido en un principio.
Además junto a esto también vamos a importar el módulo Time para tomar la función Time.sleep
y generar un pequeño delay que permita entre cada fotograma que se pueda tomar un respiro
todo el sistema y que no sea tan rápido.
Y con esto ya estaría, con esto ya tendríamos hecho el juego de la vida, así de sencillo.
¿Qué nos faltaría?
Podríamos mejorar todo el sistema que hemos creado a partir de registrar los diferentes
eventos que se realicen por teclado o por ratón, pues para que podamos tener un mayor
control.
Por ejemplo, podemos registrar cada vez que estamos pulsando una tecla en el teclado,
para así flagear, cambiar el estado de True a False y de False a True de una variable
que se llame PauseEsect y que lo que haga sea controlar el flujo de ejecución de nuestro
juego.
Podemos inicializar esta variable a False, podemos hacer que se cambie cada vez que pulso
una tecla y podemos hacer que esta variable controle si se tienen que actualizar las reglas
y el estado del juego o no.
Con esto, si lo volvemos a ejecutar, pues podremos comprobar como cada vez que pulsemos
la tecla del teclado, pues la ejecución del juego se irá parando o reanudando.
Y no solo eso, sino que con esta función de aquí, con mouse.getPreset, podemos saber
en cada momento qué tecla del ratón se está pulsando.
Con esta función lo que obtendremos será un vector de tres componentes que nos marque
a uno aquella posesión que indique pues cuál es el botón del ratón que hemos marcado,
si es el botón derecho, el botón de la rueda o el botón izquierdo.
Y claro, esto lo podemos utilizar pues de la siguiente manera, podemos decir, oye, la
suma de este vector, si es mayor a cero, significa que hay algo que está siendo pulsado, alguno
de los botones del ratón.
Así que lo que tienes que hacer ahora es sacarme la posición en la pantalla del ratón.
Esta posición te la va a devolver en píxeles y a nosotros nos interesa saber qué celda
estamos pulsando y esto lo podemos calcular simplemente pues haciendo la división por
el ancho y alto de cada una de las celdas.
Con esto, bueno, si queremos que sean índices, lo que tenemos que hacer es redondear este
valor y convertirlo a enteros.
Y cuando ya sabemos qué celda estamos clicando exactamente, podemos cambiar el estado de
dicha celda por un uno.
Con esta ahora, si lo volvemos a ejecutar, podremos comprobar cómo podemos dibujar en
pantalla e ir activando aquellas celdas que nos interesen que participen en el juego e
incluso si queremos mejorar un poco la user experience, podemos coger y decir oye, no,
no me pongas un uno en esta celda, sino que simplemente tómame el valor de contrario
al del botón izquierdo del ratón.
Esto significa si estoy pulsando algún botón del ratón que no sea el izquierdo, me vas
a poner un uno.
Pero si estoy pulsando el izquierdo, lo que me vas a poner es un cero y esto te va a permitir
no solo escribir, sino también borrar.
Y con esto damos por finalizado el tutorial, espero que haya quedado menos de 10 minutos.
No sé, ha habido una siesta de por medio, la edición hará su magia, pero bueno, con
esto ya tienes un juego que puedes jugar, vale, al juego de la vida.
Puedes crear tus autómatas, puedes estar probándolos, puedes echarte una tarde buenísima
con ello.
Y ahora voy a responder la pregunta que muchos se estarán haciendo y es ¿por qué he hecho
este tutorial?
O mejor aún, ¿por qué he hecho este tutorial si ya lo hice la semana pasada en directo
en Twitch y lo resubí incluso a mi canal secundario NotCSV?
¿Por qué estoy repitiendo este contenido?
Pues básicamente porque sé que hay mucha gente que aún no sabe que esto está pasando.
Como sabrán hace cuestión de un mes y medio, lancé la propuesta de 100 horas de Machine
Learning, que básicamente es estar yo en directo en Twitch durante todo el año haciendo
100 horas de streaming explicando aquellos contenidos que a mí realmente me interesan.
Hemos hecho unas cuantas horas que ya son bastante interesantes, leyendo artículos
sobre aprendizaje reforzado, hemos visto código de cómo puede entrenarse a una IA para que
aprenda a jugar a juegos, hemos estado haciendo un montón de cosas e incluso la semana pasada
abrimos un nuevo apartado que es el que vamos a explorar las próximas semanas.
Es el tema de los autómatas celulares, un tema que a mí me parece súper apasionante
y que efectivamente viene a raíz de todo el tema del juego de la vida.
Este ha sido el comienzo y esta es la única ocasión en la que voy a convertir este contenido
del canal secundario de los directos a un contenido en el canal principal, pero han
de saber que si no están atentos al otro contenido, es contenido educativo bastante
interesante que también se van a perder.
En las próximas semanas seguramente estemos modificando este juego de la vida para ver
qué pasa si añadimos reglas nuevas a todo esto, o también estaremos combinándolo con
redes neuronales profundas para ver cómo podemos modificar el estado para que sea un
poquito más avanzado, vamos a decir por no decir loco.
También veremos algunos artículos donde esto ya se ha hecho con resultados bastante
curiosos e intentaremos entenderlos a partir de leer el artículo paso por paso e ir explicando
lo que en gran medida pueda.
Y también estaremos hablando incluso de la locura esta que ha salido esta semana, casualmente,
presentada por Wolfram, donde plantea incluso un modelado de todo el universo, una teoría
que unifica toda la física del universo a partir de un sistema similar al juego de la
vida o un sistema basado en unas reglas muy sencillas que de manera recursiva pues se
van aplicando y acaban generando un universo.
Todo esto lo estaré explicando y lo estaré cubriendo, pero será todo a través de Twitch
y de Node.csv.
Ya le digo, el tutorial de hoy ha sido un tutorial cortito de 10 minutos, muy picadito
y que a lo mejor le sirva para aprender cosas interesantes, pero de verdad considero que
se aprende mucho más durante una hora en la que yo no estoy explicando exactamente
lo que tienes que hacer si no estoy yo aprendiendo y peleándome y chocándome y teniendo fallos
y teniendo que decidir en tiempo real cómo se implementa todo esto, que fue lo que sucedió
la semana pasada.
De ahí el motivo de este vídeo, el motivo de este tutorial, que igualmente espero que
les haya servido y ya saben que pueden seguirme en Twitch con Twitch Prime, pueden suscribirse
y apoyar de manera gratuita para apoyar este contenido, lo podéis hacer a través de Patreon,
ya lo dije que ahora más que nunca es muy importante hacerlo y en cualquier caso, si
no, volveremos con más inteligencia.