presets.rs

10.0 kB · rust · 334 lines

1use mrlyrs::life::{Boundary, Counts, Source};2use mrlyrs::math::bang::universe::orbit;3use mrlyrs::math::bang::Code;45/// The Moore neighbourhood's code at side 3 in the plane.6pub const MOORE: u128 = 7;7/// The Menger tile's code at side 3 in space.8pub const MENGER: u128 = 23;9/// The 26-cell neighbourhood's code at side 3 in space.10pub const MOORE_3D: u128 = 127;1112/// One seed design: a universe code drawn at a side and a Kronecker level.13#[derive(Clone, Copy, Debug, PartialEq, Eq)]14pub struct Seed {15    /// The universe code.16    pub code: u128,17    /// The odd side of the level-1 tile.18    pub side: usize,19    /// The Kronecker level.20    pub level: usize,21}2223/// One neighbourhood mask: a popped design, or the seed board copied and optionally inverted.24#[derive(Clone, Copy, Debug, PartialEq, Eq)]25pub enum Mask {26    /// The design a code names at a side and level, centre popped.27    Design {28        /// The universe code.29        code: u128,30        /// The odd side.31        side: usize,32        /// The Kronecker level.33        level: usize,34    },35    /// The tessellated seed board itself, centre popped.36    Copy {37        /// Whether the board is inverted before popping.38        inverted: bool,39    },40}4142impl Mask {43    /// Returns the mask's printable name.44    pub fn name(&self) -> String {45        match self {46            Mask::Design { code, side, level } if *code == MOORE && *side == 3 && *level == 1 => {47                "moore".to_string()48            }49            Mask::Design { code, side, level } => format!("d{code}s{side}l{level}"),50            Mask::Copy { inverted: false } => "copy".to_string(),51            Mask::Copy { inverted: true } => "copyinv".to_string(),52        }53    }54}5556/// One rule: a sequence drawn into both sides with its zero and one rolls, Life, or the whole outer-totalistic space.57#[derive(Clone, Copy, Debug, PartialEq, Eq)]58pub enum Rule {59    /// Birth and survive both drawn from one sequence.60    Drawn {61        /// The sequence behind both sides.62        seq: Source,63        /// Whether zero stays in the counts.64        zeros: bool,65        /// Whether one stays in the counts.66        ones: bool,67    },68    /// Conway's B3/S23.69    Life,70    /// Every outer-totalistic rule of the mask, declared for the largest preset only.71    Every,72}7374impl Rule {75    /// Returns the rule's printable name.76    pub fn name(&self) -> String {77        match self {78            Rule::Drawn { seq, zeros, ones } => {79                let mut name = seq.name();80                if *zeros {81                    name.push_str("+0");82                }83                if *ones {84                    name.push_str("+1");85                }86                name87            }88            Rule::Life => "b3s23".to_string(),89            Rule::Every => "every".to_string(),90        }91    }92    /// Returns the rule's family: number, design, life or every.93    pub fn family(&self) -> &'static str {94        match self {95            Rule::Drawn { seq, .. } if Source::numbers().contains(seq) => "number",96            Rule::Drawn { .. } => "design",97            Rule::Life => "life",98            Rule::Every => "every",99        }100    }101    /// Returns the counts that create a cell.102    pub fn birth(&self) -> Counts {103        match self {104            Rule::Drawn { seq, zeros, ones } => Counts::drawn(*seq, *zeros, *ones),105            Rule::Life => Counts::List(vec![3]),106            Rule::Every => Counts::List(Vec::new()),107        }108    }109    /// Returns the counts that keep a cell.110    pub fn survive(&self) -> Counts {111        match self {112            Rule::Drawn { seq, zeros, ones } => Counts::drawn(*seq, *zeros, *ones),113            Rule::Life => Counts::List(vec![2, 3]),114            Rule::Every => Counts::List(Vec::new()),115        }116    }117}118119/// One census preset: every axis the loop sweeps and the cut it stops at.120#[derive(Clone, Debug, PartialEq, Eq)]121pub struct Preset {122    /// The preset's name.123    pub name: &'static str,124    /// The dimension the seeds live in.125    pub dimension: usize,126    /// The seed designs.127    pub seeds: Vec<Seed>,128    /// The odd tessellation factors.129    pub tessellations: Vec<usize>,130    /// The masks.131    pub masks: Vec<Mask>,132    /// The rules.133    pub rules: Vec<Rule>,134    /// The boundaries.135    pub boundaries: Vec<Boundary>,136    /// The canvas side every board is padded to.137    pub canvas: usize,138    /// The generation cap.139    pub generations: usize,140}141142impl Preset {143    /// Returns the nominal run count before any dedupe.144    pub fn runs(&self) -> usize {145        let rules: usize = self146            .rules147            .iter()148            .map(|rule| if *rule == Rule::Every { 1 << 18 } else { 1 })149            .sum();150        self.seeds.len()151            * self.tessellations.len()152            * self.masks.len()153            * rules154            * self.boundaries.len()155    }156    /// Returns the preset as printable lines.157    pub fn describe(&self) -> Vec<String> {158        let seeds: Vec<String> = self159            .seeds160            .iter()161            .map(|s| format!("{}s{}l{}", s.code, s.side, s.level))162            .collect();163        let masks: Vec<String> = self.masks.iter().map(Mask::name).collect();164        let rules: Vec<String> = self.rules.iter().map(Rule::name).collect();165        let bounds: Vec<String> = self166            .boundaries167            .iter()168            .map(|b| format!("{b:?}").to_lowercase())169            .collect();170        vec![171            format!("preset {} dimension {}", self.name, self.dimension),172            format!("seeds {} : {}", seeds.len(), seeds.join(" ")),173            format!("tessellations {:?}", self.tessellations),174            format!("masks {} : {}", masks.len(), masks.join(" ")),175            format!("rules {} : {}", rules.len(), rules.join(" ")),176            format!("boundaries {}", bounds.join(" ")),177            format!("canvas {} generations {}", self.canvas, self.generations),178            format!("nominal runs {}", self.runs()),179        ]180    }181}182183/// Returns the smallest code of every non-empty orbit of a dimension's designs.184pub fn classes(dimension: usize) -> Vec<u128> {185    let total: u128 = 1 << (1 << dimension);186    (1..total)187        .filter(|&bits| {188            let code = Code::from(bits);189            orbit(code, dimension).iter().next() == Some(&code)190        })191        .collect()192}193194fn drawn(zeros: bool, ones: bool) -> Vec<Rule> {195    Source::all()196        .into_iter()197        .map(|seq| Rule::Drawn { seq, zeros, ones })198        .collect()199}200201/// The mini preset: side-3 classes at level 1, tessellation 1 and 3, side-3 masks and Copy, rolls off, Wrap, canvas 27, 64 generations.202pub fn mini() -> Preset {203    let seeds = classes(2)204        .into_iter()205        .map(|code| Seed {206            code,207            side: 3,208            level: 1,209        })210        .collect();211    let mut masks = vec![Mask::Design {212        code: MOORE,213        side: 3,214        level: 1,215    }];216    masks.extend(217        classes(2)218            .into_iter()219            .filter(|&code| code != MOORE)220            .map(|code| Mask::Design {221                code,222                side: 3,223                level: 1,224            }),225    );226    masks.push(Mask::Copy { inverted: false });227    masks.push(Mask::Copy { inverted: true });228    let mut rules = drawn(false, false);229    rules.push(Rule::Life);230    Preset {231        name: "mini",232        dimension: 2,233        seeds,234        tessellations: vec![1, 3],235        masks,236        rules,237        boundaries: vec![Boundary::Wrap],238        canvas: 27,239        generations: 64,240    }241}242243/// The medi preset: sides 3 and 5 at levels 1 and 2, tessellation to 5, masks to side 9 and Copy, all rolls, both boundaries, canvas 243, 256 generations.244pub fn medi() -> Preset {245    let mut seeds = Vec::new();246    for side in [3, 5] {247        for level in [1, 2] {248            seeds.extend(249                classes(2)250                    .into_iter()251                    .map(|code| Seed { code, side, level }),252            );253        }254    }255    let mut masks = Vec::new();256    for side in [3, 5, 7, 9] {257        masks.extend(classes(2).into_iter().map(|code| Mask::Design {258            code,259            side,260            level: 1,261        }));262    }263    masks.push(Mask::Copy { inverted: false });264    masks.push(Mask::Copy { inverted: true });265    let mut rules = Vec::new();266    for zeros in [false, true] {267        for ones in [false, true] {268            rules.extend(drawn(zeros, ones));269        }270    }271    rules.push(Rule::Life);272    Preset {273        name: "medi",274        dimension: 2,275        seeds,276        tessellations: vec![1, 3, 5],277        masks,278        rules,279        boundaries: vec![Boundary::Wrap, Boundary::Constant],280        canvas: 243,281        generations: 256,282    }283}284285/// The maxi preset: the side-3 classes in space, the Menger and Moore masks to level 3, tessellation to 11, every outer-totalistic rule, canvas 243, 1024 generations.286pub fn maxi() -> Preset {287    let seeds = classes(3)288        .into_iter()289        .map(|code| Seed {290            code,291            side: 3,292            level: 1,293        })294        .collect();295    let mut masks = Vec::new();296    for level in [1, 2, 3] {297        masks.push(Mask::Design {298            code: MENGER,299            side: 3,300            level,301        });302    }303    masks.push(Mask::Design {304        code: MOORE_3D,305        side: 3,306        level: 1,307    });308    Preset {309        name: "maxi",310        dimension: 3,311        seeds,312        tessellations: vec![1, 3, 5, 7, 9, 11],313        masks,314        rules: vec![Rule::Every],315        boundaries: vec![Boundary::Wrap, Boundary::Constant],316        canvas: 243,317        generations: 1024,318    }319}320321#[cfg(test)]322mod tests {323    use super::*;324    #[test]325    fn the_plane_has_five_filled_classes_and_the_mini_preset_a_thousand_runs() {326        assert_eq!(classes(2), vec![1, 3, 6, 7, 15]);327        assert_eq!(classes(3).len(), 21);328        let preset = mini();329        assert_eq!(preset.masks[0].name(), "moore");330        assert_eq!(preset.rules.len(), 24);331        assert_eq!(preset.runs(), 5 * 2 * 7 * 24);332        assert_eq!(maxi().rules[0].family(), "every");333    }334}