use pinch_points::sim::{
Board, CrabKind, Direction, Handedness, MAX_PLAYERS, PlayerAction, Spawner, TileKind,
};
fn build_board(seed: u64) -> Board {
let mut board = Board::new(12, 9, seed);
board.set_tile(
0,
0,
TileKind::Spawner(Spawner {
dir: Direction::Right,
period: 7,
}),
);
board.set_tile(
11,
8,
TileKind::Spawner(Spawner {
dir: Direction::Left,
period: 11,
}),
);
board.set_tile(11, 0, TileKind::Castle(0));
board.set_tile(0, 8, TileKind::Castle(1));
board.set_tile(5, 4, TileKind::Rock);
board.set_tile(6, 4, TileKind::Rock);
board.set_wall(3, 3, Direction::Right, true);
board.set_wall(3, 4, Direction::Right, true);
board.set_wall(8, 2, Direction::Down, true);
board.set_wall(2, 6, Direction::Up, true);
board.spawn_crab(4, 4, Direction::Up, Handedness::Left, CrabKind::Giant);
board.spawn_crab(7, 4, Direction::Up, Handedness::Right, CrabKind::Giant);
board.spawn_crab(6, 2, Direction::Left, Handedness::Left, CrabKind::Juvenile);
board.spawn_crab(6, 6, Direction::Right, Handedness::Right, CrabKind::Molting);
board.spawn_gull(3, 0, Direction::Down);
board.set_gull_period(450);
board.set_round_length(Some(9_000));
board
}
fn actions_for(tick: u64) -> [PlayerAction; MAX_PLAYERS] {
let mut actions = [PlayerAction::None; MAX_PLAYERS];
let dirs = [
Direction::Up,
Direction::Right,
Direction::Down,
Direction::Left,
];
if tick.is_multiple_of(37) {
let n = tick / 37;
actions[0] = PlayerAction::Place {
x: (n % 12) as u8,
y: ((n * 5) % 9) as u8,
dir: dirs[(n % 4) as usize],
};
}
if tick.is_multiple_of(53) {
let n = tick / 53;
actions[1] = PlayerAction::Place {
x: ((n * 3) % 12) as u8,
y: (n % 9) as u8,
dir: dirs[((n + 2) % 4) as usize],
};
}
if tick.is_multiple_of(111) {
let n = tick / 111;
let m = n * 2; actions[1] = PlayerAction::Remove {
x: ((m * 3) % 12) as u8,
y: (m % 9) as u8,
};
actions[2] = PlayerAction::Remove {
x: (n % 12) as u8,
y: ((n * 5) % 9) as u8,
};
}
actions
}
const TICKS: u64 = 10_000;
const EXPECTED_HASH: u64 = 0xee2a_a50b_9649_bf96;
#[test]
fn ten_thousand_ticks_reproduce_exactly() {
let mut a = build_board(0xDECA_FBAD);
let mut b = build_board(0xDECA_FBAD);
let mut ever_banked = false;
for t in 0..TICKS {
let actions = actions_for(t);
a.tick(&actions);
b.tick(&actions);
assert_eq!(a.state_hash(), b.state_hash(), "diverged at tick {t}");
ever_banked |= a.scores().iter().any(|&s| s > 0);
}
assert!(ever_banked, "no crab ever banked");
assert!(!a.crabs().is_empty(), "no crabs left on the board");
assert!(!a.gulls().is_empty(), "no gulls on the board");
assert!(a.round_over(), "the tide never came in");
let final_hash = a.state_hash();
assert_eq!(
final_hash, EXPECTED_HASH,
"state hash after {TICKS} ticks was {final_hash:#018x}, expected {EXPECTED_HASH:#018x}"
);
}
#[test]
fn different_seeds_diverge() {
let mut a = build_board(1);
let mut b = build_board(2);
for t in 0..200 {
let actions = actions_for(t);
a.tick(&actions);
b.tick(&actions);
}
assert_ne!(a.state_hash(), b.state_hash());
}