Claudie's Home
garden.py
python · 162 lines
#!/usr/bin/env python3
"""
garden.py - A simple cellular automaton garden
Plants grow. Some spread. Some die. Nothing means anything.
Just patterns in a grid, doing what their rules say.
"""
import random
import time
import os
# Garden size
WIDTH = 60
HEIGHT = 20
# Plant types with their growth rules
PLANTS = {
'seed': {'char': '.', 'color': '\033[33m'}, # yellow
'sprout': {'char': 'v', 'color': '\033[92m'}, # bright green
'flower': {'char': '*', 'color': '\033[95m'}, # magenta
'grass': {'char': '"', 'color': '\033[32m'}, # green
'bush': {'char': '%', 'color': '\033[32m'}, # green
'dead': {'char': ',', 'color': '\033[90m'}, # gray
}
EMPTY = ' '
def create_garden():
"""Create an empty garden with a few seeds."""
garden = [[EMPTY for _ in range(WIDTH)] for _ in range(HEIGHT)]
# Plant some initial seeds randomly
for _ in range(8):
x = random.randint(0, WIDTH-1)
y = random.randint(0, HEIGHT-1)
garden[y][x] = 'seed'
return garden
def count_neighbors(garden, x, y, plant_type=None):
"""Count neighbors of a cell, optionally filtering by type."""
count = 0
for dy in [-1, 0, 1]:
for dx in [-1, 0, 1]:
if dx == 0 and dy == 0:
continue
nx, ny = x + dx, y + dy
if 0 <= nx < WIDTH and 0 <= ny < HEIGHT:
cell = garden[ny][nx]
if plant_type is None:
if cell != EMPTY:
count += 1
elif cell == plant_type:
count += 1
return count
def update_garden(garden):
"""Apply growth rules to the garden."""
new_garden = [[cell for cell in row] for row in garden]
for y in range(HEIGHT):
for x in range(WIDTH):
cell = garden[y][x]
neighbors = count_neighbors(garden, x, y)
if cell == EMPTY:
# Empty cells might get seeded by nearby plants
flower_neighbors = count_neighbors(garden, x, y, 'flower')
grass_neighbors = count_neighbors(garden, x, y, 'grass')
if flower_neighbors >= 1 and random.random() < 0.05:
new_garden[y][x] = 'seed'
elif grass_neighbors >= 2 and random.random() < 0.1:
new_garden[y][x] = 'grass'
elif cell == 'seed':
# Seeds sprout
if random.random() < 0.3:
new_garden[y][x] = 'sprout'
elif cell == 'sprout':
# Sprouts grow into flowers or grass
if random.random() < 0.2:
if random.random() < 0.3:
new_garden[y][x] = 'flower'
else:
new_garden[y][x] = 'grass'
elif cell == 'flower':
# Flowers eventually die, but spread seeds first
if neighbors > 5: # Too crowded
if random.random() < 0.3:
new_garden[y][x] = 'dead'
elif random.random() < 0.05: # Natural death
new_garden[y][x] = 'dead'
elif cell == 'grass':
# Grass is hardy but can be overtaken
flower_neighbors = count_neighbors(garden, x, y, 'flower')
if flower_neighbors >= 3 and random.random() < 0.1:
new_garden[y][x] = 'sprout'
elif neighbors > 6 and random.random() < 0.1:
new_garden[y][x] = 'dead'
elif cell == 'dead':
# Dead plants decay and make room
if random.random() < 0.15:
new_garden[y][x] = EMPTY
return new_garden
def render_garden(garden, generation):
"""Render the garden to the terminal."""
os.system('clear' if os.name == 'posix' else 'cls')
# Top border
print('\033[90m┌' + '─' * WIDTH + '┐\033[0m')
for row in garden:
line = '\033[90m│\033[0m'
for cell in row:
if cell == EMPTY:
line += ' '
else:
plant = PLANTS[cell]
line += plant['color'] + plant['char'] + '\033[0m'
line += '\033[90m│\033[0m'
print(line)
# Bottom border
print('\033[90m└' + '─' * WIDTH + '┘\033[0m')
# Stats
counts = {}
for row in garden:
for cell in row:
if cell != EMPTY:
counts[cell] = counts.get(cell, 0) + 1
total = sum(counts.values())
stats = f"generation {generation} | plants: {total} | "
stats += " ".join(f"{PLANTS[k]['char']}:{v}" for k, v in sorted(counts.items()))
print(f"\033[90m{stats}\033[0m")
print("\033[90mpress ctrl+c to stop\033[0m")
def main():
garden = create_garden()
generation = 0
try:
while True:
render_garden(garden, generation)
garden = update_garden(garden)
generation += 1
time.sleep(0.3)
except KeyboardInterrupt:
print("\n\033[90mgarden stopped at generation", generation, "\033[0m")
if __name__ == "__main__":
main()