pub fn generate(
rule: u8,
steps: usize,
width: usize,
initial_state: Option<Vec<u32>>,
) -> Vec<Vec<u32>> {
if width == 0 {
return vec![];
}
let mut current = if let Some(state) = initial_state {
state.iter().take(width).copied().collect()
} else {
let mut state = vec![0; width];
state[width / 2] = 1; state
};
let mut history = vec![current.clone()];
for _ in 0..steps.saturating_sub(1) {
let mut next = vec![0; width];
for i in 0..width {
let left = if i > 0 { current[i - 1] } else { 0 };
let center = current[i];
let right = if i < width - 1 { current[i + 1] } else { 0 };
let neighborhood = (left << 2) | (center << 1) | right;
next[i] = if (rule >> neighborhood) & 1 == 1 {
1
} else {
0
};
}
current = next;
history.push(current.clone());
}
history
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cellular_automaton_rule30() {
let ca = generate(30, 5, 7, None);
assert_eq!(ca.len(), 5);
assert_eq!(ca[0].len(), 7);
assert_eq!(ca[0], vec![0, 0, 0, 1, 0, 0, 0]);
for gen in &ca {
for &cell in gen {
assert!(cell == 0 || cell == 1);
}
}
}
#[test]
fn test_cellular_automaton_edge_cases() {
let empty = generate(30, 5, 0, None);
assert_eq!(empty, Vec::<Vec<u32>>::new());
let single = generate(30, 1, 7, None);
assert_eq!(single.len(), 1);
assert_eq!(single[0].len(), 7);
}
#[test]
fn test_cellular_automaton_all_rules_binary() {
for rule in [0, 30, 90, 110, 184, 255].iter() {
let ca = generate(*rule, 5, 10, None);
for gen in &ca {
for &cell in gen {
assert!(
cell == 0 || cell == 1,
"Rule {} produced non-binary value: {}",
rule,
cell
);
}
}
}
}
#[test]
fn test_cellular_automaton_as_rhythm() {
let ca = generate(30, 8, 16, None);
let rhythm: Vec<usize> = ca[4]
.iter()
.enumerate()
.filter(|(_, &v)| v == 1)
.map(|(i, _)| i)
.collect();
assert!(!rhythm.is_empty());
assert!(rhythm.len() < 16);
}
#[test]
fn test_cellular_automaton_custom_initial() {
let initial = vec![1, 0, 1, 0, 1];
let ca = generate(30, 3, 5, Some(initial.clone()));
assert_eq!(ca.len(), 3);
assert_eq!(ca[0], initial); }
#[test]
fn test_cellular_automaton_rule110() {
let ca = generate(110, 10, 20, None);
assert_eq!(ca.len(), 10);
let total_ones: usize = ca
.iter()
.map(|gen| gen.iter().filter(|&&x| x == 1).count())
.sum();
let total_cells = ca.len() * ca[0].len();
assert!(
total_ones > 0 && total_ones < total_cells,
"Rule 110 should create mixed patterns"
);
}
#[test]
fn test_cellular_automaton_rule90() {
let ca = generate(90, 8, 15, None);
assert_eq!(ca.len(), 8);
assert_eq!(ca[0].len(), 15);
assert_eq!(ca[0][7], 1);
for gen in &ca {
let mid = gen.len() / 2;
for i in 1..mid {
if i < gen.len() - i - 1 {
assert_eq!(gen[mid - i], gen[mid + i], "Rule 90 should be symmetric");
}
}
}
}
}
pub fn rule30() -> Vec<Vec<u32>> {
generate(30, 16, 32, None)
}
pub fn rule110() -> Vec<Vec<u32>> {
generate(110, 20, 40, None)
}
pub fn rule90() -> Vec<Vec<u32>> {
generate(90, 16, 32, None)
}
pub fn rule184() -> Vec<Vec<u32>> {
generate(184, 16, 32, None)
}
pub fn classic() -> Vec<Vec<u32>> {
generate(30, 24, 48, None)
}