├── .gitignore ├── LICENSE ├── README.md └── wolfram.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Avy Faingezicht 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # automata 2 | 3 | ![rule30](https://dl.dropboxusercontent.com/u/16238897/avyfain.io/seed-wolfram-small.gif "Wolfram's Rule 30") 4 | 5 | A companion repo for my [blog post](http://faingezicht.com/articles/2017/01/23/wolfram/) about Wolfram's elementary cellular automata. 6 | 7 | Currently requires Python 3.x, and no other dependencies. 8 | 9 | ## Usage 10 | 11 | To run with the default arguments, just run: 12 | ```bash 13 | $ python3 wolfram.py 14 | Using rule 30 15 | █ 16 | ███ 17 | ██ █ 18 | ██ ████ 19 | ██ █ █ 20 | ██ ████ ███ 21 | ██ █ █ █ 22 | ██ ████ ██████ 23 | ██ █ ███ █ 24 | ██ ████ ██ █ ███ 25 | ██ █ █ ████ ██ █ 26 | ██ ████ ██ █ █ ████ 27 | ██ █ ███ ██ ██ █ █ 28 | ██ ████ ██ ███ ███ ██ ███ 29 | ██ █ █ ███ █ ███ █ █ 30 | ██ ████ ██ █ █ █████ ███████ 31 | ██ █ ███ ████ █ ███ █ 32 | ██ ████ ██ ███ ██ ██ █ ███ 33 | ██ █ █ ███ █ ██ ███ ████ ██ █ 34 | ██ ████ ██ █ ██████ █ █ ███ ████ 35 | ██ █ ███ ████ ████ ███ ██ █ █ 36 | ██ ████ ██ ███ █ ██ █ █ █ ███ ███ 37 | ██ █ █ ███ █ ███ ██ █ ███ ██ █ █ █ █ 38 | ██ ████ ██ █ ███ █ █ ████ █ █ ██ ██████ 39 | ██ █ ███ ████ ██ █████ █ █████ █ █ █ 40 | ██ ████ ██ ███ █ ██ █ █ ██ █ █████ ███ 41 | ██ █ █ ███ █ ██ █ ████ ██ █ ██ ██ █ ██ █ 42 | ██ ████ ██ █ ███ █ █ █ ███ ████ █ ██ █ ██ █ ████ 43 | ██ █ ███ ████ ████ ██ ██ ███ █ █ ████ █ █ █ 44 | ██ ████ ██ ███ █ ██ █ █ ███ █ ██ ████ ███ ██ ███ 45 | ██ █ █ ███ █ ██ █ █ █████ █ ██████ █ █ ██ █ █ █ 46 | ██ ████ ██ █ ███ █ █ ████ ████ ████ ██ █ █ █████████ 47 | ██ █ ███ ████ ████ █ █ ██ █ ██ █ █ █ █ █ 48 | ██ ████ ██ ███ █ ██ ██ ███ ██ █ ███ ██ █ █████ █ ██ ███ 49 | ██ █ █ ███ █ ██ █ █ ██ █ █ █ █ █ ████ █ █ █ ██ █ 50 | ██ ████ ██ █ ███ █ █ ████ ████ █████ ██ █████ █ ██ █ ██ ██ ████ 51 | ██ █ ███ ████ ████ █ █ █ █ █ █ ███ ██ █ █ ███ █ █ 52 | ██ ████ ██ ███ █ ██ ██ ███ ███ ██████ ██ █ █ ███ █ █ ████ ███ 53 | ██ █ █ ███ █ ██ █ █ ██ ███ ███ █ ██ ███ ██ ██ █ █ ████ █ █ 54 | ``` 55 | 56 | To get a random seed, use the `-r` flag: 57 | ```bash 58 | $ python3 wolfram.py -r 59 | Here's a random initial state 60 | Using rule 30 61 | █ ███ █████ █ █ ██ █ ██ ██ █ ███ █ ██ ██ 62 | █ █ ██ ███ █ █ ███ █ ███ █ █ ████ █ ██ 63 | ████████ █ ██ █ █ █ █ █ ███ ██ ██ ████ 64 | █ ████ █ ██ █ ██ ██ ████ █ █ █ ██ █ 65 | ██ ██ █ █ █ █ █ █ █ █████ ████ █ ██ 66 | █ █ ██ █ ██ ████ ████████ ██ █ █ █ █ 67 | █ ██ ██ ████ █ █ █ ██ ███ ██ ██ 68 | █ █ ███ ███ ████ ███ █████ █ ██ ███ █ 69 | █ █ █ █ █ ██ ███ █ ██ █ █ ███ ████ 70 | █ █ ██ █████ █ █ ██ ████ ██ █ ██ █ █ ███ 71 | █ █ █ █ █ █ █ ███ ███ ██ ██ █ ████ █ 72 | █ █ █████ ██ █ █ █ █ ██ ███ █ ███ █ ████ 73 | █ █ █ █ ██ █ █ ████ █ ███ ███ █ ██ ██ █ 74 | █ █ ██ ██ █ ███ █ █ █ █ ███ ██ ██ █ █ ██ 75 | █ █ █ ███ █ █ █ ██ ██ ████ █ ██ █ ███ █ █ 76 | █ █ █ █ ███ ██ ██ █ ███ █ ███ █ ███ █ █ ██ 77 | █ █ █ ████ █ █ █ █ ████ ██ █ █ ██ ██ █ 78 | █ █ █ █ █ ████████ ████ █ █ ██ ██ ██ █ ██ 79 | █ █ █ ██ ██ █ █ █ ██ █ █ █ █ ██████ 80 | █ █ █ █ █ ██ ███ █████ █ ███████ █ █ 81 | █ █ █ ███████ █ ██ █ ███ █ ██ ██ 82 | █ █ █ █ ██ ██ █ ███ ██ ██ ██ █ ██ 83 | █ █ █ ██ ██ ███ █ █ ███ █ ██ █ ██ █ █ █ 84 | █ █ █ █ █ ██ █ ███ ████ █ █ ██ ██ ███ █ █ 85 | █ █ █ █ ██ ██ ██████ █ █ ██ ████ █ █ █ █ 86 | █ █ █ █ █ █ █ █ ███ ██ █ █ ███████ ██ █ 87 | █ █ █ █ ████████ ██ █ █ █████ ██ █ █ 88 | █ █ █ █ █ █ ██ ██ █████ █ █ █████ 89 | █ █ █ █ ██ ██ █ ███ █ █ ██ ██ ██ 90 | █ █ █ █ █ █ ██ █ █ ████ ███ ██ █ █ ██ █ 91 | █ █ █ █ █ ██ ██ ███ ████ ███ █ ███ █ █ ██ 92 | █ █ █ █ █ █ █ █ █ █ ██ █ ██ █ █ ████ █ 93 | █ █ █ █ █ ████████ ███ ██ █ ███ █ ██ ██ █ ██ 94 | █ █ █ █ █ █ █ █ █ █ ████ █ ██ ██ 95 | █ █ █ █ █ ██ ███ █████ ██ ██ ██████ ███ █ 96 | █ █ █ █ █ █ █ ██ █ █ █ █ ██ █ █ 97 | █ █ █ █ █ █ ██ ██ █ ███ █████ █ █ █ ███ ██ 98 | █ █ █ █ █ █ █ ███ █ █ █ ██ █ █ ██ ██ █ 99 | █ █ █ █ █ █ █ █ ███ ████ █ █ ██ █ █ ███ █ ███ 100 | █ █ █ █ █ █ █ ████ █ █ ██ ██ █ █ █ █ █ 101 | ``` 102 | 103 | To pick a different rule, use the `--rule` argument: 104 | ```bash 105 | $ python3 wolfram.py -r --rule 110 106 | Here's a random initial state 107 | Using rule 110 108 | █ ██ ██ █ █ ██ █ █ █ ████████ ███ █ █ ██ 109 | ███████ ███████ ██ ██ ██ ██ █ ██ █ ██████ 110 | █ █ ██ █████████████ ███████ ██ █ 111 | █ █████ ██ █ ██ ████ ██ 112 | █ ██ █ ███ ██ ███ ██ █ ███ 113 | █ ███ ██ ██ █ ███ ██ █ ███ ██ ██ █ 114 | █ ██ █ ███ █████ ██ █ █████ ██ █████████ 115 | ████████ ███ █ ███████ █ █████ █ 116 | █ ███ █ ██ ██ █ ████ █ ██ 117 | █ ██ ███ ███ ███ ██ ██ █ ██ ███ 118 | █ █████ ███ █ ██ █ ██████ ██ ███ ██ █ 119 | █ ██ ███ ███ █████ ██ ██████ █ █████ 120 | █ ███ ██ ███ █ ██ █ ███ ██ ███ ██ █ 121 | █ ██ █ █████ ███ ███ ████ █ ███ ██ █ ███ ██ 122 | ████████ ███ ███ █ ██ ███ ██ █ ███████ █ ███ 123 | █ █ ██ ███ ██████ ██ ██████ ██ █████ █ 124 | █ ██ █████ ███ ██████ ████ ██ ███ 125 | █ █████ ███ █ ██ █ ██ █ ███ ██ █ 126 | █ ██ █ ██ ███ ███ ██ ███ ██ ██ █ █████ 127 | █ ███ ██ █████ █ ██ █ ███ ██ ████ ███████ █ 128 | █ ██ █ █████ ████████ ██ ██████ ███ █ ██ 129 | ████████ █ ██ ██████ █ ██ █ ██ ███ 130 | █ █ ██ ███ ██ █ ███████ █████ █ 131 | █ ██ █████ █ ███ ██ ██ █ ██ ███ 132 | █ █████ ███ ██ █ ███ ███ ██ ███ ██ █ 133 | █ ██ █ ██ █ █████ ██ ███ █ █████ █ █████ 134 | █ ███ ██ █████ ██ ██████ ███ ██ █████ █ 135 | █ ██ █ █████ ████ ██ ███ █ ███ ██ █ ██ 136 | ████████ █ ██ █ ███ ██ █████ █ ███ ██ ███ 137 | █ █ ██ ███ ████ █ █████ █████ █ █████ █ 138 | █ ██ █████ ███ ███ ██ █ ██ █████ ███ 139 | █ █████ ███ █ ██ ████ ██ ███ ██ █ ██ █ 140 | █ ██ █ ██ ████████ █ █████ █ ███ ██ █████ 141 | █ ███ ██ █████ █ ████ █████ █ █████ █ 142 | █ ██ █ █████ █ ████ █ ██ █████ █ ██ 143 | ████████ █ ██ ██ █ ██ ███ ██ █ ██ ███ 144 | █ █ ██ ███ ███ ███████ █ ███ ██ █████ █ 145 | █ ██ █████ █ ██ ███ █████ █ █████ ███ 146 | █ █████ ███ █████ █ ██ █████ █ ██ █ 147 | █ ██ █ ██ ███ ███ ███ ██ █ ██ █████ 148 | ``` 149 | -------------------------------------------------------------------------------- /wolfram.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import argparse 4 | from random import choice 5 | from time import sleep 6 | 7 | if hasattr(os, 'get_terminal_size'): 8 | terminal_size_fn = os.get_terminal_size 9 | else: 10 | from backports.shutil_get_terminal_size import get_terminal_size as terminal_size_fn 11 | 12 | MAX_TIME = terminal_size_fn().columns // 2 13 | RULES = {30: {"111": '0', "110": '0', "101": '0', "000": '0', 14 | "100": '1', "011": '1', "010": '1', "001": '1'}, 15 | 16 | 90: {"111": "0", "110": "1", "101": "0", "100": "1", 17 | "011": "1", "010": "0", "001": "1", "000": "0"}, 18 | 19 | 110: {"111": '0', "110": '1', "101": '1', "100": '0', 20 | "011": '1', "010": '1', "001": '1', "000": '0'}, 21 | 22 | 184: {"111": "1", "110": "0", "101": "1", "100": "1", 23 | "011": "1", "010": "0", "001": "0", "000": "0"} 24 | } 25 | 26 | 27 | def window(iterable, stride=3): 28 | for index in range(len(iterable) - stride + 1): 29 | yield iterable[index:index + stride] 30 | 31 | 32 | def generate_pattern(state, rule): 33 | for time in range(MAX_TIME): 34 | print_row(state) 35 | 36 | patterns = window(state) 37 | state = ''.join(rule[pat] for pat in patterns) 38 | state = '0{}0'.format(state) 39 | sleep(.1) 40 | 41 | 42 | def print_row(row): 43 | for cell in row: 44 | if cell == '1': 45 | sys.stdout.write(u'\u2588') 46 | else: 47 | sys.stdout.write(' ') 48 | sys.stdout.write('\n') 49 | 50 | 51 | def main(): 52 | parser = argparse.ArgumentParser() 53 | parser.add_argument("-r", "--random", 54 | help="get a random initial state", 55 | action="store_true") 56 | parser.add_argument("--rule", 57 | help="choose a rule by number", 58 | type=int, default=30) 59 | args = parser.parse_args() 60 | if args.random: 61 | print("Here's a random initial state") 62 | initial_state = ''.join(str(choice((0, 1))) for i in range(MAX_TIME)) 63 | else: 64 | initial_state = '0' * MAX_TIME + '1' + '0' * MAX_TIME 65 | 66 | rule = RULES[args.rule] 67 | print("Using rule", args.rule) 68 | generate_pattern(initial_state, rule) 69 | 70 | 71 | if __name__ == "__main__": 72 | main() 73 | --------------------------------------------------------------------------------