Primeira vez aqui? Seja bem vindo e cheque o FAQ!
x

Como implementar um automata celular em Python?

+2 votos
25 visitas
perguntada Set 8 em Ciência da Computação por Philippe Azevedo (16 pontos)  
editado Set 9 por Philippe Azevedo

Podemos definir um automata celular (Cellular Automata - CA) como um arranjo de elementos (células) que possuem um estado dentro de um conjunto de estados possíveis.

A cada momento, cada célula altera seu estado a uma nova situação que obedece a um conjunto de regras (modelo) e aos estados das células adjacentes.

Utilizando python, crie um código que:

1) Crie um classe que represente uma célula com funções básicas:
por exemplo: atribuir, ou receber o estado atual ou futuro, saber informações de sua vizinhança e atualizar seu estado: seu estado passa a ter o valor do estado futuro

2) Especialize a classe da célula para os quatro modelos discutidos, e em especial defina a análise dos estados das células vizinhas para se definir o estado futuro.

3) crie um CA que contenha uma matriz de células de acordo com o modelo desejado.

4) Permita ainda, criar uma imagem (.jpeg) do CA de acordo os estados de suas células, em uma simbologia gráfica (cada cor representa um estado). Indique ainda quantas interações entre as células ocorreram para chegar ao estado apresentado.

Compartilhe
comentou Set 9 por danielcajueiro (6,051 pontos)  
Ola Philipe. Note que vc colocou a resposta junto com a pergunta.
Primeiro vc precisa fazer a pergunta e depois colocar a resposta.
É basicamente recortar e colar na area de resposta.
comentou Set 9 por Philippe Azevedo (16 pontos)  
bom dia professor, dei uma limpeza nos comentários para caber na resposta. Até a próxima.

2 Respostas

+1 voto
respondida Set 9 por Philippe Azevedo (16 pontos)  
editado Set 9 por Philippe Azevedo
# Devo definir as bibliotecas necessárias
from random import SystemRandom
from PIL import Image, ImageDraw, ImageColor

import glob

0) Classe Célula

 class Celula:
     def __init__(self, estado):
         self.__estado = estado
         self.__vizinhos = []
         self.__estadoFuturo = estado

      def getEstado(self):
          return self.__estado

      def setEstado(self, estado):
          self.__estado = estado

      def setEstadoFuturo(self, estado):
          self.__estadoFuturo = estado

      def getEstadoFuturo(self):
          return self.__estadoFuturo

      # a regra de atualizacao do estado futuro será implementada nas classes filhas
      def analisarVizinhos(self):
          raise NotImplementedError

      # depois de analisar a vizinhanca, a celula deve atualizar o seu estado atual
      def atualizarEstado(self):
          self.__estado = self.__estadoFuturo

      # adiciona uma célula na lista de células vizinhas
      def AddVizinho(self, c):
          self.__vizinhos.append(c)

      # retorna a lista de vizinhos da célula
      def getVizinhos(self):
          return self.__vizinhos

      # Verificar quantos vizinhos cada celula possui
      def contaVizinhos(self):
          return len(self.__vizinhos)

      # as cores serao definidas de acordo com as classes filhas
      def getColor(self):
          raise NotImplementedError

      def getModelo(self):
          return "Celula"

1) Modelo Jogo da Vida (GOL):
Estados possíveis: vivo - cor branca ou morto - cor preta.
Regra 1: Se uma célula viva tiver dois ou três vizinhos vivos, ela continua a viver. Caso contrário, morrerá por solidão ou superpopulação.
Regra 2: Se uma célula morta tiver exatamente três vizinhos, ela retorna a viver.

Composição de 100 interações do modelo jogo da vida
A imagem será apresentada aqui.

