Add some tests for walking
This commit is contained in:
parent
c8fcce839b
commit
81562d6b28
@ -3,7 +3,7 @@ use num_integer::Integer;
|
||||
use crate::system::{GriddedPosition, Position};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Grid2D {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
use std::hash::Hash;
|
||||
use bevy::render::render_resource::encase::private::RuntimeSizedArray;
|
||||
use itertools::Itertools;
|
||||
use rand::distributions::Slice;
|
||||
use rand::prelude::Rng;
|
||||
use crate::system::{GriddedPosition, Position};
|
||||
use crate::system::spaces::square_grid::Grid3D;
|
||||
use crate::system::spaces::square_grid::{Grid2D, Grid3D};
|
||||
|
||||
pub trait Walker<P: Position> {
|
||||
fn walk<R: Rng>(&self, rng: &mut R, position: &P) -> P;
|
||||
@ -18,14 +22,131 @@ pub struct DiagonalRandomWalker;
|
||||
|
||||
impl Walker<Grid3D> for DiagonalRandomWalker {
|
||||
fn walk<R: Rng>(&self, rng: &mut R, position: &Grid3D) -> Grid3D {
|
||||
let a: Vec<i32> = (0..3)
|
||||
.map(|r| rng.gen_range(-1..=1))
|
||||
static OFFSETS: [Grid3D; 26] = [
|
||||
Grid3D { x: 1, y: 0, z: 0 },
|
||||
Grid3D { x: 1, y: 1, z: 0 },
|
||||
Grid3D { x: 1, y: -1, z: 0 },
|
||||
Grid3D { x: -1, y: 0, z: 0 },
|
||||
Grid3D { x: -1, y: 1, z: 0 },
|
||||
Grid3D { x: -1, y: -1, z: 0 },
|
||||
Grid3D { x: 0, y: 1, z: 0 },
|
||||
Grid3D { x: 0, y: -1, z: 0 },
|
||||
Grid3D { x: 1, y: 0, z: -1 },
|
||||
Grid3D { x: 1, y: 1, z: -1 },
|
||||
Grid3D { x: 1, y: -1, z: -1 },
|
||||
Grid3D { x: -1, y: 0, z: -1 },
|
||||
Grid3D { x: -1, y: 1, z: -1 },
|
||||
Grid3D { x: -1, y: -1, z: -1 },
|
||||
Grid3D { x: 0, y: 1, z: -1 },
|
||||
Grid3D { x: 0, y: -1, z: -1 },
|
||||
Grid3D { x: 0, y: 0, z: -1 },
|
||||
Grid3D { x: 1, y: 0, z: 1 },
|
||||
Grid3D { x: 1, y: 1, z: 1 },
|
||||
Grid3D { x: 1, y: -1, z: 1 },
|
||||
Grid3D { x: -1, y: 0, z: 1 },
|
||||
Grid3D { x: -1, y: 1, z: 1 },
|
||||
Grid3D { x: -1, y: -1, z: 1 },
|
||||
Grid3D { x: 0, y: 1, z: 1 },
|
||||
Grid3D { x: 0, y: -1, z: 1 },
|
||||
Grid3D { x: 0, y: 0, z: 1 },
|
||||
];
|
||||
|
||||
position.clone() + OFFSETS[rng.gen_range(0..OFFSETS.len())].clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Walker<Grid2D> for DiagonalRandomWalker {
|
||||
fn walk<R: Rng>(&self, rng: &mut R, position: &Grid2D) -> Grid2D {
|
||||
static OFFSETS: [Grid2D; 8] = [
|
||||
Grid2D { x: 1, y: 0 },
|
||||
Grid2D { x: 1, y: 1 },
|
||||
Grid2D { x: 1, y: -1 },
|
||||
Grid2D { x: -1, y: 0 },
|
||||
Grid2D { x: -1, y: 1 },
|
||||
Grid2D { x: -1, y: -1 },
|
||||
Grid2D { x: 0, y: 1 },
|
||||
Grid2D { x: 0, y: -1 },
|
||||
];
|
||||
|
||||
position.clone() + OFFSETS[rng.gen_range(0..OFFSETS.len())].clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn test_uniformity_and_range<W: Walker<P>, P>(walker: W, expected: &[P], n: usize, tolerance: f32) where P: GriddedPosition + Hash + Eq {
|
||||
use rand::thread_rng;
|
||||
let mut rng = thread_rng();
|
||||
let origin = &P::zero();
|
||||
|
||||
let results: Vec<P> = (0..n)
|
||||
.map(|_| walker.walk(&mut rng, origin))
|
||||
.collect();
|
||||
|
||||
position.clone() + Grid3D::from_cartesian([a[0] as f32, a[1] as f32, a[2] as f32])
|
||||
let groups = results.iter()
|
||||
.into_group_map_by(|a| (*a).clone());
|
||||
|
||||
assert_eq!(groups.len(), expected.len(), "Wrong number of walk positions generated");
|
||||
assert!(results.iter().unique().all(|a| expected.contains(a)), "Contains unexpected walk position");
|
||||
|
||||
for group in groups.values() {
|
||||
let proportion = group.len() as f32 / n as f32;
|
||||
assert!((proportion - (1.0 / expected.len() as f32)).abs() < tolerance, "Failed tolerance check");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uniformity_direct_grid2d() {
|
||||
test_uniformity_and_range(LocalRandomWalker, &[
|
||||
Grid2D { x: 1, y: 0 },
|
||||
Grid2D { x: -1, y: 0 },
|
||||
Grid2D { x: 0, y: 1 },
|
||||
Grid2D { x: 0, y: -1 },
|
||||
], 1_000_000, 0.001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uniformity_direct_grid3d() {
|
||||
test_uniformity_and_range(LocalRandomWalker, &[
|
||||
Grid3D { x: 1, y: 0, z: 0 },
|
||||
Grid3D { x: -1, y: 0, z: 0 },
|
||||
Grid3D { x: 0, y: 1, z: 0 },
|
||||
Grid3D { x: 0, y: -1, z: 0 },
|
||||
Grid3D { x: 0, y: 0, z: 1 },
|
||||
Grid3D { x: 0, y: 0, z: -1 },
|
||||
], 1_000_000, 0.001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diagonal_grid2d() {
|
||||
let mut expected = Vec::new();
|
||||
|
||||
for x in -1..=1 {
|
||||
for y in -1..=1 {
|
||||
if !(x == 0 && y == 0) {
|
||||
expected.push(Grid2D { x, y })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test_uniformity_and_range(DiagonalRandomWalker, &expected, 1_000_000, 0.001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diagonal_grid3d() {
|
||||
let mut expected = Vec::new();
|
||||
|
||||
for x in -1..=1 {
|
||||
for y in -1..=1 {
|
||||
for z in -1..=1 {
|
||||
if !(x == 0 && y == 0 && z == 0) {
|
||||
expected.push(Grid3D { x, y, z })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test_uniformity_and_range(DiagonalRandomWalker, &expected, 1_000_000, 0.001);
|
||||
}
|
||||
|
||||
mod test {
|
||||
use rand::rngs::SmallRng;
|
||||
use rand::{SeedableRng, thread_rng};
|
||||
@ -33,42 +154,6 @@ mod test {
|
||||
use crate::system::spaces::square_grid::{Grid2D, Grid3D};
|
||||
use crate::system::walker::{DiagonalRandomWalker, LocalRandomWalker, Walker};
|
||||
|
||||
#[test]
|
||||
fn uniformity() {
|
||||
let walker = LocalRandomWalker;
|
||||
let mut rng = SmallRng::from_rng(thread_rng()).unwrap();
|
||||
let mut results: Vec<Grid2D> = vec![];
|
||||
|
||||
let origin = &Grid2D::zero();
|
||||
|
||||
let x: u32 = (1_000_000);
|
||||
for i in 0..x {
|
||||
results.push(walker.walk(&mut rng, origin));
|
||||
}
|
||||
|
||||
let a = results
|
||||
.iter()
|
||||
.filter(|a| **a == Grid2D { x: 0, y: 1 })
|
||||
.count();
|
||||
|
||||
let b = results
|
||||
.iter()
|
||||
.filter(|a| **a == Grid2D { x: 0, y: -1 })
|
||||
.count();
|
||||
|
||||
let c = results
|
||||
.iter()
|
||||
.filter(|a| **a == Grid2D { x: 1, y: 0 })
|
||||
.count();
|
||||
|
||||
let d = results
|
||||
.iter()
|
||||
.filter(|a| **a == Grid2D { x: -1, y: 0 })
|
||||
.count();
|
||||
|
||||
println!("{} {} {} {}", a as f32 / x as f32, b as f32 / x as f32, c as f32 / x as f32, d as f32 / x as f32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diagonal() {
|
||||
let drw = DiagonalRandomWalker;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user