La Vida en un Canvas de HTML5

Siempre me ha gustado mucho la simulación de sistemas biológicos y me pareció una buena excusa para trastear un poco con el canvas de HTML5 y tener una idea de cómo se puede utilizar para generar gráficos 2D y mostrar la evolución de un autómata celular.

Autómatas celulares: El Juego de la Vida

Un autómata celular está formado por un conjunto de celdas dispuestas de forma regular, cada una de las cuales puede tomar distintos estados, y una serie de reglas de transición que indican cómo calcular el estado de una celda en la siguiente generación en función de su estado y del estado del resto de celdas que forman el autómata.

A partir de esto, podemos ir calculando los estados de las celdas a lo largo del tiempo y estudiar como evoluciona el autómata.

Aunque a mi me parecen algo entretenido en si mismo, parece ser que hay gente que se ha preocupado de buscarles alguna utilidad para modelar procesos del mundo real o incluso aplicarlos en algoritmos criptográficos y de corrección de errores.

El juego de la vida es un autómata celular diseñado por Jown Conway en 1970 con unas reglas muy sencillas:

  • Las celdas son cuadradas y están dispuestas sobre un rectángulo de un tamaño prefijado. Para simplificar, se suele considerar que el rectángulo es un toroide, es decir, el borde superior se conecta con el inferior y el izquierdo con el derecho.
  • Cada celda puede tener sólo 2 estados: vivo o muerto.
  • Una celda que esté viva seguirá estando viva en la siguiente generación si tiene 2 o 3 celdas vecinas vivas. Si tiene menos de 2 o más de 3, pasará a estar muerta.
  • Una celda que esté muerta, pasará a estar viva en la siguiente generación si tiene exactamente tres vecinas vivas. Si no es así, seguirá muerta.

Esta es la variante original del juego de la vida y presenta ciertas curiosidades; si quieres saber más, te recomiendo que eches un vistazo a su artículo en la wikipedia.

La implementación en Javascript

Para implementar el juego de la vida en Javascript he partido básicamente de 3 objetos:

  • Board: representa un tablero bidimensional de celdas cuadradas. Podría haber usado directamente un array de arrays, pero Board permite además inicializar los valores del tablero de forma rápida usando un string (lo que me venía muy bien para los tests) y contiene un método para calcular directamente el número de vecinos de una celda dada.
  • Game: es el juego de la vida propiamente dicho. Contiene un tablero y permite aplicar sobre él la lógica específica del juego de la vida, es decir, las reglas de evolución que hemos visto antes.
  • GameCanvas: es la parte encargada de pintar en pantalla usando un elemento canvas y la veremos con más detalle más adelante.

El código no es especialmente interesante, más bien al contrario, pero si tenéis curiosidad lo podéis encontrar en el repositorio de github del Juego de la Vida, más concretamente en board.js y game.js.

Por cierto, hay también un fichero de tests, que es un buen ejemplo de lo que quería decir en mi reciente serie sobre tests en cuanto a las cosas que creo que merecen la pena testear y las que no. Testear la lógica de la Board y Game (aplicando TDD, es decir, escribiendo los tests primero) durante el desarrollo me resultó útil, pero testear la parte de pintar en pantalla (cosa que podría haber hecho con un mock del objeto context del que hablaré dentro de un momento) hubiera sido muy costoso y no me hubiese aportado ninguna seguridad de que el código realmente iba a funcionar.

Canvas

Para pintar en pantalla el autómata se utiliza un elemento canvas de HTML5. El uso de este elemento es muy sencillo y para el que haya trabajado con la clase Graphics de .NET o con GDI+ en Windows le resultará familiar (y seguramente si has usado cualquier API para dibujar en pantalla te pasará lo mismo, pero esas 2 son las que más he usado y las que mejor conozco).

