associação pythonbrasil[11] django zope/plone planet Início Logado como (Entrar)

Diferenças para "GameOfLifeConway"

Diferenças entre as versões de 1 e 5 (4 versões de distância)
Revisão 1e 2004-05-03 13:45:12
Tamanho: 3824
Comentário:
Revisão 5e 2004-07-07 16:15:34
Tamanho: 6896
Comentário:
Deleções são marcadas assim. Adições são marcadas assim.
Linha 6: Linha 6:
Estou usando essa classe para implementar uma [http://www.geocities.com/rodviking/game.gif versão gráfica] do Life usando PyQt e Numarray. Caso tenha interesse nesse código também, contate-me através do link no fim da página :) Estou usando essa classe para implementar uma [http://www20.brinkster.com/rodviking/game.gif versão gráfica] do Life usando PyQt e Numarray. Caso tenha interesse nesse código também, contate-me através do link no fim da página :)
Linha 19: Linha 19:
    def __init__(self, rule=None):
        """
        Rule is a string with the format ss/dd where ss are digits
        in 1..8 representing how many neighbours a cell must have
        to stay alive, and dd are digits in 1..8 representing how
        many neighbours a dead cell must have to become alive.
        
        For example, the default conway rule (which is used if no
        string is passed as parameter) is "23/3", which means a cell
        stay alive if it has 2 or 3 neighbours, and becomes alive if
        it has 3 neighbours.
        """
        if not rule:
            #if not rule is passed, use the default one
            self.rule = "23/3"
        else:
            self.rule = rule
Linha 20: Linha 37:
    def countNeighbours (self, x, y):     def countNeighbours (self, row, col):
        """
        countNeighbours (row, col)
        
        Returns a number between 0 and 8, counting how many
        'living' neighbours (=1)
        a cell specified by x,y has.
        """
Linha 22: Linha 46:

        #count the living neighbours (=1) on the row above
        if row > 0:
            total += self.world[row-1, col]
            if col > 0:
                total += self.world[row-1, col-1]
            if col < self.num_cols - 1:
                total += self.world[row-1, col+1]

        #count the living neighbours on the left and right
        if col > 0:
            total += self.world[row, col-1]
        if col < self.num_cols - 1:
            total += self.world[row, col+1]

        #count the living neighbours on the row below
        if row < self.num_rows - 1:
            total += self.world[row+1, col]
            if col > 0:
                total += self.world[row+1, col-1]
            if col < self.num_cols - 1:
                total += self.world[row+1, col+1]
        return total

    def resizeGrid (self, world, rows, cols, centralizeData=True):
        """
        resizeGrid (world, rows, cols, centralizeData=True)
Linha 23: Linha 74:
        #count the living neighbours (=1) on the row above
        if x > 0:
            total += self.world[x-1, y]
            if y > 0:
                total += self.world[x-1, y-1]
            if y < self.num_cols - 1:
                total += self.world[x-1, y+1]
        Resize the matrix passed on 'world'.
        If the new values of 'rows' and 'cols' are smaller than
        the current matrix, the matrix is trimmed down starting
        from the last rows and columns.
Linha 31: Linha 79:
        #count the living neighbours on the left and right
        if y > 0:
            total += self.world[x, y-1]
        if y < self.num_cols - 1:
            total += self.world[x, y+1]
        If centralizeData=True, any eventual data present in the
        matrix will be placed centralized in the new matrix, for
        example
        [[1,1],
         [1,1]]
        when resized to 4 rows and cols would become
        [[0,0,0,0],
         [0,1,1,0],
         [0,1,1,0],
         [0,0,0,0]]
Linha 37: Linha 90:
        #count the living neighbours on the row below
        if x < self.num_rows - 1:
            total += self.world[x+1, y]
            if y > 0:
                total += self.world[x+1, y-1]
            if y < self.num_cols - 1:
                total += self.world[x+1, y+1]
        return total
        this method is used for example to "zoom in" and "zoom
        out" the matrix on graphical clients.
        """
        num_rows = len(world)
        num_cols = len(world[0])
        new_world = zeros([rows, cols])

        #if it's centered, calculates the row/col offset to be used
        row_offset = 0
        col_offset = 0
        if centralizeData and rows > num_rows:
            row_offset = (rows/2) - (num_rows/2)
        if centralizeData and cols > num_cols:
            col_offset = (cols/2) - (num_cols/2)

        #copy the data
        for row in range(num_rows):
            for col in range(num_cols):
                if (row+row_offset) < len(new_world) and \
                   (col+col_offset) < len(new_world[0]):
                    new_world[row+row_offset, col+col_offset] = \
                               world[row, col]
        return new_world
Linha 46: Linha 114:
    def emptyGrid (self):
        return zeros([self.num_rows, self.num_cols])
    
    def printGrid (self, world):
        print world
    def playLife (self, world, debug=False):
        """
        playLife (world, debug=False)
Linha 52: Linha 118:
    def playLife (self, world, debug=False):         This is the main method for the class.
        It will return a matrix which is the result of the game
        rules (defined by rule attribute) applied on the 'world'
        matrix.
        
        If debug=True, the resulting matrix is also printed on
        the console.
        """
Linha 56: Linha 129:
        
Linha 59: Linha 132:
            self.printGrid(world)
    
        result = self.emptyGrid()
            print world

        result = zeros([self.num_rows, self.num_cols])
Linha 65: Linha 138:
    
Linha 68: Linha 141:
            self.printGrid(result)             print result
Linha 70: Linha 143:
              def applyRules (self, x, y):
        # - Rule 1: if the cell is "alive" (1), it stays
        # alive only if it has 2 or 3 neighbours ('if' block)
        # - Rule 2: if a cell is "dead" (0), it becomes
        # alive if it has exactly 3 neighbours ('else' block)


    def applyRules (self, row, col):
        """
        applyRules (row, col)
        
        Apply the rule defined on self.rule for the cell(row,col)
        in the matrix self.world. This method is usually not
        called directly, but called through playLife.
        """
        survival_rule = [int(c) for c in self.rule[:self.rule.find("/")]]
        birth_rule = [int(c) for c in self.rule[self.rule.find("/")+1:]]
Linha 78: Linha 156:
        sum_neighb = self.countNeighbours(x,y)
        if self.world[x,y] == 1:
            if 2 <= sum_neighb <= 3:
        sum_neighb = self.countNeighbours(row,col)
        if self.world[row,col] == 1:
            if sum_neighb in survival_rule:
Linha 82: Linha 160:
        else: 
            if sum_neighb == 3:
        else:
            if sum_neighb in birth_rule:
Linha 89: Linha 167:
    testworld = array([[0, 0, 0, 0, 0, 0, 0, 0],
                        [1, 1, 1, 0, 0, 0, 0, 0],
                        [0, 0, 0, 0, 0, 0, 0, 0],
                        [0, 0, 0, 0, 0, 0, 0, 1],
                        [0, 0, 0, 0, 0, 0, 1, 1]])
    testworld = array([[1, 0, 1, 0, 0, 0, 0, 0],
                        [0, 1, 1, 0, 0, 0, 0, 0],
                        [0, 1, 0, 0, 0, 0, 0, 0],
                        [0, 0, 0, 0, 0, 0, 0, 0],
                        [0, 0, 0, 0, 0, 0, 0, 0]])
Linha 97: Linha 175:
    for i in range(2):     for i in range(1,5):
Linha 101: Linha 179:
                     print "-" * 20
    print "This test matrix had a glider on the top corner, that moved"
    print "down and right after 4 iterations, as you'd expect a"
    print "glider to do :)"

Game of Life, de Conway

Essa classe foi criada para executar o [http://www.math.com/students/wonders/life/life.html Game of Life] em uma matriz de 0s e 1s, onde 1 representa uma célula "viva". Já existe código para o Game of Life em Python (bem, em qualquer linguagem que você pensar, pois é um problema matemático clássico) mas eu estou querendo desenvolver por conta própria e ver quão longe eu chego, principalmente em termos de desempenho e funcionalidade.

Estou usando essa classe para implementar uma [http://www20.brinkster.com/rodviking/game.gif versão gráfica] do Life usando PyQt e Numarray. Caso tenha interesse nesse código também, contate-me através do link no fim da página :)

Por enquanto o código não está otimizado pois meu enfoque é primeiro ter meu programa funcionando pra depois melhorar o desempenho (que está razoável), mas estou aberto a sugestões, em especial nos métodos "applyRules" e "countNeighbours" que é onde estão os "gargalos".

Em breve essa classe também irá suportar o carregamento de arquivos de "pattern" (extensão .lif), que inclui regras dinâmicas.

Código

   1 from numarray import *
   2 
   3 class LifeGame:
   4     def __init__(self, rule=None):
   5         """
   6         Rule is a string with the format ss/dd where ss are digits
   7         in 1..8 representing how many neighbours a cell must have
   8         to stay alive, and dd are digits in 1..8 representing how
   9         many neighbours a dead cell must have to become alive.
  10         
  11         For example, the default conway rule (which is used if no
  12         string is passed as parameter) is "23/3", which means a cell
  13         stay alive if it has 2 or 3 neighbours, and becomes alive if
  14         it has 3 neighbours.
  15         """
  16         if not rule:
  17             #if not rule is passed, use the default one
  18             self.rule = "23/3"
  19         else:
  20             self.rule = rule
  21 
  22     def countNeighbours (self, row, col):
  23         """
  24         countNeighbours (row, col)
  25         
  26         Returns a number between 0 and 8, counting how many
  27         'living' neighbours (=1)
  28         a cell specified by x,y has.
  29         """
  30         total = 0
  31 
  32         #count the living neighbours (=1) on the row above
  33         if row > 0:
  34             total += self.world[row-1, col]
  35             if col > 0:
  36                 total += self.world[row-1, col-1]
  37             if col < self.num_cols - 1:
  38                 total += self.world[row-1, col+1]
  39 
  40         #count the living neighbours on the left and right
  41         if col > 0:
  42             total += self.world[row, col-1]
  43         if col < self.num_cols - 1:
  44             total += self.world[row, col+1]
  45 
  46         #count the living neighbours on the row below
  47         if row < self.num_rows - 1:
  48             total += self.world[row+1, col]
  49             if col > 0:
  50                 total += self.world[row+1, col-1]
  51             if col < self.num_cols - 1:
  52                 total += self.world[row+1, col+1]
  53         return total
  54 
  55     def resizeGrid (self, world, rows, cols, centralizeData=True):
  56         """
  57         resizeGrid (world, rows, cols, centralizeData=True)        
  58         
  59         Resize the matrix passed on 'world'.
  60         If the new values of 'rows' and 'cols' are smaller than
  61         the current matrix, the matrix is trimmed down starting
  62         from the last rows and columns.
  63         
  64         If centralizeData=True, any eventual data present in the
  65         matrix will be placed centralized in the new matrix, for
  66         example
  67         [[1,1],
  68          [1,1]]
  69         when resized to 4 rows and cols would become
  70         [[0,0,0,0],
  71          [0,1,1,0],
  72          [0,1,1,0],
  73          [0,0,0,0]]
  74         
  75         this method is used for example to "zoom in" and "zoom
  76         out" the matrix on graphical clients.
  77         """
  78         num_rows = len(world)
  79         num_cols = len(world[0])
  80         new_world = zeros([rows, cols])
  81 
  82         #if it's centered, calculates the row/col offset to be used
  83         row_offset = 0
  84         col_offset = 0
  85         if centralizeData and rows > num_rows:
  86             row_offset = (rows/2) - (num_rows/2)
  87         if centralizeData and cols > num_cols:
  88             col_offset = (cols/2) - (num_cols/2)
  89 
  90         #copy the data
  91         for row in range(num_rows):
  92             for col in range(num_cols):
  93                 if (row+row_offset) < len(new_world) and \
  94                    (col+col_offset) < len(new_world[0]):
  95                     new_world[row+row_offset, col+col_offset] = \
  96                                world[row, col]
  97         return new_world
  98     
  99     def playLife (self, world, debug=False):
 100         """
 101         playLife (world, debug=False)
 102         
 103         This is the main method for the class.
 104         It will return a matrix which is the result of the game
 105         rules  (defined by rule attribute) applied on the 'world'
 106         matrix.
 107         
 108         If debug=True, the resulting matrix is also printed on
 109         the console.
 110         """
 111         self.world = world
 112         self.num_rows = len(world)
 113         self.num_cols = len(world[0])
 114 
 115         if debug:
 116             print "Original grid:"
 117             print world
 118 
 119         result = zeros([self.num_rows, self.num_cols])
 120         for row in range(self.num_rows):
 121             for col in range(self.num_cols):
 122                 result [row, col] = self.applyRules(row, col)
 123 
 124         if debug:
 125             print "Result grid:"
 126             print result
 127         return result
 128 
 129 
 130     def applyRules (self, row, col):
 131         """
 132         applyRules (row, col)
 133         
 134         Apply the rule defined on self.rule for the cell(row,col)
 135         in the matrix self.world. This method is usually not
 136         called directly, but called through playLife.
 137         """
 138         survival_rule = [int(c) for c in self.rule[:self.rule.find("/")]]
 139         birth_rule = [int(c) for c in self.rule[self.rule.find("/")+1:]]
 140         cell = 0
 141         sum_neighb = self.countNeighbours(row,col)
 142         if self.world[row,col] == 1:
 143             if sum_neighb in survival_rule:
 144                 cell = 1
 145         else:
 146             if sum_neighb in birth_rule:
 147                 cell = 1
 148         return cell
 149 
 150 if __name__ == '__main__':
 151     #initialize grids
 152     testworld  = array([[1, 0, 1, 0, 0, 0, 0, 0],
 153                         [0, 1, 1, 0, 0, 0, 0, 0],
 154                         [0, 1, 0, 0, 0, 0, 0, 0],
 155                         [0, 0, 0, 0, 0, 0, 0, 0],
 156                         [0, 0, 0, 0, 0, 0, 0, 0]])
 157 
 158     #play Life!
 159     lg = LifeGame()
 160     for i in range(1,5):
 161         print "-" * 20
 162         print "Iteration", i
 163         testworld = lg.playLife(testworld, True)
 164     print "-" * 20
 165     print "This test matrix had a glider on the top corner, that moved"
 166     print "down and right after 4 iterations, as you'd expect a"
 167     print "glider to do :)"                

Volta para CookBook.


RodrigoVieira