research-windows.rs

4.7 kB · rust · 167 lines

1use figures::{ink, save, Board, Color};2use mrlyrs::core::error::Result;3use mrlyrs::math::bang::factory;4use mrlyrs::math::bang::Code;5use std::collections::BTreeSet;67const LEVEL: usize = 3;8const SIDE: usize = 27;9const SCAN: usize = 5;10const COUNTS: [usize; 4] = [2, 10, 40, 74];11const RING: usize = 11;12const CELL: usize = 22;13const HAIR: usize = 2;14const GUTTER: usize = 15;1516type Window = Vec<u8>;1718fn render(level: usize) -> Result<(usize, Vec<u8>)> {19    let design = factory::create(Code::from(495u128), 3, 2, 3, level)?;20    let side = 3usize.pow(level as u32);21    assert_eq!(design.shape, vec![side, side]);22    assert_eq!(design.sum(), 8u64.pow(level as u32));23    let parity = factory::create(Code::from(7u128), 3, 2, 2, level)?;24    assert_eq!(design.bytes()?, parity.bytes()?);25    Ok((side, design.bytes()?.to_vec()))26}2728fn windows(side: usize, cells: &[u8], k: usize) -> BTreeSet<Window> {29    let mut out = BTreeSet::new();30    for r in 0..=side - k {31        for c in 0..=side - k {32            out.insert(33                (0..k * k)34                    .map(|i| cells[(r + i / k) * side + c + i % k])35                    .collect(),36            );37        }38    }39    out40}4142fn turn(w: &[u8]) -> Window {43    (0..9).map(|i| w[(2 - i % 3) * 3 + i / 3]).collect()44}4546fn orbit(w: &[u8]) -> Vec<Window> {47    let mut out = vec![w.to_vec()];48    loop {49        let next = turn(&out[out.len() - 1]);50        if next == out[0] {51            return out;52        }53        out.push(next);54    }55}5657fn ones(w: &[u8]) -> usize {58    w.iter().filter(|&&v| v == 1).count()59}6061fn low(w: &[u8]) -> usize {62    (0..9).filter(|&i| w[i] == 1).map(|i| i / 3).sum()63}6465fn ring(three: &BTreeSet<Window>) -> Vec<(usize, usize, Window)> {66    let mut seen = BTreeSet::new();67    let mut reps = Vec::new();68    let mut fixed = Vec::new();69    for w in three {70        if seen.contains(w) {71            continue;72        }73        let orb = orbit(w);74        seen.extend(orb.iter().cloned());75        if orb.len() == 4 {76            reps.push(orb.into_iter().max_by_key(|o| (low(o), o.clone())).unwrap());77        } else {78            fixed.extend(orb);79        }80    }81    reps.sort_by_key(|w| (ones(w), w.clone()));82    assert_eq!(reps.len(), RING - 2);83    assert_eq!(fixed.len(), 4);84    let pick = |n: usize, head: u8| -> Window {85        let found: Vec<&Window> = fixed86            .iter()87            .filter(|w| ones(w) == n && w[0] == head)88            .collect();89        assert_eq!(found.len(), 1);90        found[0].clone()91    };92    let last = RING - 1;93    let mut slots = vec![94        (0, 0, pick(7, 1)),95        (0, last, pick(7, 0)),96        (last, last, pick(8, 1)),97        (last, 0, pick(0, 0)),98    ];99    assert_eq!(turn(&slots[0].2), slots[1].2);100    for (i, rep) in reps.iter().enumerate() {101        let a = turn(rep);102        let b = turn(&a);103        let c = turn(&b);104        slots.push((0, i + 1, rep.clone()));105        slots.push((i + 1, last, a));106        slots.push((last, last - 1 - i, b));107        slots.push((last - 1 - i, 0, c));108    }109    let drawn: BTreeSet<Window> = slots.iter().map(|s| s.2.clone()).collect();110    assert_eq!(&drawn, three);111    assert_eq!(slots.len(), 4 * RING - 4);112    slots113}114115fn cells(116    board: &mut Board,117    x: usize,118    y: usize,119    side: usize,120    w: &[u8],121    on: Color,122    off: Option<Color>,123) {124    for (i, &v) in w.iter().enumerate() {125        let color = if v == 1 { Some(on) } else { off };126        if let Some(color) = color {127            let (cx, cy) = (x + (i % side) * CELL, y + (i / side) * CELL);128            let size = (CELL - HAIR) as f64;129            board.rect(cx as f64, cy as f64, size, size, color);130        }131    }132}133134fn main() -> Result<()> {135    let (side, scan) = render(SCAN)?;136    let (before, coarse) = render(SCAN - 1)?;137    for (k, count) in COUNTS.iter().enumerate() {138        let found = windows(side, &scan, k + 1);139        assert_eq!(found.len(), *count);140        assert_eq!(found, windows(before, &coarse, k + 1));141    }142    let three = windows(side, &scan, 3);143    let slots = ring(&three);144    let (ground, carpet) = render(LEVEL)?;145    assert_eq!(ground, SIDE);146147    let mut board = Board::square();148    let tile = 3 * CELL - HAIR;149    let span = RING * tile + (RING - 1) * GUTTER;150    let origin = (board.width - span) / 2;151    let inner = (board.width - (SIDE * CELL - HAIR)) / 2;152    cells(153        &mut board,154        inner,155        inner,156        SIDE,157        &carpet,158        ink::fade(ink::dim(), 0.75),159        None,160    );161    for (r, c, w) in &slots {162        let (x, y) = (origin + c * (tile + GUTTER), origin + r * (tile + GUTTER));163        cells(&mut board, x, y, 3, w, ink::blue(), Some(ink::line()));164    }165    save("research-windows", &board)?;166    Ok(())167}