#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rule {
pub number: u8,
}
impl Rule {
#[inline]
pub fn new(number: u8) -> Self {
Self { number }
}
#[inline]
pub fn apply(&self, left: bool, center: bool, right: bool) -> bool {
let pattern = (left as u8) << 2 | (center as u8) << 1 | right as u8;
(self.number >> pattern) & 1 == 1
}
pub fn lookup_table(&self) -> [bool; 8] {
let mut table = [false; 8];
for (i, slot) in table.iter_mut().enumerate() {
*slot = (self.number >> i) & 1 == 1;
}
table
}
pub fn is_left_shift(&self) -> bool {
self.number == 170
}
pub fn is_right_shift(&self) -> bool {
self.number == 240
}
pub fn wolfram_class(&self) -> &'static str {
match self.number {
0 | 8 | 32 | 40 | 128 | 136 | 160 | 168 => "I",
1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 => "II",
30 | 45 | 73 | 75 | 86 | 89 | 101 | 105 | 109 | 126 | 135 | 149 | 151
| 153 | 161 | 165 | 167 | 169 | 181 | 182 | 183 | 195 | 210 | 225 => "III",
110 | 54 | 62 | 147 => "IV",
_ => "II/III",
}
}
}
impl Default for Rule {
fn default() -> Self {
Self::new(110)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rule_0_all_dead() {
let rule = Rule::new(0);
for l in [false, true] {
for c in [false, true] {
for r in [false, true] {
assert!(!rule.apply(l, c, r), "Rule 0 should always produce dead");
}
}
}
}
#[test]
fn test_rule_255_all_alive() {
let rule = Rule::new(255);
for l in [false, true] {
for c in [false, true] {
for r in [false, true] {
assert!(rule.apply(l, c, r), "Rule 255 should always produce alive");
}
}
}
}
#[test]
fn test_rule_110_known_pattern() {
let rule = Rule::new(110);
assert!(!rule.apply(false, false, false));
assert!(rule.apply(false, false, true));
assert!(rule.apply(false, true, false));
assert!(rule.apply(false, true, true));
assert!(!rule.apply(true, false, false));
assert!(rule.apply(true, false, true));
assert!(rule.apply(true, true, false));
assert!(!rule.apply(true, true, true));
}
#[test]
fn test_rule_30_known_pattern() {
let rule = Rule::new(30);
assert!(!rule.apply(false, false, false));
assert!(rule.apply(false, false, true));
assert!(rule.apply(false, true, false));
assert!(rule.apply(false, true, true));
assert!(rule.apply(true, false, false));
assert!(!rule.apply(true, false, true));
assert!(!rule.apply(true, true, false));
assert!(!rule.apply(true, true, true));
}
#[test]
fn test_lookup_table_consistency() {
let rule = Rule::new(110);
let table = rule.lookup_table();
for l in 0..2 {
for c in 0..2 {
for r in 0..2 {
let pattern = l * 4 + c * 2 + r;
assert_eq!(
rule.apply(l == 1, c == 1, r == 1),
table[pattern],
"Mismatch for pattern {pattern}"
);
}
}
}
}
#[test]
fn test_left_shift_rule() {
let rule = Rule::new(170);
assert!(rule.is_left_shift());
assert!(!rule.is_right_shift());
assert!(!rule.apply(false, false, false));
assert!(rule.apply(false, false, true));
}
#[test]
fn test_right_shift_rule() {
let rule = Rule::new(240);
assert!(rule.is_right_shift());
assert!(!rule.is_left_shift());
assert!(!rule.apply(false, false, false));
assert!(!rule.apply(false, false, true));
assert!(rule.apply(true, false, false));
}
#[test]
fn test_wolfram_classifications() {
assert_eq!(Rule::new(0).wolfram_class(), "I");
assert_eq!(Rule::new(30).wolfram_class(), "III");
assert_eq!(Rule::new(110).wolfram_class(), "IV");
}
#[test]
fn test_default_rule() {
let rule = Rule::default();
assert_eq!(rule.number, 110);
}
}