race.rs
3.6 kB · rust · 118 lines
1use crate::{code_of, Fault};2use mrlycore::Rng;3use mrlymath::two;4use wasm_bindgen::prelude::*;56/// A race: seeded walkers loose on a flat design, each stepping blind and losing the turn when a hole blocks it.7#[wasm_bindgen]8pub struct Race {9 side: usize,10 types: Vec<u8>,11 home: usize,12 at: Vec<usize>,13 trail: Vec<u16>,14 rng: Rng,15 steps: u32,16}1718#[wasm_bindgen]19impl Race {20 /// Builds a race on the design the code names, every walker at the filled site nearest the centre.21 #[wasm_bindgen(constructor)]22 pub fn new(23 code: &str,24 number: usize,25 level: usize,26 base: usize,27 walkers: usize,28 seed: u32,29 ) -> Result<Race, Fault> {30 let cell = two::create(code_of(code)?, number, level, 0, base)?;31 let side = cell.width();32 let types = cell.types().bytes().to_vec();33 let centre = (side as i64 - 1) / 2;34 let home = (0..side * side)35 .filter(|&flat| types[flat] != 0)36 .min_by_key(|&flat| {37 let (r, c) = ((flat / side) as i64 - centre, (flat % side) as i64 - centre);38 r * r + c * c39 })40 .ok_or_else(|| Fault::new("the design is empty."))?;41 Ok(Race {42 side,43 types,44 home,45 at: vec![home; walkers],46 trail: vec![0; side * side],47 rng: Rng::new(seed as u64),48 steps: 0,49 })50 }51 /// Steps every walker the given number of ticks and returns the root mean square distance from home.52 pub fn step(&mut self, ticks: u32) -> f64 {53 let side = self.side;54 for _ in 0..ticks {55 for i in 0..self.at.len() {56 let (r, c) = (self.at[i] / side, self.at[i] % side);57 let (nr, nc) = match self.rng.below(4) {58 0 => (r + 1, c),59 1 => (r.wrapping_sub(1), c),60 2 => (r, c + 1),61 _ => (r, c.wrapping_sub(1)),62 };63 if nr < side && nc < side && self.types[nr * side + nc] != 0 {64 self.at[i] = nr * side + nc;65 self.trail[nr * side + nc] = self.trail[nr * side + nc].saturating_add(1);66 }67 }68 self.steps += 1;69 }70 self.distance()71 }72 /// Returns the root mean square distance of the walkers from home.73 pub fn distance(&self) -> f64 {74 if self.at.is_empty() {75 return 0.0;76 }77 let (hr, hc) = (78 (self.home / self.side) as f64,79 (self.home % self.side) as f64,80 );81 let total: f64 = self82 .at83 .iter()84 .map(|&flat| {85 let (r, c) = (86 (flat / self.side) as f64 - hr,87 (flat % self.side) as f64 - hc,88 );89 r * r + c * c90 })91 .sum();92 (total / self.at.len() as f64).sqrt()93 }94 /// Returns the side of the grid.95 pub fn side(&self) -> u32 {96 self.side as u3297 }98 /// Returns the flat index of home.99 pub fn home(&self) -> u32 {100 self.home as u32101 }102 /// Returns the count of ticks stepped so far.103 pub fn steps(&self) -> u32 {104 self.steps105 }106 /// Returns the grid types, one byte per site.107 pub fn types(&self) -> Vec<u8> {108 self.types.clone()109 }110 /// Returns the flat position of every walker.111 pub fn positions(&self) -> Vec<u32> {112 self.at.iter().map(|&flat| flat as u32).collect()113 }114 /// Returns the visit count of every site.115 pub fn trail(&self) -> Vec<u16> {116 self.trail.clone()117 }118}