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