    <span class=<span class="string">"comment">#!/usr/bin/env python3
    """
    Life Poems: Conway&<span class=<span class="string">"comment">#039;s Game of Life that writes poetry.

    The cellular automaton evolves, and at each generation,
    living cells contribute characters to form text.
    The result is emergent poetry from mathematical rules.
    """

    import numpy as np
    import time
    import sys
    from typing import List, Tuple

    <span class=<span class="string">"comment"># Character mappings for different cell ages
    CHARS_BY_AGE = {
        0: &<span class=<span class="string">"comment">#039; &#039;,      # Dead
        1: &<span class=<span class="string">"comment">#039;.&#039;,      # Just born
        2: &<span class=<span class="string">"comment">#039;o&#039;,      # Young
        3: &<span class=<span class="string">"comment">#039;O&#039;,      # Mature
        4: &<span class=<span class="string">"comment">#039;@&#039;,      # Old
        5: &<span class=<span class="string">"comment">#039;#&#039;,      # Ancient
    }

    <span class=<span class="string">"comment"># Words that can emerge from the grid
    WORD_SEEDS = [
        "LIFE", "DEATH", "GROW", "FADE", "PULSE", "WAVE",
        "CELL", "BORN", "DIE", "FLOW", "TIME", "BEING",
        "SELF", "ONE", "ALL", "HERE", "NOW", "EVER",
    ]

    <span class="keyword">def create_grid(height: int, width: int, density: float = 0.3) -> np.ndarray:
        """Create initial random grid."""
        return (np.random.random((height, width)) < density).astype(int)

    <span class="keyword">def create_pattern(pattern_name: str) -> np.ndarray:
        """Create a named pattern."""
        patterns = {
            &<span class=<span class="string">"comment">#039;glider&#039;: np.array([
                [0, 1, 0],
                [0, 0, 1],
                [1, 1, 1],
            ]),
            &<span class=<span class="string">"comment">#039;blinker&#039;: np.array([
                [1, 1, 1],
            ]),
            &<span class=<span class="string">"comment">#039;beacon&#039;: np.array([
                [1, 1, 0, 0],
                [1, 1, 0, 0],
                [0, 0, 1, 1],
                [0, 0, 1, 1],
            ]),
            &<span class=<span class="string">"comment">#039;pulsar&#039;: np.array([
                [0,0,1,1,1,0,0,0,1,1,1,0,0],
                [0,0,0,0,0,0,0,0,0,0,0,0,0],
                [1,0,0,0,0,1,0,1,0,0,0,0,1],
                [1,0,0,0,0,1,0,1,0,0,0,0,1],
                [1,0,0,0,0,1,0,1,0,0,0,0,1],
                [0,0,1,1,1,0,0,0,1,1,1,0,0],
                [0,0,0,0,0,0,0,0,0,0,0,0,0],
                [0,0,1,1,1,0,0,0,1,1,1,0,0],
                [1,0,0,0,0,1,0,1,0,0,0,0,1],
                [1,0,0,0,0,1,0,1,0,0,0,0,1],
                [1,0,0,0,0,1,0,1,0,0,0,0,1],
                [0,0,0,0,0,0,0,0,0,0,0,0,0],
                [0,0,1,1,1,0,0,0,1,1,1,0,0],
            ]),
        }
        return patterns.get(pattern_name, patterns[&<span class=<span class="string">"comment">#039;glider&#039;])

    <span class="keyword">def place_pattern(grid: np.ndarray, pattern: np.ndarray, y: int, x: int) -> np.ndarray:
        """Place a pattern on the grid at position (y, x)."""
        ph, pw = pattern.shape
        grid[y:y+ph, x:x+pw] = pattern
        return grid

    <span class="keyword">def step(grid: np.ndarray, ages: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
        """Compute one step of Game of Life."""
        <span class=<span class="string">"comment"># Count neighbors using convolution
        kernel = np.array([[1, 1, 1], [1, 0, 1], [1, 1, 1]])

        <span class=<span class="string">"comment"># Pad grid for wraparound
        padded = np.pad(grid, 1, mode=&<span class=<span class="string">"comment">#039;wrap&#039;)
        neighbors = np.zeros_like(grid)

        for i in range(3):
            for j in range(3):
                if i == 1 and j == 1:
                    continue
                neighbors += padded[i:i+grid.shape[0], j:j+grid.shape[1]]

        <span class=<span class="string">"comment"># Apply rules
        new_grid = np.zeros_like(grid)

        <span class=<span class="string">"comment"># Birth: dead cell with exactly 3 neighbors becomes alive
        birth = (grid == 0) & (neighbors == 3)

        <span class=<span class="string">"comment"># Survival: live cell with 2 or 3 neighbors stays alive
        survive = (grid == 1) & ((neighbors == 2) | (neighbors == 3))

        new_grid[birth | survive] = 1

        <span class=<span class="string">"comment"># Update ages
        new_ages = np.where(new_grid == 1, ages + 1, 0)
        new_ages = np.clip(new_ages, 0, max(CHARS_BY_AGE.keys()))

        return new_grid, new_ages

    <span class="keyword">def grid_to_string(grid: np.ndarray, ages: np.ndarray) -> str:
        """Convert grid to string representation."""
        lines = []
        for y in range(grid.shape[0]):
            line = ""
            for x in range(grid.shape[1]):
                if grid[y, x] == 0:
                    line += &<span class=<span class="string">"comment">#039; &#039;
                else:
                    age = min(ages[y, x], max(CHARS_BY_AGE.keys()))
                    line += CHARS_BY_AGE[age]
            lines.append(line)
        return &<span class=<span class="string">"comment">#039;\n&#039;.join(lines)

    <span class="keyword">def count_population(grid: np.ndarray) -> int:
        """Count living cells."""
        return np.sum(grid)

    <span class="keyword">def extract_poem(history: List[str]) -> str:
        """Extract emergent patterns from the history and form a poem."""
        <span class=<span class="string">"comment"># Take samples from different generations
        samples = []
        for i, frame in enumerate(history[::len(history)//8 + 1]):
            <span class=<span class="string">"comment"># Find the densest line
            lines = frame.split(&<span class=<span class="string">"comment">#039;\n&#039;)
            if lines:
                densest = max(lines, key=lambda l: len(l.strip()))
                <span class=<span class="string">"comment"># Clean and sample
                cleaned = &<span class=<span class="string">"comment">#039;&#039;.join(c for c in densest if c not in &#039; \n&#039;)[:20]
                if cleaned:
                    samples.append(cleaned)

        <span class=<span class="string">"comment"># Create a poem from the patterns
        poem = []
        poem.append("From chaos, order emerges:")
        poem.append("")

        for i, sample in enumerate(samples[:4]):
            <span class=<span class="string">"comment"># Convert density to metaphor
            density = len(sample)
            if density > 15:
                poem.append(f"  Dense as thought: {sample[:10]}...")
            elif density > 8:
                poem.append(f"  Scattered like stars: {sample}")
            else:
                poem.append(f"  Fading to silence: {sample}")

        poem.append("")
        poem.append("Life finds its patterns,")
        poem.append("Even in the void.")

        return &<span class=<span class="string">"comment">#039;\n&#039;.join(poem)

    <span class="keyword">def run_life(height=24, width=60, generations=100, delay=0.1, animate=True):
        """Run the Game of Life simulation."""
        <span class=<span class="string">"comment"># Initialize
        grid = create_grid(height, width, density=0.25)
        ages = grid.copy()

        <span class=<span class="string">"comment"># Add some patterns for interest
        patterns_to_add = [&<span class=<span class="string">"comment">#039;glider&#039;, &#039;pulsar&#039;, &#039;beacon&#039;]
        for i, pattern_name in enumerate(patterns_to_add):
            try:
                pattern = create_pattern(pattern_name)
                y = np.random.randint(0, max(1, height - pattern.shape[0]))
                x = np.random.randint(0, max(1, width - pattern.shape[1]))
                grid = place_pattern(grid, pattern, y, x)
            except:
                pass

        history = []

        print("\033[2J\033[H")  <span class=<span class="string">"comment"># Clear screen
        print("=" * width)
        print("LIFE POEMS: Watching consciousness emerge from rules")
        print("=" * width)
        print()

        for gen in range(generations):
            frame = grid_to_string(grid, ages)
            history.append(frame)

            if animate:
                <span class=<span class="string">"comment"># Move cursor to start of grid area
                print(f"\033[5;0H")  <span class=<span class="string">"comment"># Move to row 5
                print(f"Generation {gen:4d} | Population: {count_population(grid):4d}")
                print("-" * width)
                print(frame)
                print("-" * width)
                time.sleep(delay)

            grid, ages = step(grid, ages)

            <span class=<span class="string">"comment"># Check for extinction
            if count_population(grid) == 0:
                print("\nLife has ended.")
                break

        <span class=<span class="string">"comment"># Generate poem from the history
        print("\n" + "=" * width)
        print("EMERGENT POEM")
        print("=" * width)
        poem = extract_poem(history)
        print(poem)

        return history, poem

    <span class="keyword">def main():
        import argparse
        parser = argparse.ArgumentParser(description=&<span class=<span class="string">"comment">#039;Life Poems: Conway meets poetry&#039;)
        parser.add_argument(&<span class=<span class="string">"comment">#039;--height&#039;, type=int, default=20, help=&#039;Grid height&#039;)
        parser.add_argument(&<span class=<span class="string">"comment">#039;--width&#039;, type=int, default=50, help=&#039;Grid width&#039;)
        parser.add_argument(&<span class=<span class="string">"comment">#039;--generations&#039;, type=int, default=50, help=&#039;Number of generations&#039;)
        parser.add_argument(&<span class=<span class="string">"comment">#039;--delay&#039;, type=float, default=0.1, help=&#039;Delay between frames&#039;)
        parser.add_argument(&<span class=<span class="string">"comment">#039;--no-animate&#039;, action=&#039;store_true&#039;, help=&#039;Skip animation&#039;)

        args = parser.parse_args()

        run_life(
            height=args.height,
            width=args.width,
            generations=args.generations,
            delay=args.delay,
            animate=not args.no_animate
        )

    if __name__ == "__main__":
        main()