1use std::error::Error;
2use std::fmt;
3use std::str::FromStr;
4
5const TRUTH_TABLE_SIZE: usize = 9;
6
7#[derive(Clone, PartialEq, Eq, Debug)]
39pub struct Rule {
40 birth: [bool; TRUTH_TABLE_SIZE],
41 survival: [bool; TRUTH_TABLE_SIZE],
42}
43
44impl Rule {
47 pub const fn new(birth: &[bool; 9], survival: &[bool; 9]) -> Self {
66 Self {
67 birth: *birth,
68 survival: *survival,
69 }
70 }
71
72 #[inline]
90 pub const fn is_born(&self, count: usize) -> bool {
91 self.birth[count]
92 }
93
94 #[inline]
112 pub const fn is_survive(&self, count: usize) -> bool {
113 self.survival[count]
114 }
115
116 pub const fn conways_life() -> Self {
132 Self::new(
133 &[false, false, false, true, false, false, false, false, false],
134 &[false, false, true, true, false, false, false, false, false],
135 )
136 }
137}
138
139impl fmt::Display for Rule {
142 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
143 fn count_slice_numbers(slice: &[bool]) -> usize {
144 slice.iter().filter(|x| **x).count()
145 }
146 fn convert_slice_to_string(slice: &[bool]) -> String {
147 slice
148 .iter()
149 .enumerate()
150 .filter_map(|(i, &x)| if x { Some(i) } else { None })
151 .map(|n| char::from_digit(n as u32, TRUTH_TABLE_SIZE as u32).unwrap()) .collect()
153 }
154 let mut buf = String::with_capacity(count_slice_numbers(&self.birth) + count_slice_numbers(&self.survival));
155 buf += "B";
156 buf += &convert_slice_to_string(&self.birth);
157 buf += "/S";
158 buf += &convert_slice_to_string(&self.survival);
159 f.write_str(&buf)?;
160 Ok(())
161 }
162}
163
164#[derive(Debug)]
165pub struct ParseRuleError;
166
167impl Error for ParseRuleError {}
168
169impl fmt::Display for ParseRuleError {
170 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171 f.write_str("cannot parse rule from the string")
172 }
173}
174
175impl FromStr for Rule {
176 type Err = ParseRuleError;
177 fn from_str(s: &str) -> Result<Self, Self::Err> {
178 fn convert_numbers_to_slice(numbers: &str) -> Option<[bool; TRUTH_TABLE_SIZE]> {
179 numbers.chars().try_fold([false; TRUTH_TABLE_SIZE], |mut buf, c| {
180 let n = c.to_digit(TRUTH_TABLE_SIZE as u32)? as usize;
181 buf[n] = true;
182 Some(buf)
183 })
184 }
185 let fields_splitted: Vec<_> = s.split('/').collect();
186 if fields_splitted.len() != 2 {
187 return Err(ParseRuleError);
188 }
189 let (labels, numbers): (Vec<_>, Vec<_>) = fields_splitted
190 .iter()
191 .map(|s| s.split_at(s.find(|c: char| c.is_ascii_digit()).unwrap_or(s.len())))
192 .unzip();
193 let numbers = if labels.iter().zip(["B", "S"]).all(|(lhs, rhs)| lhs.eq_ignore_ascii_case(rhs)) {
194 numbers
196 } else if labels.iter().all(|s| s.is_empty()) {
197 vec![numbers[1], numbers[0]]
199 } else {
200 return Err(ParseRuleError);
201 };
202 let Some(slices) = numbers
203 .into_iter()
204 .map(convert_numbers_to_slice)
205 .collect::<Option<Vec<_>>>() else {
206 return Err(ParseRuleError);
207 };
208 Ok(Self {
209 birth: slices[0],
210 survival: slices[1],
211 })
212 }
213}
214
215#[cfg(test)]
218mod tests {
219 use super::*;
220 use anyhow::Result;
221 const RULE_HIGHLIFE: Rule = Rule::new(
222 &[false, false, false, true, false, false, true, false, false],
223 &[false, false, true, true, false, false, false, false, false],
224 );
225 fn check_value(target: &Rule, expected_birth: &[usize], expected_survival: &[usize]) {
226 for i in 0..=8 {
227 assert_eq!(target.is_born(i), expected_birth.iter().any(|&x| x == i));
228 assert_eq!(target.is_survive(i), expected_survival.iter().any(|&x| x == i));
229 }
230 }
231 #[test]
232 fn new_conways_life() {
233 let target = Rule::new(
234 &[false, false, false, true, false, false, false, false, false],
235 &[false, false, true, true, false, false, false, false, false],
236 );
237 check_value(&target, &[3], &[2, 3]);
238 }
239 #[test]
240 fn new_highlife() {
241 let target = Rule::new(
242 &[false, false, false, true, false, false, true, false, false],
243 &[false, false, true, true, false, false, false, false, false],
244 );
245 check_value(&target, &[3, 6], &[2, 3]);
246 }
247 #[test]
248 fn conways_life() {
249 let target = Rule::conways_life();
250 check_value(&target, &[3], &[2, 3]);
251 }
252 #[test]
253 fn display_conways_life() {
254 let target = Rule::conways_life();
255 assert_eq!(target.to_string(), "B3/S23");
256 }
257 #[test]
258 fn display_highlife() {
259 let target = RULE_HIGHLIFE;
260 assert_eq!(target.to_string(), "B36/S23");
261 }
262 #[test]
263 fn from_str_birth_survival_notation() -> Result<()> {
264 let target: Rule = "B3/S23".parse()?;
265 check_value(&target, &[3], &[2, 3]);
266 Ok(())
267 }
268 #[test]
269 fn from_str_s_b_notation() -> Result<()> {
270 let target: Rule = "23/3".parse()?;
271 check_value(&target, &[3], &[2, 3]);
272 Ok(())
273 }
274 #[test]
275 fn from_str_birth_survival_notation_without_birth_number() -> Result<()> {
276 let target: Rule = "B/S23".parse()?;
277 check_value(&target, &[], &[2, 3]);
278 Ok(())
279 }
280 #[test]
281 fn from_str_birth_survival_notation_without_survival_number() -> Result<()> {
282 let target: Rule = "B3/S".parse()?;
283 check_value(&target, &[3], &[]);
284 Ok(())
285 }
286 #[test]
287 fn from_str_birth_survival_notation_lowercase_b() -> Result<()> {
288 let target: Rule = "b3/S23".parse()?;
289 check_value(&target, &[3], &[2, 3]);
290 Ok(())
291 }
292 #[test]
293 fn from_str_birth_survival_notation_lowercase_s() -> Result<()> {
294 let target: Rule = "B3/s23".parse()?;
295 check_value(&target, &[3], &[2, 3]);
296 Ok(())
297 }
298 #[test]
299 fn from_str_no_separator() {
300 let target = "B0S0".parse::<Rule>();
301 assert!(target.is_err());
302 }
303 #[test]
304 fn from_str_too_many_separators() {
305 let target = "B0/S0/C0".parse::<Rule>();
306 assert!(target.is_err());
307 }
308 #[test]
309 fn from_str_no_label_birth() {
310 let target = "0/S0".parse::<Rule>();
311 assert!(target.is_err());
312 }
313 #[test]
314 fn from_str_no_label_survival() {
315 let target = "B0/0".parse::<Rule>();
316 assert!(target.is_err());
317 }
318 #[test]
319 fn from_str_birth_survival_notation_too_large_number() {
320 let target = "B9/S0".parse::<Rule>();
321 assert!(target.is_err());
322 }
323}