|
| 1 | +""" |
| 2 | +Return an image of 16 generations of one-dimensional cellular automata based on a given |
| 3 | +ruleset number |
| 4 | +https://mathworld.wolfram.com/ElementaryCellularAutomaton.html |
| 5 | +""" |
| 6 | + |
| 7 | +from typing import List |
| 8 | + |
| 9 | +from PIL import Image |
| 10 | + |
| 11 | +# Define the first generation of cells |
| 12 | +# fmt: off |
| 13 | +CELLS = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, |
| 14 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] |
| 15 | +# fmt: on |
| 16 | + |
| 17 | + |
| 18 | +def format_ruleset(ruleset: int) -> List[int]: |
| 19 | + """ |
| 20 | + >>> format_ruleset(11100) |
| 21 | + [0, 0, 0, 1, 1, 1, 0, 0] |
| 22 | + >>> format_ruleset(0) |
| 23 | + [0, 0, 0, 0, 0, 0, 0, 0] |
| 24 | + >>> format_ruleset(11111111) |
| 25 | + [1, 1, 1, 1, 1, 1, 1, 1] |
| 26 | + """ |
| 27 | + return [int(c) for c in f"{ruleset:08}"[:8]] |
| 28 | + |
| 29 | + |
| 30 | +def new_generation(cells: List[List[int]], rule: List[int], time: int) -> List[int]: |
| 31 | + population = len(cells[0]) # 31 |
| 32 | + next_generation = [] |
| 33 | + for i in range(population): |
| 34 | + # Get the neighbors of each cell |
| 35 | + left_neighbor = 0 if i == 0 else cells[time][i - 1] # special: leftmost cell |
| 36 | + right_neighbor = 0 if i == population - 1 else cells[time][i + 1] # rightmost |
| 37 | + # Define a new cell and add it to the new generation |
| 38 | + situation = 7 - int(f"{left_neighbor}{cells[time][i]}{right_neighbor}", 2) |
| 39 | + next_generation.append(rule[situation]) |
| 40 | + return next_generation |
| 41 | + |
| 42 | + |
| 43 | +def generate_image(cells: List[List[int]]) -> Image.Image: |
| 44 | + """ |
| 45 | + Convert the cells into a greyscale PIL.Image.Image and return it to the caller. |
| 46 | + >>> from random import random |
| 47 | + >>> cells = [[random() for w in range(31)] for h in range(16)] |
| 48 | + >>> img = generate_image(cells) |
| 49 | + >>> isinstance(img, Image.Image) |
| 50 | + True |
| 51 | + >>> img.width, img.height |
| 52 | + (31, 16) |
| 53 | + """ |
| 54 | + # Create the output image |
| 55 | + img = Image.new("RGB", (len(cells[0]), len(cells))) |
| 56 | + pixels = img.load() |
| 57 | + # Generates image |
| 58 | + for w in range(img.width): |
| 59 | + for h in range(img.height): |
| 60 | + color = 255 - int(255 * cells[h][w]) |
| 61 | + pixels[w, h] = (color, color, color) |
| 62 | + return img |
| 63 | + |
| 64 | + |
| 65 | +if __name__ == "__main__": |
| 66 | + rule_num = bin(int(input("Rule:\n").strip()))[2:] |
| 67 | + rule = format_ruleset(int(rule_num)) |
| 68 | + for time in range(16): |
| 69 | + CELLS.append(new_generation(CELLS, rule, time)) |
| 70 | + img = generate_image(CELLS) |
| 71 | + # Uncomment to save the image |
| 72 | + # img.save(f"rule_{rule_num}.png") |
| 73 | + img.show() |
0 commit comments