140 lines
4.0 KiB
Python
140 lines
4.0 KiB
Python
from time import sleep_ms
|
|
from framebuf import FrameBuffer1
|
|
from urandom import getrandbits
|
|
|
|
class ConwaysGameOfLife:
|
|
def __init__(self, lcd):
|
|
# High score
|
|
self.best = 0
|
|
# PCD8544 (Nokia 5110) LCD
|
|
self.lcd = lcd
|
|
self.width = 84
|
|
self.height = 48
|
|
self.pages = self.height // 8
|
|
self.buffer = bytearray(self.pages * self.width)
|
|
self.framebuf = FrameBuffer1(self.buffer, self.width, self.height)
|
|
|
|
def draw(self):
|
|
self.lcd.data(self.buffer)
|
|
|
|
def intro(self):
|
|
self.framebuf.fill(0)
|
|
self.framebuf.text("Conway's", 10, 8, 1)
|
|
self.framebuf.text("Game", 26, 16, 1)
|
|
self.framebuf.text("of", 34, 24, 1)
|
|
self.framebuf.text("Life", 26, 32, 1)
|
|
self.draw()
|
|
|
|
def end(self, score, best, size):
|
|
# The 8x8 font is too wide to fit "Generations", so I called it "Score"
|
|
self.framebuf.fill(0)
|
|
self.framebuf.text("Score", 0, 0, 1)
|
|
self.framebuf.text(str(score), 0, 8, 1)
|
|
self.framebuf.text("Best score", 0, 16, 1)
|
|
self.framebuf.text(str(best), 0, 24, 1)
|
|
self.framebuf.text("Pixel size", 0, 32, 1)
|
|
self.framebuf.text(str(size), 0, 40, 1)
|
|
self.draw()
|
|
|
|
def begin(self, size=4, delay=20):
|
|
# Size of lifeforms in pixels
|
|
self.size = size
|
|
# Delay in ms between generations
|
|
self.delay = delay
|
|
|
|
# Localised to avoid self lookups
|
|
# Possible performance optimisation, TBC
|
|
draw = self.draw
|
|
delay = self.delay
|
|
tick = self.tick
|
|
|
|
# Randomise initial pixels
|
|
self.randomise()
|
|
|
|
# Begin
|
|
generations = 0
|
|
try:
|
|
while tick():
|
|
generations = generations + 1
|
|
draw()
|
|
sleep_ms(delay)
|
|
except KeyboardInterrupt:
|
|
pass
|
|
|
|
# New high score?
|
|
if generations > self.best:
|
|
self.best = generations
|
|
|
|
# End
|
|
self.end(generations, self.best, self.size)
|
|
|
|
def randomise(self):
|
|
size = self.size
|
|
width = self.width
|
|
height = self.height
|
|
cell = self.cell
|
|
|
|
self.framebuf.fill(0)
|
|
|
|
for x in range(0, width, size):
|
|
for y in range(0, height, size):
|
|
# random bit: 0 = pixel off, 1 = pixel on
|
|
cell(x, y, getrandbits(1))
|
|
|
|
self.draw()
|
|
|
|
def cell(self, x, y, colour):
|
|
size = self.size
|
|
buf = self.framebuf
|
|
|
|
for i in range(size):
|
|
for j in range(size):
|
|
buf.pixel(x + i, y + j, colour)
|
|
|
|
def get(self, x, y):
|
|
if not 0 <= x < self.width or not 0 <= y < self.height:
|
|
return 0
|
|
return self.framebuf.pixel(x, y) & 1
|
|
|
|
def tick(self):
|
|
size = self.size
|
|
width = self.width
|
|
height = self.height
|
|
get = self.get
|
|
cell = self.cell
|
|
|
|
# If no pixels are born or die, the game ends
|
|
something_happened = False
|
|
|
|
for x in range(0, width, size):
|
|
for y in range(0, height, size):
|
|
|
|
# Is the current cell alive
|
|
alive = get(x, y)
|
|
|
|
# Count number of neighbours
|
|
neighbours = (
|
|
get(x - size, y - size) +
|
|
get(x, y - size) +
|
|
get(x + size, y - size) +
|
|
get(x - size, y) +
|
|
get(x + size, y) +
|
|
get(x + size, y + size) +
|
|
get(x, y + size) +
|
|
get(x - size, y + size)
|
|
)
|
|
|
|
# Apply the game rules
|
|
if alive and not 2 <= neighbours <= 3:
|
|
# This pixel dies
|
|
cell(x, y, 0)
|
|
if not something_happened:
|
|
something_happened = True
|
|
elif not alive and neighbours == 3:
|
|
# A new pixel is born
|
|
cell(x, y, 1)
|
|
if not something_happened:
|
|
something_happened = True
|
|
|
|
return something_happened
|