Modelo jogo da vida com 70 interações
A imagem será apresentada aqui.

 # implementação da célula com as regras do jogo da vida (GOL)
 class CelulaGOL(Celula):
      def __init__(self):
         # SystemRandom: uma forma mais imprevisível de gerar numeros aleatórios que Random :)
         rnd = SystemRandom()
         escolha = rnd.random()

         if escolha > 0.5:
           # Celula indica chamada do metodo (__init__) da classe pai (Celula)
           Celula.__init__(self, False)  # morto
         else:
           Celula.__init__(self, True)  # vivo

      # o metodo verifica o estado de vivo das células adjacentes e em outro momento
      # atualizaremos o estado da celula, via método atualizarEstado()
      def analisarVizinhos(self):
           # Dois estados vivo(1) e morto (0)
            vivos = self.getQteVizinhosEstado(True)
           # Regra 1
           if (self.getEstado() == True):
                if (vivos == 2 or vivos == 3):
                     self.setEstadoFuturo(True)
                # Caso contrário, morrerá por solidão ou superpopulação.
                else:
                     self.setEstadoFuturo(False)
           # Regra 2: 
           else:
                if (vivos == 3):
                     self.setEstadoFuturo(True)
                else:
                     self.setEstadoFuturo(False)

      def getColor(self):
           if self.getEstado() == True:
                return 'black'
           else:
                return 'white'

      # sobrescremevos o método getModelo para retornar o nome da classe de célula atual
      # outra forma seria por type(self).__name__
      def getModelo(self):
           return "Celula da Vida"

2) Modelo Voto de Vichniac
Estados possíveis: vivo - cor branca ou morto - cor preta.
Regra 1: Considerando o estado da célula e as celulas adjacentes, se possuir cincou ou mais células vivas, ela se manterá viva.
Regra 2: Introduza um pouco de instabilidade no modelo, alternando o próximo estado quando possuir quatro ou cinco células vivas, conforme a regra 1.

Composição de 36 interações do modelo voto de vichniac
A imagem será apresentada aqui.
Modelo voto de Vichniac com 3 interações
A imagem será apresentada aqui.
Modelo voto de Vichniac com 20 interações
A imagem será apresentada aqui.
Modelo voto de Vichniac com 35 interações
A imagem será apresentada aqui.

class CelulaVichniac(Celula):
    def __init__(self):
        rnd = SystemRandom()
        escolha = rnd.random()
        if escolha > 0.5:
            Celula.__init__(self, False)  # morto
        else:
            Celula.__init__(self, True)  # vivo

    def analisarVizinhos(self):
        vivos = self.getQteVizinhosEstado(True)
        if self.getEstado() == True:
            vivos = vivos + 1

        if vivos <= 4:
            self.setEstadoFuturo(False)
        else:  # vivos > 4
            self.setEstadoFuturo(True)
        if vivos == 4 | vivos == 5:
            self.setEstadoFuturo(not self.getEstadoFuturo())

    def getColor(self):
        if self.getEstado() == True:
            return 'black'
        else:
            return 'white'
    def getModelo(self):
        return "Celula Vichniac"

3) Modelo do Cérebro de Brian
Estados possíveis: desligado - cor branca, descansar- cor cinza e queimando - cor preta.
Regra 1: Se estiver no estado queimando, o próximo estado é descansar.
Regra 2: Se estiver descansando, o próximo estado é desligado.
Regra 3: Se estiver queimando e tiver exatamente dois vizinhos queimando, o estado se tornará queimando.

Composição de 20 interações do modelo brian brain
A imagem será apresentada aqui.
Modelo Brian Brain com 18 interações
A imagem será apresentada aqui.

class CelulaBrianBrain(Celula):
    def __init__(self):
        random = SystemRandom()
        # Dois estados queimando(2) descansando(1) e desligado(0)
        Celula.__init__(self, random.randint(0, 2))

    def analisarVizinhos(self):
        if self.getEstado() == 2:
            self.setEstadoFuturo(1)
        elif self.getEstado() == 1:
            self.setEstadoFuturo(0)
        elif self.getEstado() == 0:
            # getQteVizinhosEstado
            firing = self.getQteVizinhosEstado(2)
            if firing == 2:
                self.setEstadoFuturo(2)
        else:
            # mantem do jeito que esta
            self.setEstadoFuturo(self.getEstado())

    # para o estado firing: cor preta,resting cor cinza e desligado: branca
    def getColor(self):
        if self.getEstado() == 0:
            return 'white'
        elif self.getEstado() == 1:
            return 'gray'
        elif self.getEstado() == 2:
            return 'black'

    def getModelo(self):
        return "Celula Brian Brain"

4) Modelo de Formato de Onda
Esse modelo não utiliza um conjunto de estado discreto, por exemplo vivo ou morto.
De forma a simplificar seu comportamento utilizaremos uma escala de valores 0 a 255, a qual será apresentada graficamente em uma escala de cinza.
Regra 1: Se a média da soma dos estados das células vizinhas for 255, o próximo estado será 0.
Regra 2: Se a média da soma dos estados das células vizinhas for 0, o próximo estado será 255.
Regra 3: Caso contrário o próximo estado será a soma do estado da célula e da média dos estados das células vizinhas, menos o estado anterior.
Regra 4: O próximo estado com valor que extrapola o intervalo de 0 a 255, ficará limitado ao limite inferior (0) ou superior (255), conforme o caso.

