ca_rules/
lib.rs

1//! Parsing rule strings of life-like and other cellular automata.
2//!
3//! Currently the following rules are supported:
4//!
5//! * [Totalistic Life-like](http://www.conwaylife.com/wiki/Totalistic_Life-like_cellular_automaton),
6//!   e.g., `B3/S23`.
7//! * [Isotropic non-totalistic Life-like](http://www.conwaylife.com/wiki/Isotropic_non-totalistic_Life-like_cellular_automaton),
8//!   e.g., `B2ci3ai4c8/S02ae3eijkq4iz5ar6i7e`.
9//! * [Non-isotropic Life-like](http://www.conwaylife.com/wiki/Non-isotropic_Life-like_cellular_automaton),
10//!   e.g., `MAPARYXfhZofugWaH7oaIDogBZofuhogOiAaIDogIAAgAAWaH7oaIDogGiA6ICAAIAAaIDogIAAgACAAIAAAAAAAA`.
11//! * [Totalistic Hexagonal](http://www.conwaylife.com/wiki/Hexagonal_neighbourhood),
12//!   e.g., `B2/S34H`.
13//! * [Isotropic non-totalistic Hexagonal](http://www.conwaylife.com/wiki/Hexagonal_neighbourhood),
14//!   e.g., `B2o3-o4m/S12m3o4m5H`.
15//! * [Non-isotropic Hexagonal](http://www.conwaylife.com/wiki/Non-isotropic_Life-like_cellular_automaton),
16//!   e.g., `MAPFgFoF2gXgH5oF4B+gH4A6AH`.
17//! * [von Neumann neighborhood](http://www.conwaylife.com/wiki/Von_Neumann_neighbourhood),
18//!   e.g., `B2/S013V`.
19//! * [Non-isotropic von Neumann](http://www.conwaylife.com/wiki/Non-isotropic_Life-like_cellular_automaton),
20//!   e.g., `MAPHmlphg`.
21//! * The corresponding [Generations rules](http://www.conwaylife.com/wiki/Generations)
22//! of the above rules, e.g., `3457/357/5`.
23//!
24//! For non-Generations rules, four different notations are supported:
25//! * [B/S notation](http://www.conwaylife.com/wiki/Rulestring#B.2FS_notation) (`B3/S23`)
26//! * [S/B notation](http://www.conwaylife.com/wiki/Rulestring#S.2FB_notation) (`23/3`)
27//! * [MAP strings](http://www.conwaylife.com/wiki/Non-isotropic_Life-like_cellular_automaton)
28//!   for [non-isotropic rules](http://www.conwaylife.com/wiki/Non-isotropic_Life-like_cellular_automaton)
29//!   (`MAPARYXfhZofugWaH7oaIDogBZofuhogOiAaIDogIAAgAAWaH7oaIDogGiA6ICAAIAAaIDogIAAgACAAIAAAAAAAA`)
30//!
31//! For Generations rules, four different notations are supported:
32//!
33//! * B/S notation (`B357/S3457/C5`)
34//! * The notation used by [Golly](http://golly.sourceforge.net/Help/Algorithms/Generations.html) (`3457/357/5`)
35//! * The notation used by [Catagolue](https://catagolue.appspot.com/rules/generations) (`g5b357s3457`)
36//! * [MAP strings](http://www.conwaylife.com/wiki/Non-isotropic_Life-like_cellular_automaton)
37//!   for [non-isotropic rules](http://www.conwaylife.com/wiki/Non-isotropic_Life-like_cellular_automaton)
38//!   (`MAPARYBFxZpF38WaRd/aZZ//hZpF39pln/+aZZ//pZp/ukWaRd/aZZ//mmWf/6Waf7paZZ//pZp/umWaf7paZbplg/5`)
39//!
40//! Please refer to [Life Wiki](http://www.conwaylife.com/wiki/Rulestring) for detailed definitions and
41//! notations of these rule strings.
42//!
43//! # Example:
44//! ```
45//! use ca_rules::ParseLife;
46//!
47//! // Define a struct for your rule:
48//! #[derive(Debug, Eq, PartialEq)]
49//! struct Rule {
50//!     b: Vec<u8>,
51//!     s: Vec<u8>,
52//! }
53//!
54//! // Implement a parser trait for your rule:
55//! // The choice of the trait depends on the type of rules you want to parse.
56//! impl ParseLife for Rule {
57//!     // Implement a function to construct the rule from b and s data:
58//!     fn from_bs(b: Vec<u8>, s: Vec<u8>) -> Self {
59//!         Rule { b, s }
60//!     }
61//! }
62//!
63//! // Then you can parse a rule string:
64//! let life = Rule::parse_rule("B3/S23").unwrap();
65//! assert_eq!(
66//!     life,
67//!     Rule {
68//!         b: vec![3],
69//!         s: vec![2, 3],
70//!     }
71//! )
72//! ```
73
74mod error;
75mod macros;
76mod rules;
77
78pub use error::ParseRuleError;
79pub use rules::*;
80
81#[cfg(test)]
82mod test {
83    use super::{ParseNtLife, ParseRuleError};
84    use base64::{
85        alphabet::STANDARD,
86        engine::{
87            general_purpose::{GeneralPurpose, GeneralPurposeConfig},
88            DecodePaddingMode, Engine,
89        },
90    };
91
92    #[derive(Debug, Eq, PartialEq)]
93    struct Rule {
94        b: Vec<u8>,
95        s: Vec<u8>,
96    }
97
98    impl ParseNtLife for Rule {
99        fn from_bs(b: Vec<u8>, s: Vec<u8>) -> Self {
100            Rule { b, s }
101        }
102    }
103
104    const ENGINE_CONFIG: GeneralPurposeConfig =
105        GeneralPurposeConfig::new().with_decode_padding_mode(DecodePaddingMode::Indifferent);
106    const ENGINE: GeneralPurpose = GeneralPurpose::new(&STANDARD, ENGINE_CONFIG);
107
108    #[test]
109    fn base64() -> Result<(), ParseRuleError> {
110        let s = "MAPARYXfhZofugWaH7oaIDogBZofuhogOiAaIDogIAAgAAWaH7oaIDogGiA6ICAAIAAaIDogIAAgACAAIAAAAAAAA";
111        let bytes = ENGINE
112            .decode(&s[3..])
113            .map_err(|_| ParseRuleError::Base64Error)?;
114        assert_eq!(bytes.len(), 0x200 / 8);
115        let mut b = Vec::new();
116        let mut s = Vec::new();
117        for (i, x) in bytes.iter().map(|x| x.reverse_bits()).enumerate() {
118            for j in 0..8 {
119                if x & (1 << j) != 0 {
120                    let k = i * 8 + j;
121                    let n = ((k & 0x1e0) >> 1 | (k & 0x0f)) as u8;
122                    if k & 0x10 == 0 {
123                        b.push(n);
124                    } else {
125                        s.push(n);
126                    }
127                }
128            }
129        }
130        assert_eq!(Rule { b, s }, Rule::parse_rule("B3/S23")?);
131        Ok(())
132    }
133}