Skip to main content

life/
life.rs

1//! Conway's Game of Life on a 24 × 24 torus, where each generation is one pass
2//! through a circuit compiled from an ordinary `life_step` function. Every
3//! generation is checked against the function, then printed as one line of
4//! `.` and `#`, row after row.
5//!
6//! cargo run --release --example life > generations.txt
7
8use handwired_rs::TensorExecutor;
9use handwired_rs::dsl::Params;
10use handwired_rs::handwired;
11
12const N: usize = 24;
13
14#[handwired]
15fn life_step(grid: [[bool; 24]; 24]) -> [[bool; 24]; 24] {
16    let mut next: [[bool; 24]; 24] = [[false; 24]; 24];
17    for row in 0..24 {
18        for column in 0..24 {
19            let mut neighbors: u8 = 0;
20            for dr in 0..3 {
21                for dc in 0..3 {
22                    if dr != 1 || dc != 1 {
23                        // Wrap around the edges: the grid is a torus.
24                        neighbors += grid[(row + dr + 23) % 24][(column + dc + 23) % 24] as u8;
25                    }
26                }
27            }
28            next[row][column] = neighbors == 3 || (grid[row][column] && neighbors == 2);
29        }
30    }
31    next
32}
33
34fn main() {
35    let compiled = life_step_circuit();
36    let report = compiled.verify(200).unwrap();
37    eprintln!(
38        "life_step: {} live operations in {} layers; verified on {} random grids",
39        compiled.program.live_operation_count(),
40        compiled.program.layers.len(),
41        report.checked
42    );
43    // Two gliders, a lightweight spaceship and an R-pentomino.
44    let mut grid = [[false; N]; N];
45    for (row, column) in [(1, 2), (2, 3), (3, 1), (3, 2), (3, 3)] {
46        grid[row][column] = true;
47    }
48    for (row, column) in [(14, 4), (15, 5), (16, 3), (16, 4), (16, 5)] {
49        grid[row][column] = true;
50    }
51    for (row, column) in [(4, 14), (4, 17), (5, 18), (6, 14), (6, 18), (7, 15), (7, 16), (7, 17), (7, 18)] {
52        grid[row][column] = true;
53    }
54    for (row, column) in [(16, 16), (16, 17), (17, 15), (17, 16), (18, 16)] {
55        grid[row][column] = true;
56    }
57    let mut executor = TensorExecutor::new(&compiled.program);
58    let mut source = Vec::new();
59    for generation in 0..120 {
60        println!(
61            "{}",
62            grid.iter().flat_map(|row| row.iter().map(|&alive| if alive { '#' } else { '.' })).collect::<String>()
63        );
64        source.clear();
65        (grid,).encode(&mut source);
66        let outputs = executor.run(&source);
67        let next: [[bool; N]; N] =
68            std::array::from_fn(|row| std::array::from_fn(|column| outputs[row * N + column] == 1.0));
69        assert_eq!(next, life_step(grid), "generation {generation}: the circuit disagrees with life_step()");
70        grid = next;
71    }
72}