sequence.rs
4.5 kB · rust · 153 lines
1use mrlycore::state::{boolean, choice, randint, sample};2use mrlymath::life::{Counts, Sequence};34/// One drawn rule: the sequences behind it and the parity rolls.5#[derive(Clone, Debug)]6pub struct Rulebook {7 /// The sequence behind the birth counts.8 pub birth_sequence: Sequence,9 /// The sequence behind the survive counts.10 pub survive_sequence: Sequence,11 /// Whether one sequence serves both sides.12 pub reflect: bool,13 /// Whether zero stays in the counts.14 pub zeros: bool,15 /// Whether one stays in the counts.16 pub ones: bool,17}1819impl Rulebook {20 /// Returns the counts that create a cell.21 pub fn birth(&self) -> Counts {22 Counts::drawn(self.birth_sequence, self.zeros, self.ones)23 }24 /// Returns the counts that keep a cell.25 pub fn survive(&self) -> Counts {26 Counts::drawn(self.survive_sequence, self.zeros, self.ones)27 }28}2930fn options(simple: bool) -> Vec<Sequence> {31 if simple {32 return Sequence::numbers().to_vec();33 }34 let mut both = Sequence::numbers().to_vec();35 both.extend(Sequence::designs());36 both.retain(|s| !s.is_random());37 both38}3940fn sown(seq: Sequence) -> Sequence {41 if seq.is_random() {42 Sequence::Random(randint(0, i64::MAX) as u64)43 } else {44 seq45 }46}4748/// Draws a rulebook, number sequences only on the simple path, random draws given a seed.49pub fn rulebook(simple: bool) -> Rulebook {50 let pool = options(simple);51 let reflect = boolean();52 let (birth_sequence, survive_sequence) = if reflect {53 let one = sown(choice(&pool));54 (one, one)55 } else {56 let pair = sample(&pool, 2);57 (sown(pair[0]), sown(pair[1]))58 };59 Rulebook {60 birth_sequence,61 survive_sequence,62 reflect,63 zeros: boolean(),64 ones: boolean(),65 }66}6768#[cfg(test)]69mod tests {70 use super::*;71 use mrlycore::state::{guard, seed};72 #[test]73 fn the_simple_tier_holds_the_six_numbers() {74 let pool = options(true);75 assert_eq!(pool.len(), 6);76 assert!(pool.iter().any(|s| s.is_random()));77 }78 #[test]79 fn the_full_tier_drops_random() {80 let pool = options(false);81 assert_eq!(pool.len(), 22);82 assert!(!pool.iter().any(|s| s.is_random()));83 assert!(pool.contains(&Sequence::GridSquares));84 }85 #[test]86 fn reflection_serves_one_sequence_to_both_sides() {87 let _g = guard();88 let mut mirrored = false;89 let mut split = false;90 for s in 0..50 {91 seed(s);92 let rule = rulebook(true);93 if rule.reflect {94 assert_eq!(rule.birth_sequence, rule.survive_sequence);95 mirrored = true;96 } else {97 assert_ne!(rule.birth_sequence, rule.survive_sequence);98 split = true;99 }100 }101 assert!(mirrored && split);102 }103 #[test]104 fn counts_respect_the_budget_and_the_rolls() {105 let _g = guard();106 for s in 0..50 {107 seed(s);108 let rule = rulebook(false);109 let birth = rule.birth().values(8).unwrap();110 let survive = rule.survive().values(8).unwrap();111 for n in birth.iter().chain(&survive) {112 assert!(*n <= 8);113 if *n == 0 {114 assert!(rule.zeros);115 }116 if *n == 1 {117 assert!(rule.ones);118 }119 }120 }121 }122 #[test]123 fn a_random_draw_carries_its_own_seed() {124 let _g = guard();125 for s in 0..200 {126 seed(s);127 let rule = rulebook(true);128 if let Sequence::Random(inner) = rule.birth_sequence {129 assert_ne!(inner, 0, "the listed placeholder seed leaked");130 assert_eq!(131 rule.birth().values(8).unwrap(),132 Counts::drawn(Sequence::Random(inner), rule.zeros, rule.ones)133 .values(8)134 .unwrap()135 );136 return;137 }138 }139 panic!("no seed drew the random sequence");140 }141 #[test]142 fn the_draw_replays_from_a_seed() {143 let _g = guard();144 seed(42);145 let a = rulebook(true);146 seed(42);147 let b = rulebook(true);148 assert_eq!(a.birth_sequence, b.birth_sequence);149 assert_eq!(a.survive_sequence, b.survive_sequence);150 assert_eq!(a.birth().values(8).unwrap(), b.birth().values(8).unwrap());151 assert_eq!(a.reflect, b.reflect);152 }153}