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