Tengo que reconocer que tampoco es que haya dibujado cosas muy complicadas (básicamente líneas y cuadrados), pero dado mi escaso talento para el diseño gráfico dudo que llegue a hacer cosas mucho más complicadas con un canvas y con lo que he hecho creo que me puedo hacer una idea de cómo manejarlo.

Para dibujar sobre un canvas, lo primero que necesitamos es declarar un elemento canvas:

<canvas id='myCanvas' width='800' height='600'><canvas>

Una vez que tenemos el elemento canvas, podemos obtener a partir de él un objeto context:

var canvas = document.getElementById('myCanvas'),
    ctx = canvas.getContext('2d');

Con el objeto context, podemos pintar rectángulos:

ctx.fillStyle = '#00FF00';
ctx.fillRect(x, y, width, height);

… o pintar sólo el borde del rectángulo:

ctx.strokeStyle = '#000000'; 
ctx.lineWidth = 1;
ctx.strokeRect(x, y, width, height); 

Una diferencia notable con respecto al objeto Graphics de .NET es que no existe el concepto de Pen o Brush para definir como queremos dibujar las líneas o rellenar las figuras. En lugar de eso, se modifican directamente las propiedades asociadas en el objeto context.

Personalmente, creo que el API de .NET separa mejor las responsabilidades en distintos objetos, aunque también es cierto que en Javascript sería fácil extender el objeto context para usarlo de una forma similar.

Sabiendo esto, pintar el estado del autómata celular sólo requiere recorrerlo e ir rellenado rectángulos, como podéis ver en el código completo.

En la clase GameCanvas se hace uso además de la librería MicroEvent de la que ya comenté algo cuando hablé sobre mixins en Javascript.

Conclusiones

La utilidad práctica de todo esto es más o menos nula, más allá de comprobar que todavía soy capaz de repetir 15 años después una de las prácticas que hice en primero de carrera (entonces con Pascal, eso sí).

Aparte de eso, el funcionamiento del elemento canvas me ha parecido sencillo, aunque su rendimiento no es muy allá. Al menos en los PCs que he probado, al usar un contexto 2d no se aprovecha la aceleración gráfica, la CPU se dispara y los ventiladores empiezan a meter ruido. Por otra parte, es posible que eso sea por algo que esté haciendo mal, así que si alguien tiene alguna sugerencia, ya sabe dónde están los comentarios ;-)

Teniendo en cuenta que en el canvas se puede pintar cualquier cosa y que, como casi todos los elementos HTML, puede responder a eventos de ratón y pulsaciones de tecla, me da un poco de miedo lo que una mente perversa pueda llegar a hacer con él.

Me dan escalofríos de pensar en herramientas que acaben generando código javascript para pintar en un canvas y simular cualquier entorno “nativo”, con sus propios controles en lugar de usar HTML+JS+CSS.

Si queréis ver el resultado del Juego de la Vida, podéis verlo funcionando online (test) o echarle un ojo al código fuente en github.


5 comentarios en “La Vida en un Canvas de HTML5

  1. Cesar Verano dijo:

    Hola, gracias por compartir estos interesantes articulos, realmente son de utilidad, disculpa, tengo una pregunta, abri la demo de este ejercicio y lo deje correr hasta que ya no se pudieron crear y destruir mas y note que algunas celdas siempre se crean y destruyen en una linea de 3 y creo que esto no esta bien según las reglas que comentas, tal vez este equivocado, pero me causo curiosidad y preferi preguntarlo.

    Muchas gracias y saludos desde Colombia, muy buen blog.

  2. Pingback: Lo mejor de la semana sobre desarrollo web en español vol. 6 | ADWE

  3. Coincido totalmente, aún no veo la utilidad real de usar Canvas y su ventaja frente a alguna otra herramienta de dibujo o animación. Claro que soy un novato en esto y seguro que hay más usos útiles para una de las API más poderosas de HTML5. Gracias por el ejemplo y te sigo a partir de ahora.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

*

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>