use std::time::Instant;
use spacewalk::{Adjacency, FullGrid, Grid, Hex, Sq};
#[test]
fn a_small_radius_costs_the_same_on_a_big_board_as_on_a_small_one() {
let big = FullGrid::square(200, 200, Adjacency::Eight); let small = FullGrid::square(20, 20, Adjacency::Eight); let (bi, si) = (big.at(Sq::new(100, 100)), small.at(Sq::new(10, 10)));
let time = |g: &FullGrid<Sq>, i| {
for _ in 0..1_000 {
let _ = g.within(i, 1, 2); }
let t = Instant::now();
for _ in 0..20_000 {
let _ = g.within(i, 1, 2);
}
t.elapsed().as_nanos().max(1) };
let (b1, s1) = (time(&big, bi), time(&small, si));
let (s2, b2) = (time(&small, si), time(&big, bi));
let ratio = (b1 + b2) as f64 / (s1 + s2) as f64;
assert_eq!(
big.within(bi, 1, 2).len(),
small.within(si, 1, 2).len(),
"same work, either way"
);
assert!(
ratio < 10.0,
"a radius-2 query cost {ratio:.1}× more on a board 100× bigger. It should cost about the \
same — that is what the offset table is for. Scanning the board would put this near 100."
);
}
#[test]
fn the_answer_is_the_same_whichever_way_it_is_computed() {
let fast = FullGrid::square(40, 40, Adjacency::Eight);
let i = fast.at(Sq::new(20, 20));
for (min, max) in [(0, 0), (0, 1), (1, 1), (2, 3), (0, 5), (3, 3)] {
let by_offsets: Vec<Sq> = fast.within(i, min, max).cells().collect();
let by_scan: Vec<Sq> = fast
.indices()
.filter(|&j| {
let d = fast.distance(i, j);
d >= min && d <= max
})
.map(|j| fast.coord(j))
.collect();
assert_eq!(by_offsets, by_scan, "range {min}..={max}");
}
}
#[test]
fn a_preposterous_radius_does_not_try_to_allocate_the_universe() {
let g = FullGrid::square(30, 30, Adjacency::Eight);
let centre = g.at(Sq::new(15, 15));
let t = Instant::now();
let all = g.within(centre, 0, u32::MAX);
let took = t.elapsed();
assert_eq!(
all.len(),
g.len(),
"every cell is within an infinite radius"
);
assert!(
took.as_millis() < 100,
"and it took {took:?}, not the rest of your life"
);
}
#[test]
fn the_crossover_is_invisible_from_outside() {
let g = FullGrid::square(20, 20, Adjacency::Four);
let i = g.at(Sq::new(10, 10));
let small = g.within(i, 0, 3); let large = g.within(i, 0, 500);
assert!(small.len() < large.len());
assert_eq!(large.len(), g.len());
assert_eq!(g.within(i, 0, 40).len(), g.len());
}
#[test]
fn an_inverted_range_is_empty_rather_than_expensive() {
let g = FullGrid::square(10, 10, Adjacency::Four);
let i = g.at(Sq::new(5, 5));
assert!(g.within(i, 5, 2).is_empty());
assert!(g.within(i, 1, 0).is_empty());
}
#[test]
fn hex_rings_still_jump_over_holes() {
let g = FullGrid::hexagon(4).filtered(|c| c != Hex::new(1, 0));
let from = g.at(Hex::new(2, 0));
let across = g.at(Hex::new(0, 0));
assert_eq!(g.distance(from, across), 2);
assert!(
g.ring(from, 2).contains(Hex::new(0, 0)),
"two away across the hole, so it is a legal jump"
);
}
#[test]
fn the_shapes_are_still_right() {
let four = FullGrid::square(11, 11, Adjacency::Four);
let i = four.at(Sq::new(5, 5));
assert_eq!(four.within(i, 1, 1).len(), 4, "a plus sign");
assert_eq!(four.within(i, 1, 2).len(), 12, "a diamond");
let eight = FullGrid::square(11, 11, Adjacency::Eight);
let j = eight.at(Sq::new(5, 5));
assert_eq!(eight.within(j, 1, 1).len(), 8, "a ring");
assert_eq!(
eight.within(j, 1, 2).len(),
24,
"a 5x5 square, less the centre"
);
let hex = FullGrid::hexagon(3);
let c = hex.at(Hex::new(0, 0));
assert_eq!(hex.within(c, 1, 1).len(), 6);
assert_eq!(hex.within(c, 1, 2).len(), 18, "6 + 12");
}