-
Notifications
You must be signed in to change notification settings - Fork 0
/
conways_game_of_life.py
195 lines (170 loc) · 6.31 KB
/
conways_game_of_life.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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#### "conways_game_of_life.py"
#### Grace Hadiyanto
#### CS439 FA14
import sys
class Cell:
__slots__ = {'_alive', '_successor_state', '_neighbors'}
def __init__(self):
# _alive is a bool representing current state of the cell
self._alive = False
self._successor_state = None
self._neighbors = []
@property
def alive(self):
"""I'm the 'alive' property"""
return self._alive
@alive.setter
def alive(self, state):
self._alive = state
@property
def neighbors(self):
"""I'm the 'neighbors' property"""
return self._neighbors
@neighbors.setter
def neighbors(self, list_of_neighbors):
self._neighbors = list_of_neighbors
@property
def successor_state(self):
"""I'm the 'successor_state' property"""
return self._successor_state
@successor_state.setter
def successor_state(self, state):
self._successor_state = state
def count_live_neighbors(self):
# Have the cell look around to see how many of their neighbors are
# currently alive.
count = 0
for neighbor in self._neighbors:
if neighbor.alive:
count += 1
return count
def prepare_transition(self):
# Have the cell prepare to transition by applying the rules of the
# game and setting their successor state.
live = True
die = False
live_neighbors = self.count_live_neighbors()
if self.alive:
if live_neighbors < 2:
self._successor_state = die
elif live_neighbors == 2 or live_neighbors == 3:
self._successor_state = live
else:
self._successor_state = die
else:
if live_neighbors == 3:
self._successor_state = live
else:
self._successor_state = die
def transition(self):
# If the successor state is different than the current state, then
# transition into the successor state.
if self._successor_state != self.alive:
self.alive = self._successor_state
# Flush the successor state since we've consumed it
self._successor_state = None
def __str__(self):
if self.alive:
return 'o'
else:
return '.'
class LifeField:
__slots__ = {'field'}
def __init__(self, seed):
# Create a field of 20 rows by 60 columns, each row representing
# a y coordinate and each column representing an x coordinate.
self.field = [[Cell() for col in range(60)] for row in range(20)]
self.find_cell_neighbors()
# Set the cells described by coordinates in the seed to be alive.
for x, y in seed:
self.field[y][x].alive = True
def find_cell_neighbors(self):
# Have each cell get to know their neighbors!
for y in range(20):
for x in range(60):
neighbors = adjacent_coordinates(y,x)
for neighbor_y, neighbor_x in neighbors:
self.field[y][x].neighbors.append(self.field[neighbor_y][neighbor_x])
def prepare_for_transition(self):
# Prepare the life field to transition to the next state by preparing
# each cell for transition into their successor state.
for y in range(20):
for x in range(60):
self.field[y][x].prepare_transition()
def simulate_tick(self):
# Simulate simultaneous births and deaths of cells by having each cell
# transition.
self.prepare_for_transition()
for y in range(20):
for x in range(60):
self.field[y][x].transition()
def __str__(self):
s = ''
for y in range(20):
for x in range(60):
s += str(self.field[y][x])
s += '\n'
return s
def adjacent_coordinates(y,x):
# Calculate legal adjacent coordinates for a given (y, x) coordinate pair
legal_adjacent_coordinates = []
if y-1 >= 0:
legal_adjacent_coordinates.append((y-1,x))
if x-1 >= 0:
legal_adjacent_coordinates.append((y-1,x-1))
if x+1 <= 59:
legal_adjacent_coordinates.append((y-1,x+1))
if y+1 <= 19:
legal_adjacent_coordinates.append((y+1,x))
if x-1 >= 0:
legal_adjacent_coordinates.append((y+1,x-1))
if x+1 <= 59:
legal_adjacent_coordinates.append((y+1,x+1))
if x-1 >= 0:
legal_adjacent_coordinates.append((y,x-1))
if x+1 <= 59:
legal_adjacent_coordinates.append((y,x+1))
return legal_adjacent_coordinates
def parse_file(file_name):
x_coordinates = []
y_coordinates = []
input_file = open(file_name, 'r')
input_file.readline()
for line in input_file:
coordinate = line.split()
x_coordinates.append(int(coordinate[0]))
y_coordinates.append(int(coordinate[1]))
return x_coordinates, y_coordinates
def translate_points(x_coordinates, y_coordinates):
minimum_x = min(x_coordinates)
minimum_y = min(y_coordinates)
translated_x = [x - minimum_x for x in x_coordinates]
translated_y = [y - minimum_y for y in y_coordinates]
return translated_x, translated_y
def main():
if len(sys.argv) != 3:
print('Required usage: python3 conways_game_of_life.py <filename> <n>')
print('Where filename is the name of a Life 1.06 file, and n is the',end='')
print(' number of generations you want to play.')
exit()
# Collect command line arguments
filename = sys.argv[1]
n = int(sys.argv[2])
# Parse file and gather coordinates, and translate them
x_coordinates, y_coordinates = parse_file(filename)
x_coordinates, y_coordinates = translate_points(x_coordinates, y_coordinates)
# Pair coordinates together to create the seed for the life game
seed = []
for i in range(len(x_coordinates)):
seed.append((x_coordinates[i],y_coordinates[i]))
# Give birth to the game and print an initial view
game = LifeField(seed)
print('TIME STEP 0')
print(game,end='\n\n')
# Play the game for the next n-1 generations!!!
for i in range(1,n):
print('TIME STEP {}'.format(i))
game.simulate_tick()
print(game, end='\n\n')
if __name__ == '__main__':
main()