cellular_automaton/
cellular_automaton.rs1use karpal_std::prelude::*;
10
11fn rule_90(grid: &NonEmptyVec<u8>) -> u8 {
17 let tails = grid.tails();
18 let current = <NonEmptyVecF as Comonad>::extract(grid);
19 let len = grid.len();
20
21 let left = if len > 1 {
23 *tails.tail.last().map(|t| &t.head).unwrap_or(&grid.head)
25 } else {
26 current
27 };
28
29 let right = if grid.tail.is_empty() {
31 grid.head } else {
33 grid.tail[0]
34 };
35
36 left ^ right
38}
39
40fn rule_majority(grid: &NonEmptyVec<u8>) -> u8 {
43 let current = <NonEmptyVecF as Comonad>::extract(grid);
44 let len = grid.len();
45
46 let left = if len > 1 {
47 let tails = grid.tails();
48 *tails.tail.last().map(|t| &t.head).unwrap_or(&grid.head)
49 } else {
50 current
51 };
52
53 let right = if grid.tail.is_empty() {
54 grid.head
55 } else {
56 grid.tail[0]
57 };
58
59 let sum = left as u16 + current as u16 + right as u16;
60 if sum >= 2 { 1 } else { 0 }
61}
62
63fn step(grid: NonEmptyVec<u8>, rule: fn(&NonEmptyVec<u8>) -> u8) -> NonEmptyVec<u8> {
67 NonEmptyVecF::extend(grid, rule)
68}
69
70fn evolve(
72 initial: NonEmptyVec<u8>,
73 rule: fn(&NonEmptyVec<u8>) -> u8,
74 steps: usize,
75) -> Vec<NonEmptyVec<u8>> {
76 let mut history = vec![initial.clone()];
77 let mut current = initial;
78 for _ in 0..steps {
79 current = step(current, rule);
80 history.push(current.clone());
81 }
82 history
83}
84
85fn display_grid(grid: &NonEmptyVec<u8>) -> String {
88 let mut s = String::new();
89 for cell in grid.iter() {
90 s.push(if *cell == 1 { '#' } else { '.' });
91 }
92 s
93}
94
95fn main() {
96 println!("=== Cellular Automaton Example ===\n");
97 println!("Using Extend (Comonad) to evolve a 1D cellular automaton.\n");
98
99 let width = 21;
101 let mid = width / 2;
102 let mut cells: Vec<u8> = vec![0; width];
103 cells[mid] = 1;
104 let initial = NonEmptyVec::new(cells[0], cells[1..].to_vec());
105
106 println!("--- Rule 90 (XOR of neighbors) ---");
108 let history = evolve(initial.clone(), rule_90, 10);
109 for (i, grid) in history.iter().enumerate() {
110 println!(" {:>2}: {}", i, display_grid(grid));
111 }
112
113 println!("\n--- Comonad::extract (read focused cell) ---");
115 println!(
116 " Head of initial grid: {}",
117 <NonEmptyVecF as Comonad>::extract(&initial)
118 );
119
120 println!("\n--- Majority rule ---");
122 let pattern = NonEmptyVec::new(1, vec![0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0]);
124 println!(" Initial: {}", display_grid(&pattern));
125 let history = evolve(pattern, rule_majority, 8);
126 for (i, grid) in history.iter().enumerate() {
127 println!(" {:>2}: {}", i, display_grid(grid));
128 }
129
130 println!("\n--- Extend::duplicate (all focused views) ---");
132 let small = NonEmptyVec::new(1, vec![2, 3]);
133 let duplicated: NonEmptyVec<NonEmptyVec<u8>> = NonEmptyVecF::duplicate(small);
134 println!(" Original: [1, 2, 3]");
135 println!(" Duplicated (each row is a focused view):");
136 for (i, view) in duplicated.iter().enumerate() {
137 let cells: Vec<String> = view.iter().map(|c| c.to_string()).collect();
138 println!(" focus {}: [{}]", i, cells.join(", "));
139 }
140}