use karpal_std::prelude::*;
fn rule_90(grid: &NonEmptyVec<u8>) -> u8 {
let tails = grid.tails();
let current = <NonEmptyVecF as Comonad>::extract(grid);
let len = grid.len();
let left = if len > 1 {
*tails.tail.last().map(|t| &t.head).unwrap_or(&grid.head)
} else {
current
};
let right = if grid.tail.is_empty() {
grid.head } else {
grid.tail[0]
};
left ^ right
}
fn rule_majority(grid: &NonEmptyVec<u8>) -> u8 {
let current = <NonEmptyVecF as Comonad>::extract(grid);
let len = grid.len();
let left = if len > 1 {
let tails = grid.tails();
*tails.tail.last().map(|t| &t.head).unwrap_or(&grid.head)
} else {
current
};
let right = if grid.tail.is_empty() {
grid.head
} else {
grid.tail[0]
};
let sum = left as u16 + current as u16 + right as u16;
if sum >= 2 { 1 } else { 0 }
}
fn step(grid: NonEmptyVec<u8>, rule: fn(&NonEmptyVec<u8>) -> u8) -> NonEmptyVec<u8> {
NonEmptyVecF::extend(grid, rule)
}
fn evolve(
initial: NonEmptyVec<u8>,
rule: fn(&NonEmptyVec<u8>) -> u8,
steps: usize,
) -> Vec<NonEmptyVec<u8>> {
let mut history = vec![initial.clone()];
let mut current = initial;
for _ in 0..steps {
current = step(current, rule);
history.push(current.clone());
}
history
}
fn display_grid(grid: &NonEmptyVec<u8>) -> String {
let mut s = String::new();
for cell in grid.iter() {
s.push(if *cell == 1 { '#' } else { '.' });
}
s
}
fn main() {
println!("=== Cellular Automaton Example ===\n");
println!("Using Extend (Comonad) to evolve a 1D cellular automaton.\n");
let width = 21;
let mid = width / 2;
let mut cells: Vec<u8> = vec![0; width];
cells[mid] = 1;
let initial = NonEmptyVec::new(cells[0], cells[1..].to_vec());
println!("--- Rule 90 (XOR of neighbors) ---");
let history = evolve(initial.clone(), rule_90, 10);
for (i, grid) in history.iter().enumerate() {
println!(" {:>2}: {}", i, display_grid(grid));
}
println!("\n--- Comonad::extract (read focused cell) ---");
println!(
" Head of initial grid: {}",
<NonEmptyVecF as Comonad>::extract(&initial)
);
println!("\n--- Majority rule ---");
let pattern = NonEmptyVec::new(1, vec![0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0]);
println!(" Initial: {}", display_grid(&pattern));
let history = evolve(pattern, rule_majority, 8);
for (i, grid) in history.iter().enumerate() {
println!(" {:>2}: {}", i, display_grid(grid));
}
println!("\n--- Extend::duplicate (all focused views) ---");
let small = NonEmptyVec::new(1, vec![2, 3]);
let duplicated: NonEmptyVec<NonEmptyVec<u8>> = NonEmptyVecF::duplicate(small);
println!(" Original: [1, 2, 3]");
println!(" Duplicated (each row is a focused view):");
for (i, view) in duplicated.iter().enumerate() {
let cells: Vec<String> = view.iter().map(|c| c.to_string()).collect();
println!(" focus {}: [{}]", i, cells.join(", "));
}
}