En este artículo trato la implementación de un segundo autómata celular muy celebre en la comunidad llamado “El mundo alámbrico” ( Wireworld ). Explico la fundamentación del AC y su implementación en Python a partir del código del proyecto del juego de la vida de Conway.
Visita el artículo anterior de la serie si aún no los has leído: Programando autómatas celulares en Python: El juego de la vida de Conway
Fundamento
El mundo alámbrico ( wireworld ) es un autómata celular creado en 1.987 por el ingeniero informático canadiense Brian Silverman. Este autómata permite simular el funcionamiento de circuitos digitales mostrando el desplazamiento de los electrones a través de pistas conductoras.
Multiplicador binario de 8 bits creado en 2002 por Nick Gardner
Al igual que El juego de la vida de Conway, este AC emplea en una serie de reglas simples que se aplican sobre las celdas que integran el espacio de simulación. Sus características descriptivas son las siguientes:
- Espacio -> Un mosaico 2D de celdas cada una de las cuales puede tener uno de los siguientes estados:
- Conjunto de estados -> Cada celda puede tener uno de los siguientes estados:
- AISLANTE -> La celda es vacía (celda negra)
- CONDUCTOR -> La celda representa un parte de un hilo conductor (celda azul)
- ELECTRON -> La celda representa un electrón (celda amarilla)
- COLA ELECTRON -> La celda representa el punto por el que un electrón acaba de pasar (celda roja)
Puerta lógica AND
- Regla de vecindad -> Emplea el patrón de vecindad de Moore de radio 1 según el cual se consideran vecinas las 8 celdas contiguas a cada una:
Celdas vecinas según vecindad de Moore r =1
- Reglas de transición -> Las reglas de transición son las siguientes y se aplican por cada celda para obtener su próximo estado según su estado actual y el de sus celdas vecinas:
Estado anterior | Estado posterior |
Aislante | Aislante |
Conductor | Si uno o dos vecinos son electrones:
· Electrón Sino · Conductor |
Electrón | Cola Electrón |
Cola Electrón | Conductor |
Patrones de inicio
Este AC permite crear diseños capaces de simular el funcionamiento circuitos digitales básicos tales como puertas lógicas AND, OR, XOR…, etc. Estas pueden componerse a su vez para diseñar circuitos digitales más complejos, tales como sumadores, memorias, flip-flops…, etc.
Los siguientes son los diseños de las puertas lógicas básicas AND, OR y XOR.
Puerta lógica XOR (disyunción excluyente)
Puerta lógica OR (disyunción no excluyente)
Puerta lógica AND (conjunción)
Implementación empleando Python
Existen implementaciones del mundo alámbrico disponibles online:
https://xalava.github.io/WireWorld/
A continuación vamos a abordar la implementación de este autómata celular con Python y la librería PyGame. Para ello vamos a basarnos en el proyecto de implementación del Juego de la Vida de Conway visto en el anterior artículo.
Si estás interesado en aprender a desarrollar videojuegos, visita nuestros cursos de videojuegos
Implementación del Autómata Celular
Lo primero es definir la clase que represente el propio autómata celular e implemente su funcionamiento. La llamaremos Wireworld y la crearemos en el fichero “wireworld.py”:
La clase debe constar de los siguientes atributos:
Las constantes WIDTH, HEIGHT determinan el nº de celdas de ancho y alto del espacio del autómata. Las constantes HEAD, TAIL, CONNECTOR y NONE determinan los valores asociados a los estados de cada celda. Finalmente, las listas internas __world y __next representan respectivamente el espacio principal del autómata celular y el espacio de respaldo empleado durante el proceso de actualización para almacenar el siguiente estado de cada celda tras aplicar las reglas de transición.
Añadimos el constructor y el método reset(). El constructor no recibe ningún parámetro e invoca únicamente el método reset(). Este es responsable de inicializar las listas que conforman el espacio principal (__world) y del de respaldo (__next). Ambas listas contienen inicialmente valores 0 indicando que todas las celdas son AISLANTES.
Las propiedades iterations y electrons devuelven respectivamente el nº de actualizaciones ejecutadas (también llamadas iteraciones), y el nº de electrones presentes en el circuito:
Los método read() y write() que permiten obtener y modificar el estado de una celda dadas sus coordenadas X e Y en el espacio del AC:
El método update() actualiza el estado de cada celda en el espacio del autómata celular. Para ello se toma el estado de cada del espacio principal (__world), se aplican las reglas de transición en función de su estado y el de las celdas vecinas, y se almacena el estado resultante en la misma posición del espacio de respaldo (__next). Una vez procesadas todas las celdas, se vuelca el contenido de la lista de respaldo a la principal actualizando así el espacio del AC definitivamente.
El código del método es el siguiente:
El método draw() proyecta el espacio del AC en un objeto superficie pygame.Surface donde cada celda se muestran como un rectángulo de 10 x 10 píxeles según posición en el espacio del AC:
Según cual sea el estado de la celda, esta se proyecta de distinto modo:
- Si estado es CONDUCTOR ( CONNECTOR ) -> Rectángulo relleno de color azul.
- Si estado es ELECTRON ( HEAD ) -> Rectángulo relleno de color amarillo.
- Si estado es COLA ELECTRON ( TAIL ) -> Rectángulo relleno de color rojo.
- Si estado es AISLANTE ( NONE ) -> Rectángulo vacío con bordes grises.
Los métodos save() y load() permitan salvar y recuperar el espacio del autómata en un fichero de texto. Ambos métodos reciben como argumento una cadena con la ruta y nombre al archivo.
El método save() guarda los valores de la lista __world en el archivo indicado como argumento. Los valores se inscriben en el archivo separados por comas y entre corchetes:
[ 0,0,1,0,0,0,2,0,2,0,0,0,1,0,0,0,0 …. ]
El método load() recupera los valores de la lista __world a partir de lo contenido del fichero en la ruta indicada como argumento.
Si estás interesado en aprender a programar en Python, visita nuestro curso de python
Implementación del interfaz de usuario con PyGame
Una vez completada la clase que implementa el autómata celular pasamos a preparar la interfaz de usuario en el módulo “main.py”. Para ello vamos a reaprovechar el código del anterior proyecto, pero incluyendo algunas mejoras:
La ventana a mostrar constará del siguiente aspecto:
Aspecto del interfaz de usuario vacío.
La ventana tiene el tamaño original de 1000 x 564 píxeles dividida en dos partes:
- El área superior de visualización del espacio del autómata.
- El área inferior de controles donde se muestran los iconos que nos permitirán iniciar, parar y reiniciar el autómata celular, y cargar y guardar su diseño. A la izquierda se muestra el estado del autómata (PAUSE, RUNNING), y a la derecha el nº de iteraciones y el nº de electrones presentes en cada momento.
Edición de circuitos.
El usuario puede crear cualquier circuito e indicar la posición de los electrones con el ratón empleando las siguientes reglas:
- Haciendo clic con el botón izquierdo sobre una celda:
- Si la celda es Aislante -> Se ajusta como Conductor.
- Si la celda es Conductor -> Se ajusta como Electrón.
- Si la celda es Electrón -> Se ajusta como Conductor
- Si la celda es Cola de Electrón -> Se ajusta como Conductor.
- Haciendo clic con el botón derecho una celda pasa a ser Aislante sea cual sea su estado.
Clases auxiliares Button y Text
Para simplificar la implementación del main() vamos a definir una clases auxiliares para controlar los textos y los botones de la parte inferior. Estas clases las definimos en el fichero: “guihelper.py” y son las siguientes:
Clase Button: Representa una imagen y el área que ocupa en la ventana:
Clase Text: Representa un texto incluyendo su fuente, tamaño, posición, color y contenido.
Implementación del módulo principal
El fichero principal “main.py” incluye código del proyecto anterior distinguiéndose en la importación de la clase Wireworld del módulo wireworld.py:
Inicialización
La función principal main() inicializa primero todos los elementos:
- Ventana principal
- Objeto autómata (world)
- Objetos botón (inicio, parada, limpieza, cargado y guardado)
- Objetos textos (estado, iteraciones y electrones)
- Variable de estado running (false). El autómata está inicialmente detenido.
Si estás interesado en aprender a programar en Python, visita nuestro curso de python
Bucle principal -> Captura de eventos
A continuación, el bucle principal captura los eventos del sistema y de usuario:
- QUIT -> Cerrado de ventana
- MOUSEBUTTONDOWN -> Pulsación de ratón
Para comprobar la pulsación sobre cada uno de los iconos mostrados en la parte inferior de la ventana se emplean sus métodos is_clicked() pasando como argumentos las coordenadas del puntero del ratón.
Para editar el espacio del AC se emplea la función auxiliar mouse_click(). Esta función es llamada desde el main() al detectarse la pulsación del ratón sobre el espacio del autómata celular (coordenada y < 500 ). El método recibe como argumentos las coordenadas X e Y (mouse_x, mouse_y) del cursor del ratón y un indicador del botón pulsado (button).
El método auxiliar mouse_click() obtiene el estado de la celda señalada por el cursor y le asigna el nuevo estado empleando los métodos read() y write() del objeto world que representa el AC:
Bucle principal -> Actualización de AC
Tras la captura de eventos, viene el código de ejecución del autómata celular y la actualización de los textos mostrando el estado y el nº de iteraciones y electrones presentes mediante su propiedad text. Para ello se invoca al método update() del objeto world si la variable running es TRUE indicando que el autómata se está ejecutando:
Bucle principal -> Refresco de pantalla
Por último, se incluye el código de repintado de la ventana que incluye la proyección del espacio actualizado del AC, y los botones y textos de la parte inferior.
Modo de uso.
Al iniciar la aplicación se muestra el AC con todas las celdas inactivas y en estado parado, por lo que podemos activar las celdas que deseemos para crear una configuración inicial:
Espacio del AC representando los circuitos de las puertas lógicas XOR, OR y AND.
Inicio del AC
A continuación podemos poner en marcha el AC pulsando sobre el icono PLAY . Se muestra entonces el estado “RUNNING” y El AC muestra la evolución del estado de las celdas al tiempo que se van mostrando el nº de iteraciones y el nº de celdas activas presentes.
Parada del AC
Para detener el AC pulsamos el icono
Se muestra entonces nuevamente el estado “PAUSE”:
Espacio del AC representando los circuitos de las puertas lógicas XOR, OR y AND en electrones en pausa.
Reinicio del AC
Por último, podemos limpiar el AC desactivando el estado de todas las celdas y poniendo el contador de iteraciones nuevamente a 0. Para ello pulsamos el icono:
Carga y guardado de circuitos
- El icono permite guardar el patrón actual en el fichero indicado en el cuadro de diálogo que se muestra después.
- El icono permite cargar el patrón guardado previamente en el fichero indicado mediante el cuadro de diálogo mostrado después.
Si estás interesado en aprender a desarrollar videojuegos, visita nuestros cursos de videojuegos