class CelulaWave(Celula):
     def __init__(self, x, y):
         Celula.__init__(self, (x + y) * 14)
         self.__estadoAnterior = 0

     def analisarVizinhos(self):
         total = 0
         if self.contaVizinhos == 0:
            exit

         vizinhos = self.getVizinhos() 
         for vizinho in vizinhos:
            total = total + vizinho.getEstado()
        media = float(int( total / 8))
        #Regra 1
        if media > 255:
            self.setEstadoFuturo(0)
        # Regra 2
        elif media == 0:
            self.setEstadoFuturo(255)
        else:
            self.setEstadoFuturo(self.getEstado() + media)
            if (self.__estadoAnterior > 0):
                self.setEstadoFuturo(self.getEstadoFuturo() - self.__estadoAnterior)
            if (self.getEstadoFuturo() > 255):
                self.setEstadoFuturo(255)
            elif self.getEstadoFuturo() < 0:
                self.setEstadoFuturo(0)
        self.__estadoAnterior = self.getEstado()

    def getColor(self):
        nivel = str(format(int(self.getEstado()), '02X'))
        return '#'+ str(nivel) + str(nivel)+str(nivel)

    def getModelo(self):
        return "Celula Formato Onda"

Composição de 70 interações do modelo formato de onda
A imagem será apresentada aqui.
Modelo formato de Onda com 6 interações
A imagem será apresentada aqui.
Modelo formato de Onda com 26 interações
A imagem será apresentada aqui.
Modelo formato de Onda com 49 interações
A imagem será apresentada aqui.
Modelo formato de Onda com 69 interações
A imagem será apresentada aqui.

3) crie um CA que contenha uma matriz de células de acordo com o modelo desejado.
4) Permita ainda, criar uma imagem (.jpeg) do CA de acordo os estados de suas células, em uma simbologia gráfica (cada cor representa um estado). Indique ainda quantas interações entre as células ocorreram para chegar ao estado apresentado.

