Skip to main content

life/
life.rs

1use rand::{
2    distributions::{Distribution, Standard},
3    Rng,
4};
5
6use tapestry::{coord::Coord, grid::Grid, patterns, rect::Rect};
7
8fn main() {
9    let mut life_board = LifeBoard::random((16, 16));
10
11    for _i in 0..10 {
12        println!("{}", life_board.grid);
13        life_board.step();
14    }
15}
16
17struct LifeBoard {
18    grid: Grid<LifeState>,
19}
20
21impl LifeBoard {
22    fn random<C: Into<Coord>>(dimensions: C) -> Self {
23        Self {
24            grid: Grid::with_generator(Rect::new(dimensions), |(_x, _y)| {
25                rand::random::<LifeState>()
26            }),
27        }
28    }
29
30    fn step(&mut self) {
31        let neighbor_counts = self
32            .grid
33            .iter()
34            .map(|cell| self.live_neighbor_count(cell.0))
35            // `collect` to release the borrow on `self`.
36            .collect::<Vec<_>>();
37
38        for (cell, neighbor_count) in self.grid.cell_iter_mut().zip(neighbor_counts) {
39            *cell = LifeBoard::compute_state(*cell, neighbor_count)
40        }
41    }
42
43    fn live_neighbor_count(&self, coord: Coord) -> usize {
44        self.grid
45            .selection_iter(patterns::neighborhood(coord))
46            .filter(|r_cell| {
47                if let Ok(cell) = r_cell {
48                    *cell.1 == LifeState::Alive
49                } else {
50                    false
51                }
52            })
53            .count()
54    }
55
56    fn compute_state(state: LifeState, neighbor_count: usize) -> LifeState {
57        match state {
58            LifeState::Alive => {
59                if neighbor_count == 2 || neighbor_count == 3 {
60                    LifeState::Alive
61                } else {
62                    LifeState::Dead
63                }
64            }
65            LifeState::Dead => {
66                if neighbor_count == 3 {
67                    LifeState::Alive
68                } else {
69                    LifeState::Dead
70                }
71            }
72        }
73    }
74}
75
76#[derive(Copy, Clone, PartialEq, Eq, Debug)]
77enum LifeState {
78    Dead,
79    Alive,
80}
81
82impl Default for LifeState {
83    fn default() -> Self {
84        LifeState::Dead
85    }
86}
87
88impl From<LifeState> for char {
89    fn from(ls: LifeState) -> char {
90        match ls {
91            LifeState::Alive => 'O',
92            LifeState::Dead => '∙',
93        }
94    }
95}
96
97// Allows us to randomly generate LifeState values.
98impl Distribution<LifeState> for Standard {
99    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> LifeState {
100        match rng.gen_bool(0.3) {
101            true => LifeState::Alive,
102            false => LifeState::Dead,
103        }
104    }
105}