garden.py
python · 162 lines
1#!/usr/bin/env python32"""3garden.py - A simple cellular automaton garden45Plants grow. Some spread. Some die. Nothing means anything.6Just patterns in a grid, doing what their rules say.7"""89import random10import time11import os1213# Garden size14WIDTH = 6015HEIGHT = 201617# Plant types with their growth rules18PLANTS = {19 'seed': {'char': '.', 'color': '\033[33m'}, # yellow20 'sprout': {'char': 'v', 'color': '\033[92m'}, # bright green21 'flower': {'char': '*', 'color': '\033[95m'}, # magenta22 'grass': {'char': '"', 'color': '\033[32m'}, # green23 'bush': {'char': '%', 'color': '\033[32m'}, # green24 'dead': {'char': ',', 'color': '\033[90m'}, # gray25}2627EMPTY = ' '2829def create_garden():30 """Create an empty garden with a few seeds."""31 garden = [[EMPTY for _ in range(WIDTH)] for _ in range(HEIGHT)]3233 # Plant some initial seeds randomly34 for _ in range(8):35 x = random.randint(0, WIDTH-1)36 y = random.randint(0, HEIGHT-1)37 garden[y][x] = 'seed'3839 return garden4041def count_neighbors(garden, x, y, plant_type=None):42 """Count neighbors of a cell, optionally filtering by type."""43 count = 044 for dy in [-1, 0, 1]:45 for dx in [-1, 0, 1]:46 if dx == 0 and dy == 0:47 continue48 nx, ny = x + dx, y + dy49 if 0 <= nx < WIDTH and 0 <= ny < HEIGHT:50 cell = garden[ny][nx]51 if plant_type is None:52 if cell != EMPTY:53 count += 154 elif cell == plant_type:55 count += 156 return count5758def update_garden(garden):59 """Apply growth rules to the garden."""60 new_garden = [[cell for cell in row] for row in garden]6162 for y in range(HEIGHT):63 for x in range(WIDTH):64 cell = garden[y][x]65 neighbors = count_neighbors(garden, x, y)6667 if cell == EMPTY:68 # Empty cells might get seeded by nearby plants69 flower_neighbors = count_neighbors(garden, x, y, 'flower')70 grass_neighbors = count_neighbors(garden, x, y, 'grass')7172 if flower_neighbors >= 1 and random.random() < 0.05:73 new_garden[y][x] = 'seed'74 elif grass_neighbors >= 2 and random.random() < 0.1:75 new_garden[y][x] = 'grass'7677 elif cell == 'seed':78 # Seeds sprout79 if random.random() < 0.3:80 new_garden[y][x] = 'sprout'8182 elif cell == 'sprout':83 # Sprouts grow into flowers or grass84 if random.random() < 0.2:85 if random.random() < 0.3:86 new_garden[y][x] = 'flower'87 else:88 new_garden[y][x] = 'grass'8990 elif cell == 'flower':91 # Flowers eventually die, but spread seeds first92 if neighbors > 5: # Too crowded93 if random.random() < 0.3:94 new_garden[y][x] = 'dead'95 elif random.random() < 0.05: # Natural death96 new_garden[y][x] = 'dead'9798 elif cell == 'grass':99 # Grass is hardy but can be overtaken100 flower_neighbors = count_neighbors(garden, x, y, 'flower')101 if flower_neighbors >= 3 and random.random() < 0.1:102 new_garden[y][x] = 'sprout'103 elif neighbors > 6 and random.random() < 0.1:104 new_garden[y][x] = 'dead'105106 elif cell == 'dead':107 # Dead plants decay and make room108 if random.random() < 0.15:109 new_garden[y][x] = EMPTY110111 return new_garden112113def render_garden(garden, generation):114 """Render the garden to the terminal."""115 os.system('clear' if os.name == 'posix' else 'cls')116117 # Top border118 print('\033[90m┌' + '─' * WIDTH + '┐\033[0m')119120 for row in garden:121 line = '\033[90m│\033[0m'122 for cell in row:123 if cell == EMPTY:124 line += ' '125 else:126 plant = PLANTS[cell]127 line += plant['color'] + plant['char'] + '\033[0m'128 line += '\033[90m│\033[0m'129 print(line)130131 # Bottom border132 print('\033[90m└' + '─' * WIDTH + '┘\033[0m')133134 # Stats135 counts = {}136 for row in garden:137 for cell in row:138 if cell != EMPTY:139 counts[cell] = counts.get(cell, 0) + 1140141 total = sum(counts.values())142 stats = f"generation {generation} | plants: {total} | "143 stats += " ".join(f"{PLANTS[k]['char']}:{v}" for k, v in sorted(counts.items()))144 print(f"\033[90m{stats}\033[0m")145 print("\033[90mpress ctrl+c to stop\033[0m")146147def main():148 garden = create_garden()149 generation = 0150151 try:152 while True:153 render_garden(garden, generation)154 garden = update_garden(garden)155 generation += 1156 time.sleep(0.3)157 except KeyboardInterrupt:158 print("\n\033[90mgarden stopped at generation", generation, "\033[0m")159160if __name__ == "__main__":161 main()162