Skip to content

Instantly share code, notes, and snippets.

@louisswarren
Last active November 30, 2021 04:16
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 louisswarren/aaec24795b9463957d01508632f6fb66 to your computer and use it in GitHub Desktop.
Save louisswarren/aaec24795b9463957d01508632f6fb66 to your computer and use it in GitHub Desktop.
Cellular automata
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
from collections import defaultdict
import sys
from svg import *
def log(*a, **k):
print(*a, **k, file=sys.stderr)
ROWS = 16
STOP_AFTER = ROWS
COLS = ROWS*2-1
BOXSIZE = 16
HEADER_HEIGHT = BOXSIZE * ROWS * (3/4)
NOM_WIDTH = COLS * BOXSIZE + 1 + 20
NOM_HEIGHT = NOM_WIDTH // 2 ** 0.5
NOM_YBIAS = NOM_HEIGHT - (ROWS * BOXSIZE + 10)
IMG_HEIGHT = 1024
IMG_WIDTH = IMG_HEIGHT / NOM_HEIGHT * NOM_WIDTH
RULE_GAP = 2
RULE_MGAP = 3
RULE_MARGIN = 2
if len(sys.argv) > 1:
rule = int(sys.argv[1])
else:
rule = 30
# Basic header
svg_header()
svg_open(width=IMG_WIDTH, height=IMG_HEIGHT,
x_bias=-(NOM_WIDTH-1)/2-1, y_bias=-NOM_YBIAS+12,
xw=NOM_WIDTH+1, yh=NOM_HEIGHT+1)
def draw_box(x, y, value=None, scale=1):
svg_rect((x*scale, y*scale), scale*BOXSIZE, scale*BOXSIZE,
fill="black" if value else "white",
stroke="grey", stroke_width=scale)
def draw_cell(row, col, value=None, scale=1):
draw_box(BOXSIZE*(col-0.5), BOXSIZE*row,
value=value, scale=scale)
#def draw_rule(x, y, n):
# scale = COLS / (4*3 + 3*RULE_GAP + 2*RULE_MARGIN)
# draw_box((x - 1.5)*BOXSIZE, y * BOXSIZE , n & 4 != 0 , scale)
# draw_box((x - 0.5)*BOXSIZE, y * BOXSIZE , n & 2 != 0 , scale)
# draw_box((x + 0.5)*BOXSIZE, y * BOXSIZE , n & 1 != 0 , scale)
# draw_box((x - 0.5)*BOXSIZE, (y + 1) * BOXSIZE, rule & (1 << n) != 0, scale)
#
#draw_rule(-(3 + RULE_MGAP)/2 - 3 - RULE_GAP, -7, 7)
#draw_rule(-(3 + RULE_MGAP)/2 , -7, 6)
#draw_rule( (3 + RULE_MGAP)/2 , -7, 5)
#draw_rule( (3 + RULE_MGAP)/2 + 3 + RULE_GAP, -7, 4)
#draw_rule(-(3 + RULE_MGAP)/2 - 3 - RULE_GAP, -3, 3)
#draw_rule(-(3 + RULE_MGAP)/2 , -3, 2)
#draw_rule( (3 + RULE_MGAP)/2 , -3, 1)
#draw_rule( (3 + RULE_MGAP)/2 + 3 + RULE_GAP, -3, 0)
def draw_rule(row, col, n):
draw_cell(row, col - 1, n & 4 != 0)
draw_cell(row, col, n & 2 != 0)
draw_cell(row, col + 1, n & 1 != 0)
draw_cell(row + 1, col, rule & (1 << n) != 0)
draw_rule(-4, -14, 7)
draw_rule(-4, -10, 6)
draw_rule(-4, -6, 5)
draw_rule(-4, -2, 4)
draw_rule(-4, 2, 3)
draw_rule(-4, 6, 2)
draw_rule(-4, 10, 1)
draw_rule(-4, 14, 0)
cells = defaultdict(lambda: False)
cells[(0,0)] = True
draw_cell(0, 0, cells[(0,0)])
for j in range(1, ROWS):
if j >= STOP_AFTER:
for i in range(-j, j+1):
draw_cell(j, i, False)
continue
for i in range(-j, j+1):
prev = 4 * cells[(j-1, i-1)]
prev += 2 * cells[(j-1, i )]
prev += 1 * cells[(j-1, i+1)]
cells[(j,i)] = rule & (1 << prev) != 0
draw_cell(j, i, cells[(j,i)])
# Footer
svg_close()
.PHONY: test
test: 027.png
.PHONY: all
all:
$(MAKE) -j 4 $$(for i in $$(seq -w 0 255); do echo -n "$$i.png "; done)
%.png: %.svg
convert $^ $@
%.svg: cell.py svg.py
python3 $< $* > $@
.PHONY: clean
clean:
rm -f *.png *.svg *.mp4
def xml(tag, _xml_tag_is_a_singleton=True, **options):
s = f'<{tag}'
kw_attrib = lambda x: x.replace('_', '-')
if options:
s += ' '
s += ' '.join(f'{kw_attrib(k)}="{str(v)}"' for k, v in options.items())
if _xml_tag_is_a_singleton:
s += ' />'
else:
s += '>'
print(s)
def xml_open(*args, **kwargs):
xml(*args, **kwargs, _xml_tag_is_a_singleton=False)
def xml_close(tag):
print(f'</{tag}>')
def svg_header():
'<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
def svg_open(width, height, x_bias, y_bias, xw, yh):
vb = f'{x_bias} {y_bias} {xw} {yh}'
ns = 'http://www.w3.org/2000/svg'
xml_open('svg', width=width, height=height, viewBox=vb, xmlns=ns)
def svg_close():
xml_close('svg')
def svg_poly(*points, **opts):
point_str = ' '.join(f'{x},{y}' for x, y in points)
xml('polyline', points=point_str, **opts)
def svg_circle(point, radius, **opts):
xml('circle', cx=point[0], cy=point[1], r=radius, **opts)
def svg_line(p1, p2, **opts):
xml('line', x1=p1[0], y1=p1[1], x2=p2[0], y2=p2[1], **opts)
def svg_rect(p, width, height, x_radius=0, y_radius=0, **opts):
xml('rect', x=p[0], y=p[1], width=width, height=height,
rx=x_radius, ry=y_radius, **opts)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment