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}