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}