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}