Created
July 6, 2012 05:20
-
-
Save Mause/3058249 to your computer and use it in GitHub Desktop.
A silly implementation of conways game of life
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"an implementation of conways game of life" | |
# File: gol.py | |
# | |
# Project: Conways game of life | |
# Component: | |
# | |
# Authors: Dominic May; | |
# Lord_DeathMatch; | |
# Mause | |
# Any live cell with fewer than two live neighbours dies, as if caused by | |
# under-population. | |
# Any live cell with two or three live neighbours lives on to the next | |
# generation. | |
# Any live cell with more than three live neighbours dies, as if by | |
# overcrowding. | |
# Any dead cell with exactly three live neighbours becomes a live cell, as if | |
# by reproduction. | |
from gol_utilities import read_in_data | |
DIRECTIONS = [ | |
# (0, 0), self | |
(-1, -1), # SW | |
(-1, 0), # W | |
(-1, 1), # NW | |
(0, -1), # S | |
(0, 1), # N | |
(1, -1), # SE | |
(1, 0), # E | |
(1, 1) # NE | |
] | |
class Cell(object): | |
__slots__ = ['state', 'item'] | |
DEAD = 0 | |
LIVING = 2 | |
STATES = { | |
'.': DEAD, | |
'o': LIVING | |
} | |
def __init__(self, state): | |
self.state = self.STATES[state] | |
self.item = None | |
def is_living(self): | |
return self.state == Cell.LIVING | |
def is_dead(self): | |
return self.state == Cell.DEAD | |
def colour(self): | |
return "black" if self.is_living() else "blue" | |
class GOL(object): | |
def __init__(self, initial_filename): | |
self.initial_filename = initial_filename | |
self.load_grid() | |
# check that the grid is uniform | |
assert len(set(map(len, self.grid))) == 1 | |
self.grid_height = len(self.grid) | |
self.grid_width = len(self.grid[0]) | |
self.loop_interval = 10 | |
self.cycles = 0 | |
def increase_speed(self): | |
if (self.loop_interval - 25) > 0: | |
self.loop_interval -= 25 | |
self.speed_box.config(text=str(self.loop_interval)) | |
def decrease_speed(self): | |
self.loop_interval += 25 | |
self.speed_box.config(text=str(self.loop_interval)) | |
def load_grid(self): | |
grid = [ | |
[ | |
Cell(x_cell) | |
for x_cell in y_row | |
] | |
for y_row in read_in_data(self.initial_filename) | |
] | |
self.grid = list(filter(bool, grid)) | |
def draw_deltas(self, deltas): | |
raise NotImplementedError() | |
def apply_deltas(self, deltas): | |
raise NotImplementedError() | |
def reload(self): | |
self.cycles = 0 | |
self.load_grid() | |
self.draw_deltas([]) | |
def lives(self, x, y): | |
return self.grid[y][x].is_living() | |
def neighboring_cells(self, x, y): | |
# “Easier to ask forgiveness than permission” | |
# or in this case, faster, as the code will succeed 90% of the time, | |
# but if we were asking for permission, we would have to check every | |
# time we look at a position | |
for x_diff, y_diff in DIRECTIONS: | |
try: | |
yield self.grid[y + y_diff][x + x_diff] | |
except IndexError: | |
pass | |
def living_neighbors(self, x, y): | |
n = filter( | |
Cell.is_living, | |
self.neighboring_cells(x, y) | |
) | |
return len(list(n)) | |
def calc_deltas(self): | |
deltas = set() | |
for y in range(self.grid_height): | |
for x in range(self.grid_width): | |
# calculate once | |
alive_neighbors = self.living_neighbors(x, y) | |
# Any live cell with fewer than two live neighbours dies, | |
# as if caused by under-population. | |
if alive_neighbors < 2 and self.lives(x, y): | |
deltas.add(((x, y), Cell.DEAD)) | |
# Any live cell with two or three live neighbours | |
# lives on to the next generation. | |
elif (alive_neighbors in {2, 3} and | |
self.lives(x, y)): | |
deltas.add(((x, y), Cell.LIVING)) | |
# Any live cell with more than three live neighbours dies, | |
# as if by overcrowding. | |
elif alive_neighbors > 3 and self.lives(x, y): | |
deltas.add(((x, y), Cell.DEAD)) | |
# Any dead cell with exactly three live | |
# neighbours becomes a live cell, as if by reproduction. | |
elif alive_neighbors == 3 and not self.lives(x, y): | |
deltas.add(((x, y), Cell.LIVING)) | |
return deltas | |
def cycle(self): | |
self.cycles += 1 | |
deltas = self.calc_deltas() | |
if deltas: | |
self.apply_deltas(deltas) | |
self.draw_deltas(deltas) | |
class GOLConsole(GOL): | |
def draw_deltas(self, deltas): | |
for row in self.grid: | |
for cell in row: | |
print({0: '.', 2: 'O'}[cell.state], end='') | |
print() | |
def apply_deltas(self, deltas): | |
# as we're not updating an existing display, this is useless | |
pass | |
class GOLNull(GOL): | |
def draw_deltas(self, deltas): | |
pass | |
def apply_deltas(self, deltas): | |
pass | |
def main(): | |
# import os | |
# import time | |
inst = GOLNull("glider_gun.txt") | |
import cProfile | |
cProfile.runctx( | |
"inst.cycle()", | |
{}, {'inst': inst}, | |
sort="cumulative" | |
) | |
# time.sleep(inst.loop_interval / 1000) | |
# os.system('cls') | |
if __name__ == '__main__': | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from threading import Thread | |
from tkinter import Tk, BOTTOM, X, Button, Frame, Canvas, FALSE, Label, ALL | |
from gol import GOL, Cell | |
from gol_utilities import StatusBar | |
class GOLGUI(GOL): | |
def __init__(self, *args, **kwargs): | |
super().__init__(*args, **kwargs) | |
self.canvas_multiplier = 5 | |
def draw_deltas(self, deltas): | |
self.canvas.delete(ALL) | |
for (x, y), state in deltas: | |
if state == Cell.DEAD: | |
# kill it | |
self.canvas.delete(self.grid[y][x].item) | |
else: | |
# bring it to life | |
x_initial = x * self.canvas_multiplier | |
y_initial = y * self.canvas_multiplier | |
self.grid[y][x].item = self.canvas.create_rectangle( | |
x_initial + 2, y_initial + 2, | |
x_initial + self.canvas_multiplier, | |
y_initial + self.canvas_multiplier, | |
fill="BLACK" | |
# self.grid[y][x].colour() | |
) | |
def apply_deltas(self, deltas): | |
for (x, y), state in deltas: | |
self.grid[y][x].state = state | |
def run_single_cycle(self, event=None): | |
cycle_thread = Thread(target=self.cycle) | |
cycle_thread.start() | |
def loop(self): | |
self.cycle() | |
self.root.after(self.loop_interval, self.loop) | |
def cycle(self): | |
self.cycle_box.config(text=self.cycles) | |
return super().cycle() | |
def build_ui_button_frame(self): | |
buttons = Frame(self.root) | |
cycle_button = Button( | |
buttons, | |
text="Cycle", | |
command=self.run_single_cycle | |
) | |
cycle_button.grid(row=0, column=0) | |
reload_button = Button(buttons, text="Reload", command=self.reload) | |
reload_button.grid(row=0, column=1) | |
loop_button = Button(buttons, text="Loop", command=self.loop) | |
loop_button.grid(row=0, column=2) | |
increase_button = Button( | |
buttons, | |
text="+", | |
command=self.increase_speed | |
) | |
increase_button.grid(row=1, column=0) | |
self.speed_box = Label(buttons) | |
self.speed_box.config(text=str(self.loop_interval)) | |
self.speed_box.grid(row=1, column=1) | |
decrease_button = Button( | |
buttons, | |
text="-", | |
command=self.decrease_speed | |
) | |
decrease_button.grid(row=1, column=2) | |
self.cycle_box = Label(buttons) | |
self.cycle_box.config(text=self.cycles) | |
self.cycle_box.grid(row=2, column=1) | |
buttons.pack() | |
def build_ui(self): | |
self.root = Tk() | |
self.status = StatusBar(self.root) | |
self.status.pack(side=BOTTOM, fill=X) | |
self.build_ui_button_frame() | |
self.canvas = Canvas( | |
self.root, | |
width=self.canvas_multiplier * self.grid_width, | |
height=self.canvas_multiplier * self.grid_height | |
) | |
self.canvas.config(background='GREY') | |
self.canvas.pack() | |
self.root.bind('<space>', self.run_single_cycle) | |
self.root.title("Dom's Game Of Life") | |
self.root.resizable(width=FALSE, height=FALSE) | |
def main(): | |
# inst = GOLGUI("builder.txt") | |
# inst = GOLGUI("output.txt") | |
inst = GOLGUI("builder.txt") | |
inst.build_ui() | |
inst.root.mainloop() | |
if __name__ == '__main__': | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from tkinter import Frame, Label, SUNKEN, W, X | |
from collections import Counter | |
from itertools import chain | |
def counter(array, charac): | |
return Counter(chain.from_iterable(array))[charac] | |
class StatusBar(Frame): | |
def __init__(self, master): | |
super().__init__(master) | |
self.label = Label(self, bd=1, relief=SUNKEN, anchor=W) | |
self.label.pack(fill=X) | |
def set(self, format, *args): | |
self.label.config(text=format % args) | |
self.label.update_idletasks() | |
def clear(self): | |
self.label.config(text="") | |
self.label.update_idletasks() | |
def read_in_data(filename): | |
with open(filename) as fh: | |
return [ | |
list(line.strip()) | |
for line in fh.readlines() | |
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
o.....................................o. | |
.......................................o | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
........................................ | |
o......................................o | |
oo....................................oo |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment