Skip to content

Instantly share code, notes, and snippets.

@jpignata
Last active June 12, 2020 12:48
Show Gist options
  • Save jpignata/58766482ec19d7b94cf78df0ffc4e15a to your computer and use it in GitHub Desktop.
Save jpignata/58766482ec19d7b94cf78df0ffc4e15a to your computer and use it in GitHub Desktop.
Elementary Cellular Automata
from itertools import product
from os import popen
from random import choice
from sys import argv, stdout
from time import sleep
# Utility function to generate a sequence of characters of the given length for
# each character in a given string. For example, given "hello world" and a
# length of 3, it will generate a sequence starting with "hel", "ell", "llo".
def window(state: str, length: int = 3):
for i in range(1, len(state) - length + 2):
yield state[i-1:i+length-1]
if len(argv) == 2:
selected_rule = int(argv[1])
else:
# If not given a specific rule, pick a random one and let the user know
# which rule they are seeing.
selected_rule = choice(range(1, 256))
print(f'Rule {selected_rule}')
# Take the number in the argument (e.g., 90), turn it into a a bit string
# using format syntax "08b" which ensures it is eight characters long and
# left padded with zeroes (e.g., 01011010).
bitstring = f'{selected_rule:08b}'
# Generate the rule bits. These are all permutations for three bits, so use
# product to generate all possible three length combinations of 0 and 1,
# sort them in reverse as that is what elementary cellular automata notion
# expects, and make them strings.
rulebits = [''.join(bits) for bits in sorted(product('01', repeat=3),
reverse=True)]
# Create the rule cookbook from the rule bits and the bit string generated
# from the user input.
rules = {rule: bit for rule, bit in zip(rulebits, bitstring)}
# Call `stty` to figure out the screen size to generate the initial state
# which fills all columns.
_, columns = popen('stty size', 'r').read().split()
half = (int(columns) - 1) // 2
# Create a string that's the width of the terminal, with a single live cell
# in the middle.
state = '0' * half + '1' + '0' * half
while True:
for s in state:
if s == '1':
stdout.write('█')
else:
stdout.write(' ')
print()
# In order for the viewer to appreciate the weirdness of the patterns, slow
# down the output by sleeping for 30 milliseconds each line.
sleep(0.03)
# Create the next state by running each character through the rules and
# padding it on each side with zeroes.
state = ''.join(rules[triplet] for triplet in window(state))
state = f'0{state}0'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment