Skip to content

Commit

Permalink
feat(solver): add freeze deadlock detection
Browse files Browse the repository at this point in the history
  • Loading branch information
ShenMian committed Jan 20, 2024
1 parent 142f85d commit de1468a
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 15 deletions.
79 changes: 67 additions & 12 deletions src/solver/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,23 +101,20 @@ impl State {
continue;
}

// TODO: 检测是否会产生新的死锁
if self.can_block_crate(&new_crate_position, solver) {
continue;
}

let mut new_movements = self.movements.clone();
if let Some(path) =
find_path(&self.player_position, &next_player_position, |position| {
self.can_block_player(position, solver)
})
{
new_movements.extend(
path.windows(2)
.map(|p| Direction::from_vector(p[1] - p[0]).unwrap())
.map(Movement::with_move),
)
}
let path = find_path(&self.player_position, &next_player_position, |position| {
self.can_block_player(position, solver)
})
.unwrap();
new_movements.extend(
path.windows(2)
.map(|p| Direction::from_vector(p[1] - p[0]).unwrap())
.map(Movement::with_move),
);
new_movements.push(Movement::with_push(push_direction));

// skip tunnels
Expand All @@ -138,6 +135,18 @@ impl State {
new_crate_positions.remove(crate_position);
new_crate_positions.insert(new_crate_position);

// skip deadlocks
if !solver.level.target_positions.contains(&new_crate_position)
&& self.is_freeze_deadlock(
&new_crate_position,
&new_crate_positions,
solver,
&mut HashSet::new(),
)
{
continue;
}

let new_player_position = new_crate_position - push_direction.to_vector();

let new_state = State::new(
Expand Down Expand Up @@ -174,6 +183,52 @@ impl State {
instance
}

fn is_freeze_deadlock(
&self,
crate_position: &Vector2<i32>,
crate_positions: &HashSet<Vector2<i32>>,
solver: &Solver,
visited: &mut HashSet<Vector2<i32>>,
) -> bool {
if !visited.insert(*crate_position) {
return true;
}

for direction in [
Direction::Up,
Direction::Down,
Direction::Left,
Direction::Right,
]
.chunks(2)
{
let neighbors = [
crate_position + direction[0].to_vector(),
crate_position + direction[1].to_vector(),
];
if solver
.level
.get_unchecked(&neighbors[0])
.intersects(Tile::Wall)
|| solver
.level
.get_unchecked(&neighbors[1])
.intersects(Tile::Wall)
{
continue;
}
if (crate_positions.contains(&neighbors[0])
&& self.is_freeze_deadlock(&neighbors[0], crate_positions, solver, visited))
|| (crate_positions.contains(&neighbors[1])
&& self.is_freeze_deadlock(&neighbors[1], crate_positions, solver, visited))
{
continue;
}
return false;
}
return true;
}

/// Minimum number of pushes required to complete the level.
fn lower_bound(&self, solver: &Solver) -> usize {
*self
Expand Down
3 changes: 2 additions & 1 deletion src/systems/auto_solve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ pub fn spawn_lowerbound_marks(
mut board: Query<&mut Board>,
) {
let Board { board, tile_size } = &mut *board.single_mut();
let solver = solver_state.solver.lock().unwrap();

let lowerbounds = solver_state.solver.lock().unwrap().lower_bounds().clone();
let lowerbounds = solver.lower_bounds().clone();
let max_lowerbound = lowerbounds
.iter()
.map(|(_, lowerbound)| *lowerbound)
Expand Down
4 changes: 2 additions & 2 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ mod tests {
let levels = Level::load_from_file(Path::new("assets/levels/box_world.xsb")).unwrap();
let mut failed = 0;
for i in [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 19, 21, 24, 28, 29, 31, 34,
36, 40, 43, 61, 62, 64, 65, 69, 74, 76, 90, 91,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 21, 24, 28, 30, 31,
34, 36, 40, 43, 61, 62, 64, 65, 69, 74, 76, 90, 91,
] {
let level = levels[i].clone();
let mut solver = Solver::new(level.clone());
Expand Down

0 comments on commit de1468a

Please sign in to comment.