Source code for BiblioPixelAnimations.matrix.GameOfLife

import copy, random, threading, time
from collections import deque
from bibliopixel.animation.matrix import Matrix
from bibliopixel.colors import COLORS
from bibliopixel.util import log
from bibliopixel.layout import font


[docs]class Table: def __init__(self, height, width, rand_max, table=None): self.toroidal = True self._rand_max = rand_max if table: self.table = table self.height = len(table) self.width = len(table[0]) else: self.height = height self.width = width self.genNewTable() self._oldStates = deque() for i in range(3): self._oldStates.append([])
[docs] def genNewTable(self): self.table = [] random.seed(time.time()) for y in range(0, self.height): self.table.append([]) for x in range(0, self.width): rand = random.randint(0, self._rand_max) if rand == 0: self.table[y].append(1) else: self.table[y].append(0)
[docs] def liveNeighbours(self, y, x): """Returns the number of live neighbours.""" count = 0 if y > 0: if self.table[y - 1][x]: count = count + 1 if x > 0: if self.table[y - 1][x - 1]: count = count + 1 if self.width > (x + 1): if self.table[y - 1][x + 1]: count = count + 1 if x > 0: if self.table[y][x - 1]: count = count + 1 if self.width > (x + 1): if self.table[y][x + 1]: count = count + 1 if self.height > (y + 1): if self.table[y + 1][x]: count = count + 1 if x > 0: if self.table[y + 1][x - 1]: count = count + 1 if self.width > (x + 1): if self.table[y + 1][x + 1]: count = count + 1 if self.toroidal: if y == 0: if self.table[self.height - 1][x]: count = count + 1 if y == self.height - 1: if self.table[0][x]: count = count + 1 if x == 0: if self.table[y][self.width - 1]: count = count + 1 if x == self.width - 1: if self.table[y][0]: count = count + 1 return count
[docs] def turn(self): """Turn""" nt = copy.deepcopy(self.table) for y in range(0, self.height): for x in range(0, self.width): neighbours = self.liveNeighbours(y, x) if self.table[y][x] == 0: if neighbours == 3: nt[y][x] = 1 else: if (neighbours < 2) or (neighbours > 3): nt[y][x] = 0 self._oldStates.append(self.table) if len(self._oldStates) > 3: self._oldStates.popleft() self.table = nt
[docs] def checkStable(self): for t in self._oldStates: if self.table == t: return True return False
[docs]class GameOfLife(Matrix): COLOR_DEFAULTS = ('bg', COLORS.Off), ('color', COLORS.Red), def __init__(self, layout, toroidal=False, **kwds): super().__init__(layout, **kwds) self.toroidal = toroidal self._finishCount = 0
[docs] def pre_run(self): self._table = Table(self.height, self.width, 1, None) self._table.toroidal = self.toroidal
[docs] def stepTable(self): x = 0 y = 0 for row in self._table.table: for col in row: color = self.palette(int(col != 0)) self.layout.set(x, y, color) x = x + 1 y = y + 1 x = 0 self._table.turn()
[docs] def step(self, amt=1): self.stepTable() if self._table.checkStable(): self._finishCount += 1 if self._finishCount > 10: self._table.genNewTable() self._finishCount = 0 self.animComplete = True
[docs]class GameOfLifeRGB(Matrix): def __init__(self, layout, toroidal=True, **kwds): super().__init__(layout, **kwds) self.toroidal = toroidal
[docs] def pre_run(self): self._tableR = Table(self.height, self.width, 1, None) self._tableR.toroidal = self.toroidal time.sleep(0.01) self._tableG = Table(self.height, self.width, 1, None) self._tableG.toroidal = self.toroidal time.sleep(0.01) self._tableB = Table(self.height, self.width, 1, None) self._tableB.toroidal = self.toroidal
[docs] def stepTables(self): x = 0 y = 0 for row in range(self._tableR.height): for col in range(self._tableR.width): r = (self._tableR.table[row][col]) * 255 g = (self._tableG.table[row][col]) * 255 b = (self._tableB.table[row][col]) * 255 c = (r, g, b) self.layout.set(x, y, c) x = x + 1 y = y + 1 x = 0 self._tableR.turn() self._tableG.turn() self._tableB.turn()
[docs] def doStableCheck(self, table): if table.checkStable(): table.genNewTable()
[docs] def step(self, amt=1): self.stepTables() self.doStableCheck(self._tableR) self.doStableCheck(self._tableG) self.doStableCheck(self._tableB)
[docs]class GameOfLifeClock(Matrix): def __init__(self, layout, font_name='16x8', mil_time=False, **kwds): super().__init__(layout, **kwds) self.font_name = font_name self.mil_time = mil_time self.scale = 1 self.steady_time = 2000 self.history = None self.next_history = None self.next_thread = None self.next_ready = False # Find the text size while True: x, y = font.str_dim('00:00', font=self.font_name, font_scale=self.scale, final_sep=False) if x > self.width or y > self.height: self.scale -= 1 break self.scale += 1 # create a time frame while we wait self.init_frame = self.create_time_table(int(time.time()))
[docs] def create_time_table(self, t): t = time.localtime(t) hr = t.tm_hour if not self.mil_time: hr = hr % 12 hrs = str(hr).zfill(2) mins = str(t.tm_min).zfill(2) val = hrs + ":" + mins w, h = font.str_dim(val, font=self.font_name, font_scale=self.scale, final_sep=False) x = (self.width - w) // 2 y = (self.height - h) // 2 old_buf = copy.copy(self.layout.colors) self.layout.all_off() self.layout.drawText(val, x, y, color=COLORS.Red, font=self.font_name, font_scale=self.scale) table = [] for y in range(self.height): table.append([0] * self.width) for x in range(self.width): table[y][x] = int(any(self.layout.get(x, y))) self.layout.setBuffer(old_buf) return table
[docs] def generate_history(self, t, steps): history = [] start = self.create_time_table(t) steady_frames = int(self.steady_time // self._sleep) steady = steps if steps <= steady_frames else steady_frames for _ in range(steady): history.append(start) gol = Table(self.height, self.width, 1, start) for _ in range(steps - steady): gol.turn() history.append(copy.deepcopy(gol.table)) return history
[docs] def gen_next(self): start = self._msTime() t = int(time.time()) assert self._sleep, "GameOfLifeClock requires a set FPS!" self.next_history = self.generate_history(t + 60, 60000 // self._sleep) self.next_ready = True log.debug("History Generate Time: {}".format(self._msTime() - start))
[docs] def do_gen_next(self): if not self.next_thread or not self.next_thread.isAlive(): self.next_thread = threading.Thread(target=self.gen_next) self.next_thread.start()
[docs] def swap_history(self): self.history = self.next_history self.next_ready = False self.do_gen_next()
[docs] def display_frame(self, frame): for y in range(self.height): for x in range(self.width): c = COLORS.Red if frame[y][x] else COLORS.Black self.layout.set(x, y, c)
[docs] def step(self, amt=1): if not self.history and not self.next_ready: self.display_frame(self.init_frame) if self._sleep: self.do_gen_next() else: if not self.history: self.swap_history() if len(self.history) > 1: self.display_frame(self.history.pop(-1)) else: self.display_frame(self.history[0]) if self.next_ready: self.swap_history()