class CelularAutomata:
    # metodo que cria a classe CelularAutomata, o parâmetro modelo definirá qual classe de células será utilizada
    def __init__(self, modelo, linhas, colunas):
        self.__linhas = linhas
        self.__colunas = colunas
        self.__modelo = modelo
        # quantas vezes o automata celular foi atualizado
        self.__interacao = 0

        # criar a matriz de celulas de acordo com o modelo
        if modelo == 0:
            self.__matrix = [[CelulaGOL() for x in range(colunas)] for y in range(linhas)]
        elif modelo == 1:
            self.__matrix = [[CelulaVichniac() for x in range(colunas)] for y in range(linhas)]
            # vou consolidar um pouco mais a maioria
            for i in range(linhas):
                for j in range(colunas):
                    if i >26 or  i < 7:
                        self.__matrix[i][j].setEstado(1)
        elif modelo == 2:
            self.__matrix = [[CelulaBrianBrain() for x in range(colunas)] for y in range(linhas)]
        elif modelo == 3:
            self.__matrix = [[CelulaWave(x / self.__colunas, y/ self.__linhas) for x in range(colunas)] for y in range(linhas)]
        else:
            # erro sair
            quit
        # Referenciar os vizinhos
        for i in range(linhas):
            for j in range(colunas):
                # Adicionar para cada celula as referencias dos oito vizinhos
                if (i > 0 and j > 0):
                    self.__matrix[i][j].AddVizinho(self.__matrix[i - 1][j - 1])
                if (i > 0):
                    self.__matrix[i][j].AddVizinho(self.__matrix[i - 1][j])
                if (i > 0 and j < colunas - 1):
                    self.__matrix[i][j].AddVizinho(self.__matrix[i - 1][j + 1])
                if (j > 0):
                    self.__matrix[i][j].AddVizinho(self.__matrix[i][j - 1])
                if (j < colunas - 1):
                    self.__matrix[i][j].AddVizinho(self.__matrix[i][j + 1])
                if (i < linhas - 1 and j > 0):
                    self.__matrix[i][j].AddVizinho(self.__matrix[i + 1][j - 1])
                if (i < linhas - 1):
                    self.__matrix[i][j].AddVizinho(self.__matrix[i + 1][j])
                if (i < linhas - 1 and j < colunas - 1):
                    self.__matrix[i][j].AddVizinho(self.__matrix[i + 1][j + 1])

    # retorna a celula na posicao i,j
    def getCelula(self, i, j):
        return self.__matrix[i][j]

    # método que processa os estados das celulas vizinhas para definir o proximo estado de cada celula
    def analisaCA(self):
        for i in range(self.__linhas):
            for j in range(self.__colunas):
                self.__matrix[i][j].analisarVizinhos()

    # método que atualiza o estado de cada celula com base em seu estado futuro
    def atualizaCA(self):
        for i in range(self.__linhas):
            for j in range(self.__colunas):
                self.__matrix[i][j].atualizarEstado()
        self.__interacao = self.__interacao + 1

    # método que conta a quantidade de celulas que possui determinado estado
    # utilizarei para parar a criacao de imagens, quanto todas as celulas estiver mortas
    def contaCelulasEstado(self, estado):
        qte = 0
        for i in range(self.__linhas):
            for j in range(self.__colunas):
                if self.__matrix[i][j].getEstado() == estado:
                    qte = qte + 1
        return qte

    #verifica se há no CA celulas que permitam interacao (nao mortas)
    # True - todas as células morreram
    def morreu(self):
        if self.__modelo != 3:
            if self.contaCelulasEstado(0) == self.__linhas*self.__colunas:
                return True
            else:
                return False
        else:
            soma = 0
            for i in range(self.__linhas):
                for j in range(self.__colunas):
                    soma = soma + self.__matrix[i][j].getEstado()
            if soma == 0:
                return True
            else:
                return False

    def getImage(self, legenda):
        # diametro do circulo a ser inserido na imagem, igual ao da referencia Generative Art.
        tam = 10
        img = Image.new('RGB', (tam * self.__linhas, tam * self.__colunas), color='white')
        d = ImageDraw.Draw(img)

        for i in range(self.__linhas):
            for j in range(self.__colunas):
                # o metodo ImageColor.getrgb transforma o texto em um objeto color (r,g,b)
                d.ellipse((tam * i, tam * j, tam * (i + 1), tam * (j + 1)),
                  fill=ImageColor.getrgb(self.__matrix[i][j].getColor()), outline=(0, 0, 0))

        if legenda > 0:
            msg = "Modelo: " + self.__matrix[i][j].getModelo() + " interacao: " + str(self.__interacao)
            # inserindo texto na imagem, posicao (1,1), cor amarela
            d.text((1, 1), msg, fill=(255, 255, 0))
        return img

Agora integrando tudo com método crIaImagens

# cria para o modelo de automata celular indicado por m, as imagens para interacoes pedidas
def criaImagens(diretorio, m, interacoes):
    if m > 3:
        print('Modelo nao previsto')
        exit()
    ca = CelularAutomata(m, 50, 30)
    # tenho que padronizar a quantidade de digitos a ser escrita no nome do arquivo
    # para ordenar
    tam = len(str(interacoes))
    indice = ''
    for i in range(tam):
        indice = indice + str(0)
    ca.getImage(1).save(diretorio + 'img_' + str(m) + '_' + indice + '.jpeg')
    # faca n  mudancas de estado e salve a imagem de cada uma
    for i in range(interacoes):
        # ao analisar e atualizar, acontece uma interacao
        ca.analisaCA()
        ca.atualizaCA()
        indice = ''
        for j in range(tam - len(str(i + 1))):
            indice = indice + str(0)
        indice = indice + str(i + 1)
        ca.getImage(1).save(diretorio + 'img_' + str(m) + '_' + indice + '.jpeg')
        # se todo o CA morreu, terminar
        if ca.morreu() == True: break

if __name__ != "__main__":
    diretorio = '/Users/philippe/PycharmProjects/'
    for m in range(4):
        if m == 0:
            # GOL 100 iteracoes
            criaImagens(diretorio, m, 100)
        elif m == 1:
            #  Vichniac, 166 iteracoes
            criaImagens(diretorio, m, 166)
        elif m == 2:
            #  Brian Brain, 20 iteracoes
            criaImagens(diretorio, m, 20)
        elif m == 3:
            #  Onda, 70 iteracoes
            criaImagens(diretorio, m, 70)
