use std::hash::{Hash, Hasher};
use rustc_hash::FxHasher;
use crate::{
FxHashSet, Map, Tiles, Vector2,
path_finding::{compute_area_anchor, compute_reachable_area},
solver::{Strategy, context::Context},
};
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct State {
pub player_position: Vector2<i32>,
pub box_positions: FxHashSet<Vector2<i32>>,
}
impl State {
pub fn lower_bound(&self, ctx: &Context) -> i32 {
self.box_positions
.iter()
.map(|box_position| ctx.min_costs()[box_position])
.sum()
}
pub fn canonicalized_hash(&self, ctx: &Context) -> u64 {
let mut hasher = FxHasher::default();
match ctx.strategy() {
Strategy::Quick | Strategy::PushOptimal => {
let mut canonicalized_state = self.clone();
canonicalized_state.canonicalize_player(ctx.map());
canonicalized_state.hash(&mut hasher);
hasher.finish()
}
Strategy::MoveOptimal => {
self.hash(&mut hasher);
hasher.finish()
}
}
}
fn canonicalize_player(&mut self, map: &Map) {
let player_reachable_area = compute_reachable_area(self.player_position, |position| {
!(map[position].intersects(Tiles::Wall) || self.box_positions.contains(&position))
});
self.player_position = compute_area_anchor(&player_reachable_area).unwrap();
}
}
impl Hash for State {
fn hash<H: Hasher>(&self, state: &mut H) {
self.player_position.hash(state);
let mut sorted_boxes: Vec<_> = self.box_positions.iter().collect();
sorted_boxes.sort_by_key(|position| (position.x, position.y));
for box_position in sorted_boxes {
box_position.hash(state);
}
}
}