order.rs

8.9 kB · rust · 316 lines

1use crate::word::{observe, render, Obs, CODES};2use std::collections::HashMap;34pub struct Row {5    pub total: usize,6    pub fill: usize,7    pub diagonal: usize,8    pub boundary: usize,9    pub perimeter: usize,10    pub components: usize,11    pub euler: usize,12    pub holes: usize,13    pub profile: usize,14    pub peak: usize,15    pub support: usize,16}1718pub fn words(codes: &[u8], length: usize) -> Vec<Vec<u8>> {19    let mut out: Vec<Vec<u8>> = vec![Vec::new()];20    for _ in 0..length {21        let mut next: Vec<Vec<u8>> = Vec::new();22        for word in out.iter() {23            for &code in codes.iter() {24                let mut child = word.clone();25                child.push(code);26                next.push(child);27            }28        }29        out = next;30    }31    out32}3334fn peak(obs: &Obs) -> u64 {35    *obs.profile.iter().max().expect("a profile has a row")36}3738fn support(obs: &Obs) -> usize {39    obs.profile.iter().filter(|value| **value > 0).count()40}4142pub fn sensitivity(codes: &[u8], length: usize) -> Row {43    let mut groups: HashMap<Vec<u8>, Vec<Vec<u8>>> = HashMap::new();44    for word in words(codes, length) {45        let mut key = word.clone();46        key.sort_unstable();47        groups.entry(key).or_default().push(word);48    }49    let mut row = Row {50        total: 0,51        fill: 0,52        diagonal: 0,53        boundary: 0,54        perimeter: 0,55        components: 0,56        euler: 0,57        holes: 0,58        profile: 0,59        peak: 0,60        support: 0,61    };62    for (_, family) in groups.iter() {63        if family.len() < 2 {64            continue;65        }66        row.total += 1;67        let seen: Vec<Obs> = family.iter().map(|word| observe(word)).collect();68        let head = &seen[0];69        let split = |test: &dyn Fn(&Obs, &Obs) -> bool| seen.iter().any(|obs| test(head, obs));70        if split(&|a, b| a.fill != b.fill) {71            row.fill += 1;72        }73        if split(&|a, b| a.diagonal != b.diagonal) {74            row.diagonal += 1;75        }76        if split(&|a, b| a.boundary != b.boundary) {77            row.boundary += 1;78        }79        if split(&|a, b| a.perimeter != b.perimeter) {80            row.perimeter += 1;81        }82        if split(&|a, b| a.components != b.components) {83            row.components += 1;84        }85        if split(&|a, b| a.euler != b.euler) {86            row.euler += 1;87        }88        if split(&|a, b| a.holes != b.holes) {89            row.holes += 1;90        }91        if split(&|a, b| a.profile != b.profile) {92            row.profile += 1;93        }94        if split(&|a, b| peak(a) != peak(b)) {95            row.peak += 1;96        }97        if split(&|a, b| support(a) != support(b)) {98            row.support += 1;99        }100    }101    row102}103104fn index(word: &[u8]) -> usize {105    word.iter()106        .fold(0usize, |at, code| at * 15 + (*code as usize - 1))107}108109pub fn library_search() -> (usize, Vec<Vec<u8>>) {110    let mut table = vec![[0i64; 4]; 15 * 15 * 15];111    for word in words(&CODES, 3) {112        let obs = observe(&word);113        table[index(&word)] = [114            obs.boundary as i64,115            obs.components as i64,116            obs.euler,117            obs.holes as i64,118        ];119    }120    let target = [36usize, 188, 188, 100];121    let mut hits: Vec<Vec<u8>> = Vec::new();122    let mut scanned = 0usize;123    let mut pick: Vec<u8> = Vec::new();124    choose(&CODES, 10, 0, &mut pick, &mut |subset: &[u8]| {125        scanned += 1;126        let mut counts = [0usize; 4];127        let mut total = 0usize;128        for a in 0..10 {129            for b in a..10 {130                for c in b..10 {131                    let letters = [subset[a], subset[b], subset[c]];132                    let mut orders: Vec<[u8; 3]> = Vec::new();133                    for order in [134                        [0usize, 1, 2],135                        [0, 2, 1],136                        [1, 0, 2],137                        [1, 2, 0],138                        [2, 0, 1],139                        [2, 1, 0],140                    ] {141                        let word = [letters[order[0]], letters[order[1]], letters[order[2]]];142                        if !orders.contains(&word) {143                            orders.push(word);144                        }145                    }146                    if orders.len() < 2 {147                        continue;148                    }149                    total += 1;150                    for slot in 0..4 {151                        let head = table[index(&orders[0])][slot];152                        if orders.iter().any(|word| table[index(word)][slot] != head) {153                            counts[slot] += 1;154                        }155                    }156                }157            }158        }159        if total == 210 && counts == target {160            hits.push(subset.to_vec());161        }162    });163    (scanned, hits)164}165166fn choose(pool: &[u8], size: usize, at: usize, pick: &mut Vec<u8>, visit: &mut dyn FnMut(&[u8])) {167    if pick.len() == size {168        visit(pick);169        return;170    }171    if at >= pool.len() || pool.len() - at < size - pick.len() {172        return;173    }174    pick.push(pool[at]);175    choose(pool, size, at + 1, pick, visit);176    pick.pop();177    choose(pool, size, at + 1, pick, visit);178}179180pub fn diagonal_factors() -> usize {181    let mut bad = 0usize;182    for word in words(&CODES, 3) {183        let product: u64 = word184            .iter()185            .map(|code| render(&[*code]).diagonal())186            .product();187        if render(&word).diagonal() != product {188            bad += 1;189        }190    }191    bad192}193194pub fn contacts_multiply() -> usize {195    let mut bad = 0usize;196    for word in words(&CODES, 3) {197        let mut rows = 1u64;198        let mut cols = 1u64;199        for code in word.iter() {200            let (h, v) = render(&[*code]).contacts();201            rows *= h;202            cols *= v;203        }204        let (h, v) = render(&word).contacts();205        if h != rows || v != cols {206            bad += 1;207        }208    }209    bad210}211212pub fn boundary_pairs() -> (usize, usize) {213    let mut bad = 0usize;214    let mut pairs = 0usize;215    for a in 0..16u8 {216        for b in 0..16u8 {217            pairs += 1;218            let left = pair_grid(a, b);219            let right = pair_grid(b, a);220            if left != right {221                bad += 1;222            }223        }224    }225    (pairs, bad)226}227228fn pair_grid(a: u8, b: u8) -> (u64, u64) {229    let side = 4usize;230    let mut cells = vec![false; side * side];231    for (ra, ca) in crate::word::corners(a) {232        for (rb, cb) in crate::word::corners(b) {233            cells[(2 * ra + rb) * side + 2 * ca + cb] = true;234        }235    }236    let grid = crate::word::Grid { side, cells };237    (grid.boundary(), grid.interior())238}239240pub fn pair_component_table(pool: &[u8]) -> Vec<(u8, u8, u64, u64)> {241    let mut out = Vec::new();242    for (i, &a) in pool.iter().enumerate() {243        for &b in pool.iter().skip(i + 1) {244            let left = render(&[a, b]).components();245            let right = render(&[b, a]).components();246            out.push((a, b, left, right));247        }248    }249    out250}251252pub const PERIODS: [(&[u8], usize); 6] = [253    (&[3, 6], 2),254    (&[3, 6], 3),255    (&[7, 9], 3),256    (&[6, 9], 3),257    (&[3, 5, 6], 2),258    (&[7, 11, 13], 2),259];260261pub fn block_reduction() -> (usize, usize) {262    use mrlymath::bang::{magic, MagicLayer};263    use mrlymath::name::Bang;264    let mut cases = 0usize;265    let mut bad = 0usize;266    for (period, repeats) in PERIODS.iter() {267        let layers: Vec<MagicLayer> = period268            .iter()269            .map(|code| MagicLayer::new(Bang::new(*code as u128, 2, 2), 2))270            .collect();271        let composite = magic(&layers).expect("the factory accepts a plane period");272        let power = composite.fractal(*repeats);273        let mut flat: Vec<u8> = Vec::new();274        for _ in 0..*repeats {275            flat.extend_from_slice(period);276        }277        let grid = render(&flat);278        cases += 1;279        let same = power.shape == vec![grid.side, grid.side]280            && power281                .bytes()282                .iter()283                .zip(grid.cells.iter())284                .all(|(byte, cell)| (*byte == 1) == *cell);285        if !same {286            bad += 1;287        }288    }289    (cases, bad)290}291292pub fn crate_agreement(length: usize) -> (usize, usize) {293    use mrlymath::bang::{magic, MagicLayer};294    use mrlymath::name::Bang;295    let mut checked = 0usize;296    let mut bad = 0usize;297    for word in words(&CODES, length) {298        let layers: Vec<MagicLayer> = word299            .iter()300            .map(|code| MagicLayer::new(Bang::new(*code as u128, 2, 2), 2))301            .collect();302        let tensor = magic(&layers).expect("the factory accepts a plane word");303        let grid = render(&word);304        checked += 1;305        let same = tensor.shape == vec![grid.side, grid.side]306            && tensor307                .bytes()308                .iter()309                .zip(grid.cells.iter())310                .all(|(byte, cell)| (*byte == 1) == *cell);311        if !same {312            bad += 1;313        }314    }315    (checked, bad)316}