demo-race.rs

3.6 kB · rust · 127 lines

1use mrlycore::errors::Result;2use mrlycore::Rng;3use mrlyfig::{ink, save, Board, Color, Frame, Grid};4use mrlymath::two;56const NUMBER: usize = 3;7const BASE: usize = 3;8const LEVEL: usize = 4;9const SIDE: usize = 81;10const WALKERS: usize = 300;11const STEPS: usize = 1200;12const SEED: u64 = 7;1314struct Swarm {15    filled: Vec<bool>,16    home: usize,17    at: Vec<usize>,18}1920impl Swarm {21    fn spread(&self) -> f64 {22        let (hr, hc) = ((self.home / SIDE) as f64, (self.home % SIDE) as f64);23        let total: f64 = self24            .at25            .iter()26            .map(|&flat| {27                let (r, c) = ((flat / SIDE) as f64 - hr, (flat % SIDE) as f64 - hc);28                r * r + c * c29            })30            .sum();31        (total / self.at.len() as f64).sqrt()32    }33}3435fn race(code: u128, seed: u64) -> Result<Swarm> {36    let cell = two::create(code, NUMBER, LEVEL, 0, BASE)?;37    let types = cell.types();38    let filled: Vec<bool> = (0..SIDE * SIDE)39        .map(|flat| types.get(&[flat / SIDE, flat % SIDE]) != 0)40        .collect();41    let centre = ((SIDE - 1) / 2) as i64;42    let home = (0..SIDE * SIDE)43        .filter(|&flat| filled[flat])44        .min_by_key(|&flat| {45            let (r, c) = ((flat / SIDE) as i64 - centre, (flat % SIDE) as i64 - centre);46            r * r + c * c47        })48        .expect("a filled site");49    let mut rng = Rng::new(seed);50    let mut at = vec![home; WALKERS];51    for _ in 0..STEPS {52        for walker in at.iter_mut() {53            let (r, c) = (*walker / SIDE, *walker % SIDE);54            let (nr, nc) = match rng.below(4) {55                0 => (r + 1, c),56                1 => (r.wrapping_sub(1), c),57                2 => (r, c + 1),58                _ => (r, c.wrapping_sub(1)),59            };60            if nr < SIDE && nc < SIDE && filled[nr * SIDE + nc] {61                *walker = nr * SIDE + nc;62            }63        }64    }65    Ok(Swarm { filled, home, at })66}6768fn team(board: &mut Board, frame: Frame, swarm: &Swarm, tint: Color) {69    let grid = Grid::new(frame, SIDE, SIDE, 0.0);70    for flat in 0..SIDE * SIDE {71        if swarm.filled[flat] {72            grid.fill(73                board,74                flat % SIDE,75                flat / SIDE,76                ink::mix(ink::line(), ink::dim(), 0.35),77            );78        }79    }80    let unit = frame.w / SIDE as f64;81    let spot = |flat: usize| {82        (83            frame.x + (flat % SIDE) as f64 * unit + unit / 2.0,84            frame.y + (flat / SIDE) as f64 * unit + unit / 2.0,85        )86    };87    let (hx, hy) = spot(swarm.home);88    board.ring(hx, hy, swarm.spread() * unit, unit * 0.5, tint);89    for &walker in &swarm.at {90        let (x, y) = spot(walker);91        board.disc(x, y, unit * 0.62, tint);92    }93}9495fn main() -> Result<()> {96    let mut board = Board::square();97    let frame = board.frame(0.08);98    let fast = race(127, SEED)?;99    let slow = race(239, SEED + 777)?;100    for swarm in [&fast, &slow] {101        assert_eq!(swarm.filled.len(), 6561);102        assert_eq!(swarm.filled.iter().filter(|on| **on).count(), 2401);103        assert_eq!(swarm.at.len(), WALKERS);104        assert!(swarm.at.iter().all(|&flat| swarm.filled[flat]));105    }106    assert!(fast.spread() > slow.spread());107    let side = frame.w * 0.55;108    team(109        &mut board,110        Frame::new(frame.x, frame.y, side, side),111        &fast,112        ink::blue(),113    );114    team(115        &mut board,116        Frame::new(117            frame.x + frame.w - side,118            frame.y + frame.h - side,119            side,120            side,121        ),122        &slow,123        ink::orange(),124    );125    save("demo-race", &board)?;126    Ok(())127}