Skip to content

Instantly share code, notes, and snippets.

@Mause
Created July 6, 2012 05:20
Show Gist options
  • Save Mause/3058249 to your computer and use it in GitHub Desktop.
Save Mause/3058249 to your computer and use it in GitHub Desktop.
A silly implementation of conways game of life
"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()
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()
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()
]
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