Last active
June 12, 2020 12:48
-
-
Save jpignata/58766482ec19d7b94cf78df0ffc4e15a to your computer and use it in GitHub Desktop.
Elementary Cellular Automata
This file contains 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
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