83 lines
1.7 KiB
Python
83 lines
1.7 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import sys
|
|
import numpy as np
|
|
|
|
|
|
def lament(*args, **kwargs):
|
|
return print(*args, file=sys.stderr, **kwargs)
|
|
|
|
|
|
def colorize(x):
|
|
return x * 17 % 256
|
|
|
|
|
|
def render(row, scale):
|
|
grays = colorize(row)
|
|
if scale > 1:
|
|
grays = np.repeat(grays, scale)
|
|
line = " ".join(str(x) for x in grays)
|
|
for y in range(scale):
|
|
print(line)
|
|
|
|
|
|
def limit(row): # in-place
|
|
row[row < 0] = 0
|
|
row[row > 15] = 15
|
|
return row
|
|
|
|
|
|
def initialize(width):
|
|
row = np.zeros(width, int)
|
|
value = 0
|
|
for i in range(width):
|
|
if np.random.randint(8) == 0: # only change values occasionally
|
|
value = np.random.randint(16)
|
|
row[i] = value
|
|
limit(row)
|
|
return row
|
|
|
|
|
|
name, args = sys.argv[0], sys.argv[1:]
|
|
|
|
if len(args) == 0:
|
|
rule_lut = None
|
|
elif len(args) == 16:
|
|
rule_lut = [int(x.strip(",")) for x in args]
|
|
else:
|
|
lament(f"usage: {name}")
|
|
lament(f"usage: {name} {{rules 1 through 16}}")
|
|
sys.exit(1)
|
|
|
|
scale = 3
|
|
width, height = 960 // scale, 960 // scale
|
|
|
|
if rule_lut is None:
|
|
rule_lut = np.random.randint(-2, 2 + 1, size=(4, 4))
|
|
lament(" ".join(f"{x:+2}" for x in rule_lut.flat))
|
|
else:
|
|
rule_lut = np.array(rule_lut).reshape(4, 4)
|
|
|
|
print("P2") # magic code for an ascii Portable GrayMap (PGM) file
|
|
print(width * scale, height * scale)
|
|
print(255) # maximum color value
|
|
|
|
row = initialize(width)
|
|
|
|
for i in range(height):
|
|
# left, center, right:
|
|
L = np.roll(row, 1)
|
|
C = row.copy()
|
|
R = np.roll(row, -1)
|
|
|
|
diffusion = (L + C + C + R + 2) // 4
|
|
|
|
# v = [0,1,2,3,1,0,3,2,2,3,0,1,3,2,1,0][V]
|
|
y = (L ^ (L >> 2)) % 4
|
|
x = (R ^ (R >> 2)) % 4
|
|
|
|
delta = rule_lut[y, x]
|
|
row = diffusion + delta
|
|
|
|
limit(row)
|
|
render(row, scale)
|