research-walks.rs

4.3 kB · rust · 155 lines

1use mrlycore::errors::Result;2use mrlycore::{Color, Rng};3use mrlyfig::{ink, plot, save, Board, Frame};4use mrlymath::two::designs;5use mrlymath::two::Cell2d;6use std::collections::VecDeque;78const LEVEL: usize = 4;9const STEPS: usize = 20000;10const SEED: u64 = 7;1112fn sites(cell: &Cell2d) -> (usize, Vec<bool>) {13    let side = cell.width();14    let types = cell.types();15    let mut mask = vec![false; side * side];16    for row in 0..side {17        for col in 0..side {18            mask[row * side + col] = types.get(&[row, col]) != 0;19        }20    }21    (side, mask)22}2324fn around(side: usize, at: usize) -> Vec<usize> {25    let (row, col) = (at / side, at % side);26    let mut out = Vec::with_capacity(4);27    if row > 0 {28        out.push(at - side);29    }30    if row + 1 < side {31        out.push(at + side);32    }33    if col > 0 {34        out.push(at - 1);35    }36    if col + 1 < side {37        out.push(at + 1);38    }39    out40}4142fn giant(side: usize, mask: &[bool]) -> Vec<usize> {43    let mut seen = vec![false; mask.len()];44    let mut best = Vec::new();45    for start in 0..mask.len() {46        if !mask[start] || seen[start] {47            continue;48        }49        let mut part = Vec::new();50        let mut queue = VecDeque::from([start]);51        seen[start] = true;52        while let Some(at) = queue.pop_front() {53            part.push(at);54            for next in around(side, at) {55                if mask[next] && !seen[next] {56                    seen[next] = true;57                    queue.push_back(next);58                }59            }60        }61        if part.len() > best.len() {62            best = part;63        }64    }65    best66}6768fn middle(side: usize, part: &[usize]) -> usize {69    let mid = (side as f64 - 1.0) / 2.0;70    *part71        .iter()72        .min_by(|a, b| {73            let reach = |at: &usize| {74                let (row, col) = ((at / side) as f64, (at % side) as f64);75                (row - mid).hypot(col - mid)76            };77            reach(a).partial_cmp(&reach(b)).unwrap()78        })79        .unwrap()80}8182fn walk(side: usize, mask: &[bool], start: usize, seed: u64) -> Vec<usize> {83    let mut rng = Rng::new(seed);84    let mut at = start;85    let mut path = vec![at];86    for _ in 0..STEPS {87        let (row, col) = (at / side, at % side);88        let next = match rng.below(4) {89            0 if row > 0 => at - side,90            1 if row + 1 < side => at + side,91            2 if col > 0 => at - 1,92            3 if col + 1 < side => at + 1,93            _ => at,94        };95        if next != at && mask[next] {96            at = next;97            path.push(at);98        }99    }100    path101}102103fn panel(board: &mut Board, area: Frame, code: u128, color: Color) -> Result<usize> {104    let cell = designs::create(code, 3, LEVEL, 0, 3)?;105    let (side, mask) = sites(&cell);106    let step = area.w / side as f64;107    let dot = step * 0.7;108    for (at, on) in mask.iter().enumerate() {109        if *on {110            let (row, col) = (at / side, at % side);111            board.rect(112                area.x + col as f64 * step + (step - dot) / 2.0,113                area.y + row as f64 * step + (step - dot) / 2.0,114                dot,115                dot,116                ink::fade(ink::dim(), 0.55),117            );118        }119    }120    let part = giant(side, &mask);121    let trail = walk(side, &mask, middle(side, &part), SEED);122    let mut seen = vec![false; mask.len()];123    for at in trail {124        if seen[at] {125            continue;126        }127        seen[at] = true;128        let (row, col) = (at / side, at % side);129        board.rect(130            area.x + col as f64 * step,131            area.y + row as f64 * step,132            step,133            step,134            color,135        );136    }137    Ok(mask.iter().filter(|f| **f).count())138}139140fn main() -> Result<()> {141    let mut board = Board::square();142    let frame = board.frame(0.08);143    let half = frame.w / 2.0;144    let first = Frame::new(frame.x, frame.y, half, half).inset(8.0);145    let second = Frame::new(frame.x + half, frame.y + half, half, half).inset(8.0);146    for area in [first, second] {147        plot::axis(&mut board, area, ink::line());148    }149    let a = panel(&mut board, first.inset(10.0), 127, ink::blue())?;150    let b = panel(&mut board, second.inset(10.0), 239, ink::orange())?;151    assert_eq!(a, 7usize.pow(LEVEL as u32));152    assert_eq!(b, a);153    save("research-walks", &board)?;154    Ok(())155}