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

Diferenças para "GameOfLifeConway"

Diferenças entre as versões de 3 e 4
Revisão 3e 2004-05-03 14:31:32
Tamanho: 3826
Comentário:
Revisão 4e 2004-05-04 14:44:15
Tamanho: 6802
Comentário:
Deleções são marcadas assim. Adições são marcadas assim.
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 36:
    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 44:

        #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 72:
        #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 77:
        #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 87:
        #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 109:
    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 113:
    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 122:
        
Linha 59: Linha 125:
            self.printGrid(world)
    
        result = self.emptyGrid()
            print world

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

Volta para CookBook.


RodrigoVieira