Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4652400

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

File tree

1 file changed

+185
-0
lines changed
Filter options

1 file changed

+185
-0
lines changed
+185Lines 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)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.