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 crate::system::{GriddedPosition, Position};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Grid2D {
|
pub struct Grid2D {
|
||||||
pub x: i32,
|
pub x: i32,
|
||||||
pub y: 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 rand::prelude::Rng;
|
||||||
use crate::system::{GriddedPosition, Position};
|
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> {
|
pub trait Walker<P: Position> {
|
||||||
fn walk<R: Rng>(&self, rng: &mut R, position: &P) -> P;
|
fn walk<R: Rng>(&self, rng: &mut R, position: &P) -> P;
|
||||||
@ -18,14 +22,131 @@ pub struct DiagonalRandomWalker;
|
|||||||
|
|
||||||
impl Walker<Grid3D> for DiagonalRandomWalker {
|
impl Walker<Grid3D> for DiagonalRandomWalker {
|
||||||
fn walk<R: Rng>(&self, rng: &mut R, position: &Grid3D) -> Grid3D {
|
fn walk<R: Rng>(&self, rng: &mut R, position: &Grid3D) -> Grid3D {
|
||||||
let a: Vec<i32> = (0..3)
|
static OFFSETS: [Grid3D; 26] = [
|
||||||
.map(|r| rng.gen_range(-1..=1))
|
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();
|
.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 {
|
mod test {
|
||||||
use rand::rngs::SmallRng;
|
use rand::rngs::SmallRng;
|
||||||
use rand::{SeedableRng, thread_rng};
|
use rand::{SeedableRng, thread_rng};
|
||||||
@ -33,42 +154,6 @@ mod test {
|
|||||||
use crate::system::spaces::square_grid::{Grid2D, Grid3D};
|
use crate::system::spaces::square_grid::{Grid2D, Grid3D};
|
||||||
use crate::system::walker::{DiagonalRandomWalker, LocalRandomWalker, Walker};
|
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]
|
#[test]
|
||||||
fn diagonal() {
|
fn diagonal() {
|
||||||
let drw = DiagonalRandomWalker;
|
let drw = DiagonalRandomWalker;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user