0 votos
respondida Set 14 por Felipe Yudi (46 pontos)  
editado Set 14 por Felipe Yudi

Olá Philippe. Muito interessante sua resposta. Não tenho familiaridade com a biblioteca pillow, então foi bom conhecê-la. As células ficam mais bonitas e mais parecidas com as do livro.

Aqui está uma implementação (mais simples e mais feia também) usando Matplotlib e listas do Python.
A única foi no voto de Vichniac. Acho que a explicação do livro está bem ruim, mas a inversão da regra que o autor faz para 4 ou 5 vizinhos deveria acrescentar alguma instabilidade nas bordas e não fazer com que os clusters desaparecessem. Acho que uma das fontes do problema é que o autor fala em mudar a regra no caso de 4 ou 5 vizinhos, mas no código ele parece contar a própria célula como um vizinho.

Não consegui postar gifs aqui, mas pode encontá-los neste link.

import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.animation as animation
import random

class Cell(object):
    """
    Classe que gera as celulas para o game of life, 
    vichniac vote, brians brain e wave.
    """
    def __init__(self):
        """
        Uma celula comeca morta
        """
        self.status = 0
        #Guarda o estado anterior da 
        #celula. Sera usado em wave.
        self.states = []

    def killcell(self):
        """
        Mata a celula
        """
        self.status = 0

    def revivecell(self):
        """
        Ressuscita a celula
        """
        self.status = 1

    def is_alive(self):
        """
        Retorna True se a celula 
        esta viva
        """
        if self.status == 1:
            return True
        else:
            return False

    def swapstate(self):
        """
        Inverte o estado da celula. Usado 
        no vichniac vote
        """
        if self.status == 1:
            self.status = 0
        else: 
            self.status = 1

    def restcell(self):
        """
        Coloca a celula para descancar. 
        Usamos 0.5 para o esquema de 
        cores ficar melhor. Usado no 
        brians brain
        """
        self.status = 0.5

    def set_status(self, new_state):
        """
        Muda o estado da celula para 
        new_state. Usado em wave.
        """
        self.status = new_state

    def cell_states(self):
        """
        Guarda o estado anterior e o estado inicial 
        da celula. Usado em wave.
        """
        self.states.append(self.status)
        if len(self.states) > 2:
            self.states = self.states[-2:]
        return self.states

    def get_old_status(self):
        return self.states[0]

    def get_state(self):
        """
        Retorna o status da celula
        """
        return self.status        

