use crate::rule::Rule;
#[derive(Debug, Clone)]
pub struct ElementaryCA {
pub rule: Rule,
pub state: Vec<bool>,
}
impl ElementaryCA {
pub fn new(rule_number: u8, width: usize) -> Self {
assert!(width > 0, "Width must be positive");
Self {
rule: Rule::new(rule_number),
state: vec![false; width],
}
}
pub fn set_single_center(&mut self) {
let center = self.state.len() / 2;
self.state.iter_mut().for_each(|c| *c = false);
self.state[center] = true;
}
pub fn set_state(&mut self, state: &[bool]) {
assert_eq!(state.len(), self.state.len(), "State length must match width");
self.state.copy_from_slice(state);
}
pub fn step(&mut self) -> &[bool] {
let n = self.state.len();
let mut next = vec![false; n];
for (i, slot) in next.iter_mut().enumerate() {
let left = self.state[(i + n - 1) % n];
let center = self.state[i];
let right = self.state[(i + 1) % n];
*slot = self.rule.apply(left, center, right);
}
self.state = next;
&self.state
}
pub fn step_n(&mut self, n: usize) -> &[bool] {
for _ in 0..n {
self.step();
}
&self.state
}
pub fn to_string_visual(&self) -> String {
self.state
.iter()
.map(|&alive| if alive { '█' } else { '·' })
.collect()
}
pub fn to_string_binary(&self) -> String {
self.state
.iter()
.map(|&alive| if alive { '1' } else { '0' })
.collect()
}
pub fn alive_count(&self) -> usize {
self.state.iter().filter(|&&b| b).count()
}
pub fn width(&self) -> usize {
self.state.len()
}
pub fn is_all_dead(&self) -> bool {
!self.state.iter().any(|&b| b)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_ca_initial_state() {
let ca = ElementaryCA::new(30, 10);
assert_eq!(ca.state.len(), 10);
assert!(ca.state.iter().all(|&b| !b));
assert!(ca.is_all_dead());
}
#[test]
fn test_set_single_center_odd() {
let mut ca = ElementaryCA::new(30, 5);
ca.set_single_center();
assert_eq!(ca.state, vec![false, false, true, false, false]);
}
#[test]
fn test_set_single_center_even() {
let mut ca = ElementaryCA::new(30, 4);
ca.set_single_center();
assert_eq!(ca.state, vec![false, false, true, false]);
}
#[test]
#[should_panic(expected = "Width must be positive")]
fn test_new_ca_zero_width() {
ElementaryCA::new(30, 0);
}
#[test]
fn test_rule_0_kills_everything() {
let mut ca = ElementaryCA::new(0, 5);
ca.set_single_center();
ca.step();
assert!(ca.is_all_dead());
}
#[test]
fn test_rule_255_keeps_everything() {
let mut ca = ElementaryCA::new(255, 3);
ca.set_single_center();
ca.step();
assert_eq!(ca.alive_count(), 3);
}
#[test]
fn test_rule_30_single_center_evolution() {
let mut ca = ElementaryCA::new(30, 7);
ca.set_single_center();
ca.step();
assert_eq!(ca.alive_count(), 3);
}
#[test]
fn test_rule_110_not_trivially_dead() {
let mut ca = ElementaryCA::new(110, 7);
ca.set_single_center();
for _ in 0..5 {
ca.step();
}
assert!(!ca.is_all_dead());
}
#[test]
fn test_rule_184_traffic_model() {
let mut ca = ElementaryCA::new(184, 6);
ca.set_state(&[true, false, true, false, true, false]);
ca.step();
assert_eq!(ca.state, vec![false, true, false, true, false, true]);
}
#[test]
fn test_step_n() {
let mut ca1 = ElementaryCA::new(30, 11);
ca1.set_single_center();
ca1.step_n(3);
let mut ca2 = ElementaryCA::new(30, 11);
ca2.set_single_center();
ca2.step();
ca2.step();
ca2.step();
assert_eq!(ca1.state, ca2.state);
}
#[test]
fn test_periodic_boundary() {
let mut ca = ElementaryCA::new(170, 5);
ca.set_state(&[true, false, false, false, false]);
ca.step();
assert_eq!(ca.state, vec![false, false, false, false, true]);
}
#[test]
fn test_visual_string() {
let mut ca = ElementaryCA::new(30, 3);
ca.set_state(&[true, false, true]);
assert_eq!(ca.to_string_visual(), "█·█");
}
#[test]
fn test_binary_string() {
let mut ca = ElementaryCA::new(30, 3);
ca.set_state(&[true, false, true]);
assert_eq!(ca.to_string_binary(), "101");
}
#[test]
fn test_alive_count() {
let mut ca = ElementaryCA::new(30, 5);
ca.set_state(&[true, false, true, false, true]);
assert_eq!(ca.alive_count(), 3);
}
#[test]
fn test_width() {
let ca = ElementaryCA::new(30, 42);
assert_eq!(ca.width(), 42);
}
#[test]
fn test_set_state_length_mismatch() {
let mut ca = ElementaryCA::new(30, 5);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
ca.set_state(&[true; 3]);
}));
assert!(result.is_err());
}
#[test]
fn test_rule_90_sierpinski() {
let mut ca = ElementaryCA::new(90, 15);
ca.set_single_center();
for _ in 0..4 {
ca.step();
}
assert!(!ca.is_all_dead());
}
#[test]
fn test_clone_independence() {
let mut ca = ElementaryCA::new(30, 5);
ca.set_single_center();
let clone = ca.clone();
ca.step();
assert_eq!(clone.state, vec![false, false, true, false, false]);
}
#[test]
fn test_rule_254_grows() {
let mut ca = ElementaryCA::new(254, 5);
ca.set_single_center();
ca.step();
assert!(ca.alive_count() >= 3);
}
}