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}