class Board(object):
    """
    Esta classe contem os metodos para gerar tabuleiros 
    para o game of life, vichniac vote e wave. Tambem 
    gera um cerebro para o brians brain.
    """
    def __init__(self, rows, columns):
        self.rows = rows
        self.columns = columns
        #Coloca as ceculas no grid.
        self.grid = [[Cell() for col in range(self.columns)] for row in range(self.rows)]

    def plot_board(self):
        '''
        Retorna um array de arrays contendo os 
        estados das celulas
        '''
        #Talvez nao de pra fazer isso. Em vez disso, gere um funcao que 
        #retorne os arrays. Temos que usar o return_state()
        #plt.imshow(self.grid, cmap='Greys', interpolation='nearest')
        self.new_array = []
        for row in self.grid:
            new_row = []
            for cell in row:
                new_row.append(cell.get_state())
            self.new_array.append(new_row) 
        return self.new_array

    def gen_initial_board(self):
        '''
        Inicia um tabuleiro aleatoriamente
        '''
        for row in self.grid:
            for cell in row:
                state = random.randint(0, 1)
                if state == 1:
                    cell.revivecell()
        return self.grid

    def get_neighbours(self):
        """
        Retorna um array de arrays 
        atualizado com os numero de 
        vizinhos vivos.
        """
        self.next_list_gol = []
        for row in range(len(self.grid)):
            self.next_cell_gol = []
            for item in range(len(self.grid[0])):
                """
                a = acima, b = direito, c = abaixo, d = esquerdo
                e = nordeste, f = sudeste, g = sudoeste, h = noroeste
                """
                self.a = self.grid[row-1][item].get_state()

                try:
                    self.b = self.grid[row][item+1].get_state()
                except IndexError:
                    self.b = 0

                try:
                    self.c = self.grid[row+1][item].get_state()
                except IndexError:
                    self.c = 0

                self.d = self.grid[row][item-1].get_state()

                try:
                    self.e = self.grid[row-1][item+1].get_state()
                except IndexError:
                    self.e = 0
                try:
                    self.f = self.grid[row+1][item+1].get_state()
                except IndexError:
                    self.f = 0
                try:
                    self.g = self.grid[row+1][item-1].get_state()
                except IndexError:
                    self.g = 0

                self.h = self.grid[row-1][item-1].get_state()

                #Bordas
                if row == 0:
                    self.a, self.e, self.h = 0, 0, 0
                if row == len(self.grid)-1:
                    self.c, self.f, self.g = 0, 0, 0
                if item == 0:
                    self.d, self.g, self.h = 0, 0, 0
                if item == len(self.grid[row])-1:
                    self.b, self.e, self.f = 0, 0, 0

                self.next_cell_gol.append(self.a+self.b+self.c+self.d+self.e+self.f+self.g+self.h)

            self.next_list_gol.append(self.next_cell_gol)
        return self.next_list_gol

    def game_of_life(self):
        """
        Modifica o grid baseado em next_list conforme 
        as regras do game of life
        """
        for row1, row2 in zip(self.grid, self.next_list_gol):
            for cell1, cell2 in zip(row1, row2):
                if cell1.is_alive():
                    if cell2 <=1:
                        cell1.killcell()
                    elif cell2 >= 4:
                        cell1.killcell()
                elif not cell1.is_alive() and cell2 == 3:
                    cell1.revivecell()  
        return self.grid

    def vichniac_vote(self):
        """
        Modifica o grid baseado em next_list conforme 
        as regras do vichniac vote
        """
        for row1, row2 in zip(self.grid, self.next_list_gol):
            for cell1, cell2 in zip(row1, row2):
                if cell1.is_alive(): 
                    liveCount = cell2+1
                else: 
                    liveCount = cell2 

                if  liveCount <= 4:
                    cell1.killcell()
                elif liveCount > 4:
                    cell1.revivecell()

                if liveCount == 4 or liveCount == 5:
                    cell1.swapstate()

        return self.grid


    def gen_initial_brain(self):
        '''
        Inicia um cerebro aleatoriamente.
        '''
        possible_brain_states = [0, 0.5, 1]
        for row in self.grid:
            for cell in row:
                state = random.choice(possible_brain_states)
                if state == 1:
                    cell.revivecell()
                elif state == 0.5:
                    cell.restcell()
        return self.grid

    def get_firing(self):
        """
        Retorna um array de arrays 
        atualizado com os vizinhos 
        em atividade.
        """
        self.next_list_bb = []
        for row in range(len(self.grid)):
            self.next_cell_bb = []
            for item in range(len(self.grid[0])):
                """
                a = acima, b = direito, c = abaixo, d = esquerdo
                e = nordeste, f = sudeste, g = sudoeste, h = noroeste
                """
                if self.grid[row-1][item].get_state() == 1:
                    self.a = 1
                else:
                    self.a = 0

                try:
                    if self.grid[row][item+1].get_state() == 1:
                        self.b = 1
                    else:
                        self.b = 0
                except IndexError:
                    self.b = 0

                try:
                    if self.grid[row+1][item].get_state() == 1:
                        self.c = 1 
                    else: 
                        self.c = 0
                except IndexError:
                    self.c = 0

                if self.grid[row][item-1].get_state() == 1:
                    self.d = 1
                else:
                    self.d = 0

                try:
                    if self.grid[row-1][item+1].get_state() == 1:
                        self.e = 1
                    else:
                        self.e = 0
                except IndexError:
                    self.e = 0
                try:
                    if self.grid[row+1][item+1].get_state() == 1:
                        self.f = 1
                    else:
                        self.f = 0
                except IndexError:
                    self.f = 0

                try:
                    if self.grid[row+1][item-1].get_state() == 1:
                        self.g = 1
                    else:
                        self.g = 0
                except IndexError:
                    self.g = 0

                if self.grid[row-1][item-1].get_state() == 1:
                    self.h = 1
                else:
                    self.h = 0

                #Bordas
                if row == 0:
                    self.a, self.e, self.h = 0, 0, 0
                if row == len(self.grid)-1:
                    self.c, self.f, self.g = 0, 0, 0
                if item == 0:
                    self.d, self.g, self.h = 0, 0, 0
                if item == len(self.grid[row])-1:
                    self.b, self.e, self.f = 0, 0, 0

                self.next_cell_bb.append(self.a+self.b+self.c+self.d+self.e+self.f+self.g+self.h)

            self.next_list_bb.append(self.next_cell_bb)
        return self.next_list_bb

    def brians_brain(self):
        """
        Modifica o cerebro baseado nas 
        regras do brians brain.
        """
        for row1, row2 in zip(self.grid, self.next_list_bb):
            for cell1, cell2 in zip(row1, row2):
                if cell1.get_state() == 1:
                    cell1.restcell()
                elif cell1.get_state() == 0.5:
                    cell1.killcell()
                elif cell2 == 2 and cell1.get_state() == 0:
                    cell1.revivecell()
        return self.grid

    def get_initial_pond(self):
        """
        Gera um "lago" inicial para wave.
        """
        possible_states = list(range(0, 256))
        for row in self.grid:
            for cell in row:
                cell.cell_states()
                state = random.choice(possible_states)
                cell.set_status(state)
                cell.cell_states()

        return self.grid

    def wave(self): 
        """
        Muda o lago de acordo com as 
        regras de wave.
        """
        for row1, row2 in zip(self.grid, self.next_list_gol):
            for cell1, cell2 in zip(row1, row2):
                if cell2//8 == 255:
                    cell1.set_status(0)
                elif cell2//8 == 0: 
                    cell1.set_status(255)
                else: 
                    cell1.set_status(cell1.get_state() + cell2//8 - cell1.get_old_status())
                    if cell1.get_state() > 255: 
                        cell1.set_status(255)
                    elif cell1.get_state() < 0:
                        cell1.set_status(0)
                cell1.cell_states()

        #Truque sujo para manter a escala.
        self.grid[0][0].set_status(0)
        self.grid[0][1].set_status(255)

        return self.grid

def animate_game_of_life(row, col):
    """
    Anima o game of life
    """
    fig = plt.figure()
    a = Board(row, col)
    a.gen_initial_board() 
    data = a.plot_board()
    ax = sns.heatmap(data, cmap = "Greys")
    #print(data)

    def init():
        plt.clf()
        ax = sns.heatmap(data, cmap = "Greys")

    def animate(i):
        plt.clf()
        a.get_neighbours()
        a.game_of_life()
        data = a.plot_board()
        ax = sns.heatmap(data, cmap = "Greys")

    anim = animation.FuncAnimation(fig, animate, init_func=init, interval=250)
    plt.show()

def animate_vichniac_vote(row, col):
    """
    Anima o vichniac vote
    """
    fig = plt.figure()
    a = Board(row, col)
    a.gen_initial_board() 
    data = a.plot_board()
    ax = sns.heatmap(data, cmap = "Greys")

    def init():
        plt.clf()
        ax = sns.heatmap(data, cmap = "Greys")

    def animate(i):
        plt.clf()
        a.get_neighbours()
        a.vichniac_vote()
        data = a.plot_board()
        ax = sns.heatmap(data, cmap = "Greys")

    anim = animation.FuncAnimation(fig, animate, init_func=init, interval=250)
    plt.show()

def animate_brians_brain(row, col):
    """
    Anima o brians brain
    """
    fig = plt.figure()
    a = Board(row, col)
    a.gen_initial_brain() 
    data = a.plot_board()
    ax = sns.heatmap(data, cmap = "Greys")

    def init():
        plt.clf()
        ax = sns.heatmap(data, cmap = "Greys")

    def animate(i):
        plt.clf()
        a.get_firing()
        a.brians_brain()
        data = a.plot_board()
        ax = sns.heatmap(data, cmap = "Greys")

    anim = animation.FuncAnimation(fig, animate, init_func=init, interval=250)
    plt.show()

def animate_wave(row, col):
    """
    Anima o wave
    """
    fig = plt.figure()
    a = Board(row, col)
    a.get_initial_pond() 
    data = a.plot_board()
    ax = sns.heatmap(data, cmap = "Greys")

    def init():
        plt.clf()
        ax = sns.heatmap(data, cmap = "Greys")

    def animate(i):
        plt.clf()
        a.get_neighbours()
        a.wave()
        data = a.plot_board()
        ax = sns.heatmap(data, cmap = "Greys")

    anim = animation.FuncAnimation(fig, animate, init_func=init, interval=250)
    plt.show()    

if __name__ == "__main__":
    #Use um da cada vez.
    #animate_game_of_life(100, 100)
    #animate_vichniac_vote(100, 100)
    #animate_brians_brain(100, 100)
    #animate_wave(100, 100)
...