Skip to content

Instantly share code, notes, and snippets.

@samclane
Created April 6, 2023 00:16
Show Gist options
  • Save samclane/9e53fdee907c9c1f244a96d0c2ac75b7 to your computer and use it in GitHub Desktop.
Save samclane/9e53fdee907c9c1f244a96d0c2ac75b7 to your computer and use it in GitHub Desktop.
import sys
import random
import time
import string
class AsciiCanvas:
width: int
height: int
fill_char: str
canvas: list[list[str]]
def __init__(self, width, height, fill_char=' '):
self.width = width
self.height = height
self.fill_char = fill_char
self.canvas = [
[fill_char for _ in range(width)] for _ in range(height)]
def set_pixel(self, x, y, char):
if 0 <= x < self.width and 0 <= y < self.height:
self.canvas[int(y)][int(x)] = char
def __str__(self):
return '\n'.join([''.join(row) for row in self.canvas])
def clear(self):
sys.stdout.write("\033[2J") # Clear the screen
def move_cursor_to_origin(self):
sys.stdout.write("\033[H") # Move cursor to the top-left corner
sys.stdout.flush()
def update(self):
self.move_cursor_to_origin()
sys.stdout.write(str(self))
sys.stdout.flush()
class ReactionDiffusion:
width: int
height: int
dA: float
dB: float
f: float
k: float
grid: list[list[dict[str, float]]]
dt: float
def __init__(self, width, height, dA=1.0, dB=0.5, f=0.055, k=0.062, dt=1.0):
self.width = width
self.height = height
self.dA = dA
self.dB = dB
self.f = f
self.k = k
self.grid = [[{'a': 1, 'b': random.random() * 0.1} for _ in range(width)] for _ in range(height)]
self.dt = dt
def laplace(self, x, y, chem):
val = -self.grid[y][x][chem]
for j in range(-1, 2):
for i in range(-1, 2):
if 0 <= x + i < self.width and 0 <= y + j < self.height:
val += self.grid[y + j][x + i][chem] * (0.2 if i == 0 or j == 0 else 0.05)
return val
def step(self):
new_grid = [[{'a': 0., 'b': 0.} for _ in range(self.width)] for _ in range(self.height)]
for y in range(self.height):
for x in range(self.width):
a = self.grid[y][x]['a']
b = self.grid[y][x]['b']
ab2 = a * b * b
new_a = a + self.dt * (self.dA * self.laplace(x, y, 'a') - ab2 + self.f * (1 - a))
new_b = b + self.dt * (self.dB * self.laplace(x, y, 'b') + ab2 - (self.k + self.f) * b)
new_grid[y][x]['a'] = min(max(new_a, 0), 1)
new_grid[y][x]['b'] = min(max(new_b, 0), 1)
self.grid = new_grid
def render(self, canvas, ascii_chars):
for y in range(self.height):
for x in range(self.width):
b = self.grid[y][x]['b']
index = int(b * (len(ascii_chars) - 1))
canvas.set_pixel(x, y, ascii_chars[index])
def main():
width, height = 80, 40
canvas = AsciiCanvas(width, height)
rd = ReactionDiffusion(width, height, dt=0.05)
ascii_chars = string.ascii_letters + string.digits + string.punctuation + ' '
ascii_chars = sorted(set(ascii_chars))
canvas.clear()
while True:
rd.step()
rd.render(canvas, ascii_chars)
canvas.update()
time.sleep(1/144)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment