-
-
Save aalhour/533b8ea1a1d71bbfb026c16d956aa6d2 to your computer and use it in GitHub Desktop.
Elementary Cellular Automata PNG images generator. Based on Nicolas Seriot's gist (https://goo.gl/fh4d5Y).
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
#!/usr/bin/env python3 | |
__author__ = "Ahmad Alhour" | |
__date__ = "2017-10-29" | |
__website__ = "https://gist.github.com/aalhour/533b8ea1a1d71bbfb026c16d956aa6d2" | |
""" | |
eca.py | |
Elementary Cellular Automata PNG images generator. Based on Nicolas Seriot's gist (https://goo.gl/fh4d5Y). | |
How to use: | |
python3 eca.py -h | |
Resources: | |
- http://mathworld.wolfram.com/CellularAutomaton.html | |
- http://mathworld.wolfram.com/ElementaryCellularAutomaton.html | |
- https://en.wikipedia.org/wiki/Elementary_cellular_automaton | |
- https://en.wikipedia.org/wiki/Rule_110 | |
""" | |
import os | |
import argparse | |
from typing import List | |
import png | |
SUPPORTED_ECA_RULES = [ | |
30, # 0b00011110 | |
54, # 0b00110110 | |
60, # 0b00111100 | |
62, # 0b00111110 | |
90, # 0b01011010 | |
94, # 0b01011110 | |
102, # 0b01100110 | |
110, # 0b01101110 | |
122, # 0b01111010 | |
126, # 0b01111110 | |
150, # 0b10010110 | |
158, # 0b10011110 | |
182, # 0b10110110 | |
188, # 0b10111100 | |
190, # 0b10111110 | |
220, # 0b11011100 | |
222, # 0b11011110 | |
250 # 0b11111010 | |
] | |
def create_argument_parser() -> argparse.ArgumentParser: | |
""" | |
Creates an argument parser for the command line. | |
""" | |
arg_parser = argparse.ArgumentParser(prog="eca") | |
arg_parser.add_argument( | |
"--width", | |
type=int, default=1000, help="Image width in pixels, i.e.: 1000.") | |
arg_parser.add_argument( | |
"--height", | |
type=int, default=1000, help="Image height in pixels, i.e.: 1000.") | |
arg_parser.add_argument( | |
"--rule", | |
type=int, default=110, choices=SUPPORTED_ECA_RULES, help="Elementary Cellular Automaton Rule.") | |
arg_parser.add_argument( | |
"--name", | |
type=str, action="store", default=None, | |
help="Desired name of the image. If not specified a random name will be generated.") | |
return arg_parser | |
def generate_random_str() -> str: | |
""" | |
Generates a pseudo-random hexadecimal string. | |
""" | |
return os.urandom(6).hex() | |
def write_data_to_png_file(matrix: List[List[int]], filename: str) -> None: | |
""" | |
Writes a given PNG matrix (2D array) to a file with the specified filename string. | |
""" | |
ll = [[255 - b * 255 for b in row] for row in matrix] | |
png.from_array(ll, 'L').save(filename) | |
def compute_eca_matrix(width: int=1000, height: int=1000, rule: int=110) -> List[List[int]]: | |
""" | |
Given an 2D image size, in addition to an ECA rule in integer representation, | |
generate an ECA matrix to be written to an image file. | |
""" | |
### | |
# Helper functions | |
# | |
def next_row(row: List[int], rule_bits: List[int]) -> List[int]: | |
""" | |
Generates the next row in the PNG matrix given the current row vector and | |
the bytearray of the ECA rule. | |
""" | |
r = row[-1:] + row + row[:1] | |
return [rule_bits[r[i]*4 + b*2 + r[i+2]] for (i, b) in enumerate(row)] | |
# Get the rule's bits from its decimal representation. | |
rule_bits = [int(x) for x in '{0:08b}'.format(rule)[::-1]] | |
# Setup the first row in the matrix. | |
row = [0] * width | |
row[width//2] = 1 | |
eca_matrix = [row] | |
for i in range(height-1): | |
row = next_row(row, rule_bits) | |
eca_matrix.append(row) | |
return eca_matrix | |
def main(): | |
""" | |
Entry point. Parses the command line for parameters and generates the PNG image accordingly. | |
""" | |
# Create a new argument parser. | |
arg_parser = create_argument_parser() | |
# Parse teh command line arguments. | |
args = arg_parser.parse_args() | |
# Compute the ECA Matrix. | |
eca_matrix = compute_eca_matrix(args.width, args.height, args.rule) | |
# Construct the image and file names. | |
image_name = args.name if args.name else generate_random_str() | |
file_name = "{name}__r{rule}_w{width}_h{height}.png".format( | |
name=image_name, rule=args.rule, width=args.width, height=args.height) | |
# Write the matrix to a new PNG file. | |
write_data_to_png_file(eca_matrix, file_name) | |
# Print the file path back to the user. | |
print("Image was saved at: {}".format(os.path.abspath(file_name))) | |
if __name__ == "__main__": | |
main() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment