El Juego de la Vida de Conway
El Juego de la Vida de Conway es un ejemplo clásico de cómo la complejidad puede florecer a partir de unas pocas reglas simples. Imagina una cuadrícula infinita de celdas — cada una puede estar «encendida» (viva) o «apagada» (muerta). En cada turno (o «generación»), cada celda verifica cuántos vecinos están vivos. Si está demasiado poblada, muere. Si está demasiado aislada, también muere. Pero con exactamente tres vecinos, una celda muerta cobra vida.
Este simple conjunto de reglas lleva a comportamientos infinitamente fascinantes — desde formaciones estables y osciladores repetitivos, hasta «naves espaciales» autopropulsadas que atraviesan la cuadrícula. Es una demostración perfecta de cómo la complejidad puede surgir de la simplicidad, y es incluso Turing completo, lo que significa que puede, en teoría, simular cualquier computación.
Ejemplos de patrones
Estos patrones clásicos muestran el comportamiento emergente del juego. El «Glider» demuestra movimiento a través de la cuadrícula, el «Pulsar» crea una oscilación fascinante, y el «Small Exploder» muestra cómo formas simples pueden evolucionar hacia formas complejas.
El Algoritmo
En el corazón del Juego de la Vida hay unas pocas funciones elegantes que determinan la vida y la muerte en nuestro universo celular:
// The core algorithm that determines life and death
function computeNextGeneration(currentGrid) {
const newGrid = createEmptyGrid(rows, cols);
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const neighbors = countNeighbors(currentGrid, r, c);
const cellState = currentGrid[r][c];
// Apply Conway's rules of life
if (cellState === 1) {
// Live cell survives if it has 2 or 3 neighbors
newGrid[r][c] = (neighbors === 2 || neighbors === 3) ? 1 : 0;
} else {
// Dead cell springs to life if it has exactly 3 neighbors
newGrid[r][c] = (neighbors === 3) ? 1 : 0;
}
}
}
return newGrid;
}
// Count live neighbors for each cell
function countNeighbors(g, row, col) {
let count = 0;
for (let i = 0; i < 8; i++) {
const nr = row + NEIGHBOR_OFFSETS[i * 2];
const nc = col + NEIGHBOR_OFFSETS[i * 2 + 1];
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) {
count += g[nr][nc];
}
}
return count;
}