Game of Life
“Se as pessoas não acreditam que a matemática é simples, é apenas porque elas não percebem o quão complicado é a vida“.
A frase acima é de John von Neumann, um dos maiores matemáticos do século XX. Foi o cientista chefe do projeto que criou o primeiro computador eletrônico (há controvérsias por causa do Collossus feito 3 anos antes na Inglaterra, mas só apresentado publicamente após 20 anos) , criou junto com Oskar Morgenstern o ramo da matemática/economia chamada Teoria dos Jogos, fundamentou várias partes da Mecânica Quântica; e introduziu um conceito chave para muitas partes da ciência chamada Autômatos Celulares (AC) (farei um post sobre isto em breve).
Em 1970, John Conway, outro matemático, pesquisando sobre autômatos celulares, bolou um modelo matemático para observar qual seria o resultado de uma simulação de regras simples em um conjunto de AC. Ele batizou este modelo de Game of Life ou simplesmente Life, devido a alguns resultados bem interessantes, pode-se dizer até “orgânicos”, que ele obteve após algumas simulações. O vídeo abaixo mostra o próprio Conway descrevendo sua motivação ao propor o modelo.
O modelo inicial era composto por um grid 2D de células retangulares. Uma célula escura representa uma célula morta, e uma célula “acesa”, clara, representa célula viva. Após uma série de testes para garantir que houvesse evolução no grid, sem uma super-população de autômatos ou sem condições suficientes para os autômatos, células, se desenvolverem, Conway chegou em 3 regras simples:
- Uma célula morta com exatos 3 vizinhos vivos, tornava-se viva na próxima iteração da simulação,.
- Uma célula viva com 2 ou 3 vizinhos vivos, permanece viva na próxima iteração.
- Em qualquer outro caso, a célula morre ou permanece morta (ou por super-população ou por solidão, falta de condições de ficar viva).
Logo que publicou seu modelo, ele despertou um grande interesse porque já era conhecido que Autômatos Celulares possuem o mesmo poder computacional de uma Máquina Universal de Turing, e resultados interessantes em Sistemas Auto-Organizáveis (Self-Organized Systems) e Sistemas Emergentes poderiam ser simulados e compreendidos de forma mais completa. Life permite que se observe como sistemas simples, nos quais todas as regras são conhecidas, evoluem, fornecendo indicações de como a vida pode ter sido bem simples no começo, ao contrário do que diz a teoria do Criacionismo, e, seguindo regras simples, evoluiu para o que conhecemos hoje.
Vários padrões interessantes foram identificados ao longo dos anos de pesquisa no Game of Life, como, Block, Beehive, Blinker, Toad, Glider. Alguns deles você pode ver na implementação em Processing aqui, ou em alguns outros links (aqui, e aqui) na internet.
Na versão oficial do Processing, existe uma implementação do modelo de Conway, mas visualmente ela é muito “orgânica”. Eu queria fazer algo que parecesse mais digital e mais próximo do que era visualizado nos primeiros experimentos computacionais dos anos 1970, ai implementei uma versão ligeiramente diferente. O estado inicial do grid é randômico e inclui também um teste para resetar a simulação quando o grid está estável, com o mesmo padrão por um período longo de tempo. O código processing que gera a simulação está abaixo, e a versão implementada em ProcessingJS está aqui. Qualquer dúvida ou comentário, mande uma mensagem por Contact.
[java]
int WIDTH = 800, HEIGHT = 600;
int squareHeight, squareWidth;
int numCol, numLine;
int density;
int[][][] world;
int[][] stability;
float stabilityFactor;
int stabilityCount1, previousStabilityCount;
int stabilityCount2;
color black, white;
void setup()
{
// Grid Information
numCol = 80;
numLine = 80;
squareWidth = WIDTH / numCol + 1;
squareHeight = HEIGHT / numLine + 2;
density = 70;
// Colors of the Grid
black = color(0);
white = color(255);
world = new int[numCol][numLine][2];
stability = new int[numCol][numLine];
setupGrid();
stabilityFactor = (numCol * numLine) / 60;
size(WIDTH, HEIGHT);
background(0);
// Gray Lines
stroke(100, 100, 100);
}
void setupGrid()
{
stabilityCount1 = 0;
stabilityCount2 = 0;
previousStabilityCount = 0;
for (int lines = 0; lines < numLine; lines++)
{
for (int columns = 0; columns < numCol; columns++)
{
world[columns][lines][0] = random(101) > density ? 1 : 0;
world[columns][lines][1] = 0;
stability[columns][lines] = world[columns][lines][0];
}
}
}
void draw()
{
background(0);
// Draw Current State of the Grid
for (int lines = 0; lines < numLine; lines++)
{
for (int columns = 0; columns < numCol; columns++)
{
if (world[columns][lines][0] == 1) fill(white);
if (world[columns][lines][0] == 0) fill(black);
rect(columns * squareWidth, lines * squareHeight, squareWidth, squareHeight);
}
}
// Compute the new state of the Grid
for (int lines = 0; lines < numLine; lines++)
{
for (int columns = 0; columns < numCol; columns++)
{
// Cell doesn’t change by default
world[columns][lines][1] = world[columns][lines][0];
int count = neighbours(columns, lines);
// Turn the square on if it was black and there are 3 neighbors now
if (count == 3 && world[columns][lines][0] == 0)
{
world[columns][lines][1] = 1;
}
// Turn the square off if it was white and there are more than 3 or less than 2 neighbors now
if ((count < 2 || count > 3) && world[columns][lines][0] == 1)
{
world[columns][lines][1] = 0;
}
}
}
// Copy the new generation into the current grid
// and update the stability matrix
for (int lines = 0; lines < numLine; lines++)
for (int columns = 0; columns < numCol; columns++)
{
world[columns][lines][0] = world[columns][lines][1];
if (stability[columns][lines] != world[columns][lines][0])
{
stability[columns][lines] = world[columns][lines][0];
stabilityCount1++;
}
}
// Test if the number of cells that kept the same
// value is the same for a long period of time
if (stabilityCount1 == previousStabilityCount)
{
stabilityCount2++;
if (stabilityCount2 >= stabilityFactor) setupGrid();
}
else
previousStabilityCount = stabilityCount1;
stabilityCount1 = 0;
}
int neighbours(int x, int y)
{
return world[(x + 1) % numCol][y][0] +
world[x][(y + 1) % numLine][0] +
world[(x + numCol - 1) % numCol][y][0] +
world[x][(y + numLine - 1) % numLine][0] +
world[(x + 1) % numCol][(y + 1) % numLine][0] +
world[(x + numCol - 1) % numCol][(y + 1) % numLine][0] +
world[(x + numCol - 1) % numCol][(y + numLine - 1) % numLine][0] +
world[(x + 1) % numCol][(y + numLine - 1) % numLine][0];
}
[/java]





