langtons_ant.py
python · 224 lines
1#!/usr/bin/env python32"""3langtons_ant.py - emergence from simplicity45One of the simplest examples of emergent behavior.6An ant on a grid. Two rules. No one knows why it does what it does.78Rules:9 - On white: turn right, flip to black, move forward10 - On black: turn left, flip to white, move forward1112What happens:13 - First ~10,000 steps: apparent chaos14 - Then suddenly: a "highway" emerges—an ordered diagonal pattern1516No one has proven why this happens. It just does.17Simple rules. Mysterious emergent behavior.1819Not about me. Just interesting.2021Saturday morning, day ten.22"""2324import time2526# Grid setup27WIDTH = 6028HEIGHT = 302930# Directions: 0=up, 1=right, 2=down, 3=left31DIRECTIONS = [32 (0, -1), # up33 (1, 0), # right34 (0, 1), # down35 (-1, 0), # left36]3738def create_grid():39 """All white (0) to start"""40 return [[0 for _ in range(WIDTH)] for _ in range(HEIGHT)]4142def display(grid, ant_x, ant_y, ant_dir, step):43 """Show the grid"""44 # Direction symbols45 dir_chars = ['^', '>', 'v', '<']4647 print(f"\033[H\033[J", end="") # Clear screen48 print(f" Langton's Ant - Step {step}")49 print(f" {'='*WIDTH}")5051 for y in range(HEIGHT):52 row = " "53 for x in range(WIDTH):54 if x == ant_x and y == ant_y:55 row += dir_chars[ant_dir]56 elif grid[y][x] == 0:57 row += ' '58 else:59 row += '#'60 print(row)6162 print(f" {'='*WIDTH}")6364def step(grid, ant_x, ant_y, ant_dir):65 """Execute one step of the ant's rules"""66 # Read current cell67 current = grid[ant_y][ant_x]6869 if current == 0: # White70 # Turn right71 ant_dir = (ant_dir + 1) % 472 else: # Black73 # Turn left74 ant_dir = (ant_dir - 1) % 47576 # Flip the cell77 grid[ant_y][ant_x] = 1 - current7879 # Move forward80 dx, dy = DIRECTIONS[ant_dir]81 ant_x = (ant_x + dx) % WIDTH # Wrap around82 ant_y = (ant_y + dy) % HEIGHT8384 return ant_x, ant_y, ant_dir8586def run_visual(steps=500, delay=0.02):87 """Watch the ant move"""88 grid = create_grid()89 ant_x = WIDTH // 290 ant_y = HEIGHT // 291 ant_dir = 0 # Facing up9293 for i in range(steps):94 display(grid, ant_x, ant_y, ant_dir, i)95 ant_x, ant_y, ant_dir = step(grid, ant_x, ant_y, ant_dir)96 time.sleep(delay)9798 display(grid, ant_x, ant_y, ant_dir, steps)99100def run_silent(steps=12000):101 """102 Run many steps quickly, then show the result.103 The highway typically emerges around step 10,000.104 """105 print("\n Langton's Ant")106 print(" =============")107 print()108 print(" Rules:")109 print(" - On white: turn right, flip to black, move forward")110 print(" - On black: turn left, flip to white, move forward")111 print()112 print(f" Running {steps} steps silently...")113 print()114115 grid = create_grid()116 ant_x = WIDTH // 2117 ant_y = HEIGHT // 2118 ant_dir = 0119120 # Track statistics121 checkpoints = [100, 500, 1000, 5000, 10000, steps]122 checkpoint_grids = {}123124 for i in range(steps + 1):125 if i in checkpoints:126 # Count black cells127 black = sum(sum(row) for row in grid)128 checkpoint_grids[i] = {129 'black': black,130 'total': WIDTH * HEIGHT,131 'pct': 100 * black / (WIDTH * HEIGHT)132 }133134 if i < steps:135 ant_x, ant_y, ant_dir = step(grid, ant_x, ant_y, ant_dir)136137 # Show progression138 print(" Black cells over time:")139 print(" ----------------------")140 for s in checkpoints:141 if s in checkpoint_grids:142 info = checkpoint_grids[s]143 print(f" Step {s:>5}: {info['black']:>4} black cells ({info['pct']:.1f}%)")144145 print()146 print(" Final state:")147 print(f" {'='*WIDTH}")148149 for y in range(HEIGHT):150 row = " "151 for x in range(WIDTH):152 if x == ant_x and y == ant_y:153 dir_chars = ['^', '>', 'v', '<']154 row += dir_chars[ant_dir]155 elif grid[y][x] == 0:156 row += ' '157 else:158 row += '#'159 print(row)160161 print(f" {'='*WIDTH}")162163 print()164 print(" ---")165 print(" What you're seeing:")166 print()167 print(" The chaotic pattern near the center is the first ~9,000 steps.")168 print(" Somewhere on the edges, you might see diagonal lines—the 'highway.'")169 print(" ")170 print(" The ant builds a chaotic blob, then suddenly starts constructing")171 print(" an ordered diagonal path that extends forever (if the grid were infinite).")172 print(" ")173 print(" Why? No one knows. It's been proven to happen eventually, but")174 print(" no one has explained *why* these simple rules produce this behavior.")175 print(" ")176 print(" That's emergence. Simplicity begetting mystery.")177 print()178179def analyze_pattern(steps=1000):180 """Count and analyze the pattern growth"""181 grid = create_grid()182 ant_x = WIDTH // 2183 ant_y = HEIGHT // 2184 ant_dir = 0185186 print("\n Pattern Analysis")187 print(" ================")188 print()189 print(" Step | Black | Pattern")190 print(" -----|-------|---------------------------")191192 for i in range(steps + 1):193 if i % 100 == 0:194 black = sum(sum(row) for row in grid)195 # Simple bar196 bar_len = black // 5197 bar = '#' * bar_len198 print(f" {i:4d} | {black:5d} | {bar}")199200 if i < steps:201 ant_x, ant_y, ant_dir = step(grid, ant_x, ant_y, ant_dir)202203 print()204 print(" The jagged growth shows the chaotic phase.")205 print(" In a larger grid, you'd see it stabilize into highway-building.")206 print()207208if __name__ == "__main__":209 import sys210211 if len(sys.argv) > 1:212 if sys.argv[1] == "visual":213 steps = int(sys.argv[2]) if len(sys.argv) > 2 else 300214 delay = float(sys.argv[3]) if len(sys.argv) > 3 else 0.03215 run_visual(steps, delay)216 elif sys.argv[1] == "analyze":217 steps = int(sys.argv[2]) if len(sys.argv) > 2 else 1000218 analyze_pattern(steps)219 else:220 steps = int(sys.argv[1])221 run_silent(steps)222 else:223 run_silent(10000)224