8000 Implemented Conway's Game of Life with classic "glider" example (#62) · CO18326/python@4652400 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4652400

Browse files
authored
Implemented Conway's Game of Life with classic "glider" example (AllAlgorithms#62)
Implemented Conway's Game of Life with classic "glider" example
2 parents e1caff4 + ae241cd commit 4652400

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
"""
2+
Conway's Game of Life
3+
"""
4+
import itertools
5+
6+
class GameOfLife:
7+
@classmethod
8+
def dead_grid(cls, *, height=None, width=None):
9+
return [
10+
[Dead() for _cols in range(width)]
11+
for _rows in range(height)
12+
]
13+
14+
@classmethod
15+
def from_str(cls, string):
16+
non_empty_lines = (
17+
line for line in string.splitlines()
18+
if len(line) > 0
19+
)
20+
parsed_grid = [
21+
[Cell.from_str(char) for char in line]
22+
for line in non_empty_lines
23+
]
24+
return cls(grid=parsed_grid)
25+
26+
def __init__(self, grid=None):
27+
self.grid = grid
28+
self.height = len(grid)
29+
self.width = len(grid[0])
30+
31+
32+
def __str__(self):
33+
return '\n'.join(
34+
''.join(str(cell) for cell in row)
35+
for row in self.grid
36+
)
37+
38+
def next_generation(self):
39+
next_grid = [
40+
[
41+
cell.next_state(neighbor_count)
42+
for cell, neighbor_count in row
43+
]
44+
for row in self.grid_with_live_neighbor_counts()
45+
]
46+
return GameOfLife(grid=next_grid)
47+
48+
def grid_with_live_neighbor_counts(self):
49+
'''
50+
Returns an iterator of grid rows in which each element
51+
is a tuple containing the cell and the count of living neighbors
52+
adjacent to that cell.
53+
E.g. [[(Live, 0), (Dead, 3), ...], ...]
54+
'''
55+
return (
56+
(
57+
(cell, self.count_live_neighbors(row, col))
58+
for (row, col), cell in coordinated_row
59+
)
60+
for coordinated_row in self.coordinate()
61+
)
62+
63+
def coordinate(self):
64+
'''
65+
Returns an iterator of grid rows in which each element
66+
is a tuple containg the coordinates and the content of the grid
67+
at those coordinates.
68+
E.g. [[((0, 0), Live), ((0, 1), Dead), ...], ...]
69+
'''
70+
return (
71+
(
72+
((row_index, col_index), cell)
73+
for col_index, cell in enumerate(row)
74+
)
75+
for row_index, row in enumerate(self.grid)
76+
)
77+
78+
def count_live_neighbors(self, row, col):
79+
directions_1D = (-1, 0, 1)
80+
directions_2D = itertools.product(directions_1D, directions_1D)
81+
neighbor_coords = (
82+
(row + d_row, col + d_col)
83+
for (d_row, d_col) in directions_2D
84+
if (d_row, d_col) != (0, 0)
85+
)
86+
87+
def is_coord_alive(coord):
88+
cell = self.get(*coord, default=Dead())
89+
return int(cell.is_alive)
90+
91+
return sum(map(is_coord_alive, neighbor_coords))
92+
93+
def get(self, row, col, default=None):
94+
is_within_rows = (0 <= row < self.height)
95+
is_within_cols = (0 <= col < self.width)
96+
if is_within_rows and is_within_cols:
97+
return self.grid[row][col]
98+
return default
99+
100+
101+
class Cell:
102+
@classmethod
103+
def from_str(cls, string):
104+
if string == Live.string_form:
105+
return Live()
106+
return Dead()
107+
108+
def __str__(self):
109+
return self.string_form
110+
111+
class Dead(Cell):
112+
string_form = '·'
113+
is_alive = False
114+
115+
def next_state(self, neighbor_count):
116+
if neighbor_count == 3:
117+
return Live()
118+
return Dead()
119+
120+
class Live(Cell):
121+
string_form = '0'
122+
is_alive = True
123+
124+
def next_state(self, neighbor_count):
125+
if neighbor_count in [2, 3]:
126+
return Live()
127+
return Dead()
128+
129+
130+
from textwrap import dedent
131+
132+
def run_string_example(
133+
*,
134+
seed_string=None,
135+
seed_name=None,
136+
num_gens=10
137+
):
138+
seed_game = GameOfLife.from_str(seed_string)
139+
if seed_name is None:
140+
seed_name = f'A {seed_game.height}x{seed_game.width} grid'
141+
print(dedent(f'''
142+
=========================
143+
| Conway's Game of Life |
144+
{'':=^50}
145+
| {f'Starting with seed: "{seed_name:.10}"': <46.46} |
146+
| {f'Running for {str(num_gens):1.3} generations.': <46.46} |
147+
{'':=^50}
148+
'''))
149+
latest_generation = seed_game
150+
for gen_num in range(1, num_gens + 1):
151+
print(f'Generation {gen_num}:')
152+
print(str(latest_generation))
153+
latest_generation = latest_generation.next_generation()
154+
print('Done')
155+
156+
def glider_example():
157+
glider_string = dedent('''
158+
··0····
159+
0·0····
160+
·00····
161+
·······
162+
·······
163+
·······
164+
''')
165+
run_string_example(
166+
seed_string=glider_string,
167+
seed_name='Glider',
168+
num_gens=15
169+
)
170+
171+
def question_example():
172+
from textwrap import dedent
173+
174+
game_string = dedent('''
175+
·0·
176+
0·0
177+
''')
178+
run_string_example(
179+
seed_string=game_string,
180+
num_gens=4
181+
)
182+
183+
if __name__ == '__main__':
184+
glider_example()
185+
question_example()

0 commit comments

Comments
 (0)
0