use super::*;
impl Board {
pub fn running_event(&self) -> Option<(TideEvent, u32)> {
if let Some((mania, ticks)) = self.tide.mania {
let event = match mania {
Mania::Crab => TideEvent::CrabMania,
Mania::Gull => TideEvent::GullMania,
};
return Some((event, ticks));
}
if let Some((tempo, ticks)) = self.tide.tempo {
let event = match tempo {
Tempo::Fast => TideEvent::SpeedUp,
Tempo::Slow => TideEvent::SlowDown,
};
return Some((event, ticks));
}
(self.tide.claw_call > 0).then_some((TideEvent::RightClaws, self.tide.claw_call))
}
pub fn signposts(&self) -> impl Iterator<Item = (u8, u8, Signpost)> + '_ {
self.signposts
.iter()
.enumerate()
.filter_map(|(tile, slot)| slot.map(|post| (tile as u16, post)))
.map(|(tile, post)| {
let (x, y) = self.coords_u8(tile);
(x, y, post)
})
}
pub fn lookahead(&self, seed: u64) -> Board {
let mut copy = self.clone();
copy.rng = Pcg32::new(seed, 0x0005_eaba_55ed);
copy.rules.gull_period = 0;
if self.golden_call().is_none() {
copy.golden_call = None;
}
for i in 0..copy.gulls.len() {
copy.gulls[i].takeoff_in = copy.roll_takeoff();
}
copy
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sim::classic_arena;
#[test]
fn a_lookahead_cannot_read_the_dice() {
let mut a = classic_arena(false, 2);
for _ in 0..200 {
a.tick_idle();
}
let mut b = a.clone();
b.rng = Pcg32::new(99, 7);
let (mut la, mut lb) = (a.lookahead(5), b.lookahead(5));
for _ in 0..600 {
la.tick_idle();
lb.tick_idle();
}
assert_eq!(la.state_hash(), lb.state_hash());
}
#[test]
fn a_lookahead_keeps_the_sand_and_drops_the_ambient_gulls() {
let mut board = classic_arena(false, 2);
for _ in 0..90 {
board.tick_idle();
}
let copy = board.lookahead(1);
assert_eq!(copy.crabs().len(), board.crabs().len());
assert_eq!(copy.scores(), board.scores());
assert_eq!(copy.rules().gull_period, 0);
assert!(board.rules().gull_period > 0);
}
#[test]
fn a_lookahead_learns_of_a_golden_crab_with_everybody_else() {
let mut board = Board::new(9, 7, 11);
board.set_tile(1, 1, TileKind::Castle(0));
board.set_tile(
0,
3,
TileKind::Spawner(Spawner {
dir: Direction::Right,
period: 9,
}),
);
board.rules_mut().golden_every = 600;
board.tick_idle();
let (at, _) = board.golden_call.expect("called at the stretch's start");
assert_eq!(board.golden_call(), None, "not public yet");
let goldens = |b: &Board| b.crabs().iter().any(|c| c.kind == CrabKind::Golden);
let mut far = board.lookahead(3);
assert_eq!(far.golden_call, None);
while far.ticks() <= at {
far.tick_idle();
assert!(!goldens(&far), "seen at {} of {at}", far.ticks());
}
while board.golden_call().is_none() {
board.tick_idle();
}
let mut near = board.lookahead(3);
assert_eq!(near.golden_call(), board.golden_call());
while near.ticks() <= at {
near.tick_idle();
}
assert!(goldens(&near), "out where it was called");
}
#[test]
fn the_running_event_is_named_with_its_clock() {
let mut board = classic_arena(false, 2);
assert_eq!(board.running_event(), None);
board.force_tide_event(TideEvent::SpeedUp, 0);
assert_eq!(
board.running_event(),
Some((TideEvent::SpeedUp, EVENT_TICKS))
);
board.tick_idle();
assert_eq!(
board.running_event(),
Some((TideEvent::SpeedUp, EVENT_TICKS - 1))
);
}
}