Skip to content

Instantly share code, notes, and snippets.

@Sonictherocketman
Last active September 11, 2023 21:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Sonictherocketman/4551dcf001932639a03e8849be92ff71 to your computer and use it in GitHub Desktop.
Save Sonictherocketman/4551dcf001932639a03e8849be92ff71 to your computer and use it in GitHub Desktop.
The game of life in Python.
#! /usr/bin/env python3
#
# The Game of Life in Python with no dependencies.
# by Brian Schrader
#
# The board is randomly generated each time so each play-through is unique!
#
# Usage:
# ./life.py
from collections import Counter
from tkinter import *
from tkinter.ttk import *
from dataclasses import dataclass
import math
import random
max_size = 1600, 1000
state = []
particle_width = 15
particle_kwargs = {
'outline': 'black',
'fill': 'white',
'width': 1,
}
old_particle_kwargs = {
'outline': 'black',
'fill': 'green',
'width': 1,
}
@dataclass
class CellState:
current_state: int
previous_state: int = None
age: int = 1
id: int = None
OLD_AGE = 5
def set_state(self, value):
self.previous_state = self.current_state
self.current_state = value
if self.current_state and self.previous_state:
self.age += 1
else:
self.age = 0
@property
def changed(self):
return self.previous_state != self.current_state
class App(Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
class Space:
def __init__(self, master=None, initial_state: [[CellState]]=[]):
self.master = master
self.state = initial_state
self.canvas = Canvas(self.master, bg='black')
self.canvas.pack(fill=BOTH, expand=1)
def render(self):
width = particle_width
for x, y, state in self.cells():
if state.changed:
if state.current_state:
# Newly created cell. Add it.
state.id = self.canvas.create_rectangle(
(x * width),
(y * width),
(x * width) + (width),
(y * width) + (width),
**particle_kwargs,
)
else:
# Newly dead cell. Delete it.
self.canvas.delete(state.id)
state.id = None
elif state.age == state.OLD_AGE:
# Newly old cell. Recolor it.
self.canvas.delete(state.id)
state.id = self.canvas.create_rectangle(
(x * width),
(y * width),
(x * width) + (width),
(y * width) + (width),
**old_particle_kwargs,
)
def cells(self):
for y, row in enumerate(self.state):
for x, value in enumerate(row):
yield (x, y, value)
def get_next_state(self, x, y, state):
"""
| a | b | c |
| d | - | e |
| f | g | h |
"""
y_lim = len(self.state) - 1
x_lim = len(self.state[0]) - 1
a = 0 if x == 0 or y == 0 else self.state[y-1][x-1].current_state
b = 0 if y == 0 else self.state[y-1][x ].current_state
c = 0 if y == 0 or x == x_lim else self.state[y-1][x+1].current_state
d = 0 if x == 0 else self.state[y ][x-1].current_state
e = 0 if x == x_lim else self.state[y ][x+1].current_state
f = 0 if x == 0 or y == y_lim else self.state[y+1][x-1].current_state
g = 0 if y == y_lim else self.state[y+1][x ].current_state
h = 0 if x == x_lim or y == y_lim else self.state[y+1][x+1].current_state
if not any({a, b, c, d, e, f, g, h}):
# All dead. Don't bother.
return 0
counter = Counter([a, b, c, d, e, f, g, h])
alive_neighbors = counter[1]
if state.current_state and (alive_neighbors == 2 or alive_neighbors == 3):
return 1
elif state.current_state:
return 0
elif alive_neighbors == 3:
return 1
else:
return 0
def tick(self):
for x, y, value in self.cells():
self.state[y][x].set_state(self.get_next_state(x, y, value))
def main(wait=120):
# initialize board
for y in range(max_size[1] // particle_width):
state.append([])
for _ in range(max_size[0] // particle_width):
state[y].append(CellState(random.choices([1, 0], [0.04, 0.96])[0]))
# startup app
application = App()
application.master.title('The Board')
application.master.maxsize(*max_size)
application.master.geometry(f'{max_size[0]}x{max_size[1]}')
space = Space(application.master, initial_state=state)
space.render()
def _render():
try:
space.tick()
except StopIteration:
print('Done')
return
else:
space.render()
application.master.after(wait, _render)
# kickoff
application.master.after(wait, _render)
application.mainloop()
if __name__ == '__main__':
main()
@Sonictherocketman
Copy link
Author

Screenshot 2023-09-11 at 2 51 59 PM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment