Created
December 30, 2023 13:52
-
-
Save mebeim/fe71a766baeeb310463e11e94380a304 to your computer and use it in GitHub Desktop.
LED Name Badge Conway's Game of Life Glider Gun
This file contains hidden or 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
#!/usr/bin/env python3 | |
# | |
# @mebeim - 2023-12-30 | |
# | |
# ./glider_gun.py | |
# sudo python3 ./led-badge-11x44.py -s 8 -m 5 :glider_gun_sprite.png: | |
# | |
# Generate a Glider Gun PNG sprite for the LED name badge, writable to the badge | |
# using the code here https://github.com/jnweiger/led-name-badge-ls32 | |
# | |
# Result should look like this: https://x.com/mebeim/status/1740886207662608774 | |
# | |
# Dependencies: Pillow (https://pypi.org/project/Pillow/) | |
# | |
from itertools import product | |
from PIL import Image | |
def evolve_cell(grid, r, c, h, w): | |
alive = 0 | |
for dr, dc in ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)): | |
rr, cc = r + dr, c + dc | |
if 0 <= rr < h and 0 <= cc < w: | |
alive += grid[rr][cc] | |
return alive == 3 or (alive == 2 and grid[r][c]) | |
def evolve(grid): | |
h, w = len(grid), len(grid[0]) | |
new = [[False] * w for _ in range(h)] | |
for r, c in product(range(h), range(w)): | |
new[r][c] = evolve_cell(grid, r, c, h, w) | |
return new | |
def conway(grid): | |
while 1: | |
yield grid | |
grid = evolve(grid) | |
def grid_to_pixels(grid): | |
for y in range(len(grid)): | |
for x in range(len(grid[0])): | |
yield (x, y), ((255,255,255) if grid[y][x] else (0,0,0)) | |
# Initial configuration (longer on the bottom, will be cut to 11 rows later) | |
start = '''\ | |
............................#............... | |
..........................#.#............... | |
................##......##............##.... | |
...............#...#....##............##.... | |
....##........#.....#...##.................. | |
....##........#...#.##....#.#............... | |
..............#.....#.......#............... | |
...............#...#........................ | |
................##.......................... | |
............................................ | |
............................................ | |
............................................ | |
............................................ | |
............................................ | |
............................................ | |
............................................ | |
''' | |
grid = [] | |
for line in start.splitlines(): | |
grid.append([x == '#' for x in line]) | |
# Test out animation on terminal? | |
# from time import sleep | |
# for g in conway(grid): | |
# for line in g: | |
# print(''.join('#' if x else ' ' for x in line)) | |
# sleep(0.1) | |
# import sys; sys.exit(0) | |
# From the 7th frame onwards, the animation will cycle every 30 frames, so skip | |
# the first 6 frames. | |
for _ in range(6): | |
grid = evolve(grid) | |
frames = {} | |
for i, g in enumerate(conway(grid)): | |
px = g[:11] | |
h, w = len(px), len(px[0]) | |
k = frozenset((r, c) for r, c in product(range(h), range(w)) if px[r][c]) | |
if k in frames: | |
print('Animation is', i, 'frames') | |
break | |
frames[k] = (i, px) | |
frames = {i: px for i, px in frames.values()} | |
h, w = len(frames[0]), len(frames[0][0]) | |
assert h == 11 and w == 44 | |
imgs = [] | |
for i in range(len(frames)): | |
frame = frames[i] | |
im = Image.new('RGB', (w, h)) | |
px = im.load() | |
for xy, color in grid_to_pixels(frames[i]): | |
px[xy] = color | |
imgs.append(im) | |
hblank = 4 | |
sprite_w = (w + hblank) * len(imgs) | |
out = Image.new('RGB', (sprite_w, h)) | |
for i, im in enumerate(imgs): | |
out.paste(im, ((w + hblank) * i, 0)) | |
out.save('glider_gun_sprite.png') | |
print('Saved to glider_gun_sprite.png') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment