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::<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
97impl 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}