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