AC: Jogo da Vida
Anterior: Modelos Logísticos | Índice: Ecologia | Próximo: Modelo de Lotka-Volterra
Modelos conceituais
O "Jogo da Vida" é um jogo criado em 1970 pelo matemático Horton Conway usando autômatos celulares. Na proposta original o jogo da vida é composto por uma grade de células quaradas, onde cada célula pode estar morta ou viva, e seu estado é atualizado a cada passo finito de tempo baseado no estado atual da própria célula e das 8 células vizinhas (vizinhança de Moore). Estas regras podem ser enunciadas da seguinte forma:
- Reprodução: Qualquer célula morta com exatamente três vizinhos vivos será uma célula viva no próximo passo.
- Sobrevivência: Qualquer célula viva com 2 ou 3 vizinhos vivos permanece vivo no próximo passo.
- Subpopulação: A célula morre se tiver menos de 2 vizinhos vivos na vizinhança.
- Superpopulação: A célula morre se tiver mais de 3 vizinhos na vizinhança.
Um autômato celular probabilístico foi proposto como uma extensão no jogo da vida. A intenção é possuir um modelo mais flexível com a possibilidade de alterar os limites do Jogo da Vida clássico. A ideia básica do PCAEGOL (Probabilistic Cellular Automata, Extension of the Game Of Life) é que os estados das células vizinhas de uma dada célula não são conhecidos com exatidão, dessa forma o estado resultante não é determinístico e há uma certa probabilidade de uma célula que teria determinado resultado deterministicamente, agora tenha outro. A ideia é que o sistema nem sempre se comporte conforme é esperado. As probabilidades podem ser definidas individualmente para cada célula vizinha de uma determinada célula. Por exemplo, tendo a célula central C0 e chamando as 8 células vizinhas de Ci (onde i=1,2,...,8), então temos os vetores:
- pl = (pl0,pl1,...,pl8), onde pli é a probabilidade da célula Ci ser considerada viva quando está viva.
- pd = (pd0,pd1,...,pd8), onde pdi é a probabilidade da célula Ci ser considerada viva quando está morta.
Podemos recuperar o jogo da vida facilmente fazendo pli=1$ e pdi=0. Além disso as regras clássicas do jogo da vida podem ser estendidas fazendo:
- MAXO = Número máximo de células vizinhas vivas para que não ocorre superpopulação
- MINU = Número máximo de células vizinhas vivas para que não ocorre superpopulação
- MAXB = Número máximo de células vizinhas vivas para que a célula nasça no próximo passo
- MINB = Número mínimo de células vizinhas vivas para que a célula nasça no próximo passo
Novamente podemos recuperar o Jogo da Vida clássico fazendo MAXO=3,MINU=2, MAXB=3 e MINB=3.
Código do Jogo da Vida
Mesa é um framework em Python para desenvolvimento de modelos baseados em agentes, porém também podemos simular autômatos celulares e visualizar o resultado, por isto o mesmo foi utilizado para desenvolver ambos os códigos a seguir. Algun tutoriais podem ser consultados aqui e aqui.
Para começar, precisamos definir duas classes: a de cada agent (no nosso caso cada célula) e a própria do modelo. Cada classe tem algumas obrigatórias relacionadas a forma com que avançamos o modelo. Como é assíncrono, o agente tem uma função para preparar as mudanças (step) e outra para aplicar as mudanças (advance). Além disso precisamos passar algumas informações para o modelo: que as células estão distribuídas em grade, a largura e altura da grade, e que as alterações ocorrem de forma assíncrona.
#Bibliotecas necessárias from mesa import Agent, Model #Importamos as classes Agente e Modelo from mesa.time import SimultaneousActivation #Importamos o agendador por ordem aleatória from mesa.space import MultiGrid #Importamos a malha from mesa.visualization.modules import CanvasGrid #Gerar o JSON from mesa.visualization.ModularVisualization import ModularServer #Ler o JSON import random #Biblioteca para números aleatórios #O Modelo em si class Planta(Agent): """Dinâmica da planta inspirada no jogo da vida""" def __init__(self, ide, modelo,frac): super().__init__(ide, modelo) #Requisito da biblioteca self.vivo = random.random() < frac #Guardar o estado atual self.passo=self.vivo #Guardar o próximo estado def step(self): # Método que aplica as mudanças a cada passo #Pegamos a coordenada da vizinhança vizinhos = self.model.grid.get_neighborhood(self.pos,moore=True,include_center=False) nviz=0 agentes = self.model.grid.get_cell_list_contents(vizinhos) #E o conteúdo de cada célula for planta in agentes: if (planta.vivo==True): nviz+=1 if (self.vivo==True): if (nviz<2 or nviz>3): self.passo = False elif (nviz==3): self.passo=True def advance(self): #E no simultâneo precisa de um método avance() que é o que aplica as mudanças assíncronas self.vivo=self.passo class Modelo(Model): """Modelo geral""" def __init__(self, frac, largura, altura): self.frac = frac self.grid = MultiGrid(largura, altura, True) #Grade self.schedule = SimultaneousActivation(self) #Assíncrono self.running = True #Sem codições de parada # Criar agentes i=0 for X in range(largura): for Y in range(altura): a = Planta(i, self,self.frac) self.schedule.add(a) self.grid.place_agent(a, (X, Y)) i+=1 def step(self): #Avança o modelo self.schedule.step()
Para exibir a simulação na tela, ainda precisamos definir como gerar o JSON em que especificamos como cada célula deve ser exibida na tela. No caso omo retângulos 1x1 onde a cor depende se a célula esta viva ou morta.
#Define como gerar esse JSON def retrato(planta): """Definir o retrato do agente""" portrayal = {"Shape": "rect","Filled": "true","h": 1.0,"w":1.0,"Layer":1} if (planta.vivo == True): portrayal["Color"] = "green" else: portrayal["Color"] = "grey" return portrayal
Definimos então a grade para exibir na tela:
#Definimos uma grade de 10x10 células e 500x500 píxeis grade = CanvasGrid(retrato, 10, 10, 500, 500)
E lançamos o servidor:
#Lançamo o servidor servidor = ModularServer(Modelo, [grade], "Jogo da Vida",{"frac":0.1, "largura":10, "altura":10}) servidor.port = 8521 servidor.launch() #Lançamos o servidor
Código do PCAEGOL
O código do PCAEGL segue a mesma ideia do anterior, com as alterações pertinentes devido às diferenças de modelo.
#Bibliotecas necessárias from mesa import Agent, Model #Classes Agente e Modelo from mesa.time import SimultaneousActivation #Agendador simultâneo from mesa.space import MultiGrid #Malha multigrid from mesa.visualization.modules import CanvasGrid #Gerar o JSON from mesa.visualization.ModularVisualization import ModularServer #Ler o JSON import random #Número aleatórios #Classe do agente class Agente(Agent): """Classe do agente""" def __init__(self, ide, modelo): """Função incializadora""" super().__init__(ide, modelo) #Necessário para funcionar o modelo F = 0.5 #Fração de plantas vivas self.vivo = random.random() < F #Definimos o estado inicial da planta self.passo = self.vivo #E já definimos a situação no próximo estado def step(self): """Método obrigatório que prepara as mudanças""" #pl = 1;pd = 0;MAXO=3;MINU=2;MAXB=3;MINB=3 pl = 1;pd = 0;MAXO=6;MINU=2;MAXB=3;MINB=3 #Parâmetros do PCAEGOL vizinhos = self.model.grid.get_neighborhood(self.pos,moore=True,include_center=False) #Coordenada da vizinhança nviz=0 #Contador de vizinhos agentes = self.model.grid.get_cell_list_contents(vizinhos) #Agentes na vizinhança for planta in agentes: #Percorre o agentes estado = (random.random() <pl) if (planta.vivo) else (random.random() <pd) #Se está vivo ou não nviz = (nviz+1) if (estado) else (nviz) #Soma o mais um vizinho estado = (random.random() <pl) if (self.vivo) else (random.random() <pd) if (estado): if (nviz<MINU or nviz>MAXO): #Sub ou super população self.passo = False elif (nviz >= MINB and nviz<=MAXB): #Reprodução self.passo = True def advance(self): """Método obrigatório que aplica as mudanças""" self.vivo=self.passo #Aplica as mudanças class Modelo(Model): """Modelo geral""" def __init__(self, largura, altura): """Função chamada quando o modelo é inicializazdo""" self.grid = MultiGrid(largura, altura, True) #Configura a grade self.schedule = SimultaneousActivation(self) #Configura o agendador self.running = True #Condiçao para seguir executando o modelo # Distribuir agentes em toda grade i=0 for X in range(largura): for Y in range(altura): a = Agente(i, self) self.schedule.add(a) self.grid.place_agent(a, (X, Y)) i+=1 def step(self): """Avançar um passo do modelo""" self.schedule.step() #Define como gerar esse JSON def retrato(agente): """Definir o retrato do agente""" portrayal = {"Shape": "rect","Filled": "true","h": 1.0,"w":1.0,"Layer":1} if (agente.vivo == True): portrayal["Color"] = "green" else: portrayal["Color"] = "grey" return portrayal #Lançamo o servidor grade = CanvasGrid(retrato, 20, 20, 500, 500) servidor = ModularServer(Modelo, [grade], "Jogo da Vida",{"largura":20, "altura":20}) servidor.port = 8521 servidor.launch()
Principal material utilizado
A probabilistic extension to Conway’s Game of Life (Gabriel Aguilera-Venegas e outros,)
Anterior: Modelos Logísticos | Índice: Ecologia | Próximo: Modelo de Lotka-Volterra