Skip to content

Commit

Permalink
refactor: refactor some code
Browse files Browse the repository at this point in the history
  • Loading branch information
ShenMian committed Jan 22, 2024
1 parent 28d30c8 commit 1a3f016
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 135 deletions.
1 change: 1 addition & 0 deletions src/level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ impl Level {
Ok(instance)
}

/// Creates a new empty level.
pub fn empty() -> Self {
Self {
data: vec![Tile::Void; 0],
Expand Down
252 changes: 120 additions & 132 deletions src/solver/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pub enum SolveError {
type Result<T> = std::result::Result<T, SolveError>;

impl Solver {
/// Creates a new solver.
pub fn new(mut level: Level, strategy: Strategy, lower_bound_method: LowerBoundMethod) -> Self {
level.clear(Tile::Player | Tile::Crate);
let mut instance = Self {
Expand All @@ -81,28 +82,27 @@ impl Solver {
instance
}

pub fn solve(&mut self, timeout: Duration) -> Result<Movements> {
debug_assert!(!self.heap.is_empty());
/// Search for solution using the A* algorithm.
pub fn search(&mut self, timeout: Duration) -> Result<Movements> {
let timer = Instant::now();
self.visited
.insert(self.heap.peek().unwrap().normalized(&self));
while let Some(state) = self.heap.pop() {
self.visited.insert(state.normalized(&self));

if timer.elapsed() >= timeout {
return Err(SolveError::Timeout);
}

// Solver::shrink_heap(&mut self.heap);
// Solver::print_info(&self.visited, &self.heap, &state);
if state.is_solved(&self) {
return Ok(state.movements);
}

for successor in state.successors(&self) {
if self.visited.contains(&successor.normalized(&self)) {
if !self.visited.insert(successor.normalized(&self)) {
continue;
}
if successor.is_solved(&self) {
return Ok(successor.movements);
}
self.heap.push(successor);
}

// Solver::shrink_heap(&mut self.heap);
}

Err(SolveError::NoSolution)
Expand All @@ -112,10 +112,119 @@ impl Solver {
self.strategy
}

/// Returns the best state in the binary heap, or `None` if it is empty.
pub fn best_state(&self) -> Option<&State> {
self.heap.peek()
}

pub fn tunnels(&self) -> &HashSet<(Vector2<i32>, Direction)> {
self.tunnels.get_or_init(|| self.calculate_tunnels())
}

fn calculate_tunnels(&self) -> HashSet<(Vector2<i32>, Direction)> {
let mut tunnels = HashSet::new();
for x in 1..self.level.dimensions.x - 1 {
for y in 1..self.level.dimensions.y - 1 {
let player_position = Vector2::new(x, y);
if !self
.level
.get_unchecked(&player_position)
.intersects(Tile::Floor)
{
continue;
}

for (up, right, _down, left) in [
Direction::Up,
Direction::Right,
Direction::Down,
Direction::Left,
Direction::Up,
Direction::Right,
Direction::Down,
]
.iter()
.tuple_windows::<(_, _, _, _)>()
{
// #$#
// #@#
if self
.level
.get_unchecked(&(player_position + left.to_vector()))
.intersects(Tile::Wall)
&& self
.level
.get_unchecked(&(player_position + right.to_vector()))
.intersects(Tile::Wall)
&& self
.level
.get_unchecked(&(player_position + up.to_vector() + left.to_vector()))
.intersects(Tile::Wall)
&& self
.level
.get_unchecked(&(player_position + up.to_vector() + right.to_vector()))
.intersects(Tile::Wall)
&& self
.level
.get_unchecked(&(player_position + up.to_vector()))
.intersects(Tile::Floor)
&& !self
.level
.get_unchecked(&(player_position + up.to_vector()))
.intersects(Tile::Target)
{
tunnels.insert((player_position, *up));
}

// #$_ _$#
// #@# #@#
if self
.level
.get_unchecked(&(player_position + left.to_vector()))
.intersects(Tile::Wall)
&& self
.level
.get_unchecked(&(player_position + right.to_vector()))
.intersects(Tile::Wall)
&& (self
.level
.get_unchecked(&(player_position + up.to_vector() + right.to_vector()))
.intersects(Tile::Wall)
&& self
.level
.get_unchecked(
&(player_position + up.to_vector() + left.to_vector()),
)
.intersects(Tile::Floor)
|| self
.level
.get_unchecked(
&(player_position + up.to_vector() + right.to_vector()),
)
.intersects(Tile::Floor)
&& self
.level
.get_unchecked(
&(player_position + up.to_vector() + left.to_vector()),
)
.intersects(Tile::Wall))
&& self
.level
.get_unchecked(&(player_position + up.to_vector()))
.intersects(Tile::Floor)
&& !self
.level
.get_unchecked(&(player_position + up.to_vector()))
.intersects(Tile::Target)
{
tunnels.insert((player_position, *up));
}
}
}
}
tunnels
}

pub fn lower_bounds(&self) -> &HashMap<Vector2<i32>, usize> {
self.lower_bounds
.get_or_init(|| self.calculate_lower_bounds())
Expand Down Expand Up @@ -242,114 +351,6 @@ impl Solver {
lower_bounds
}

pub fn tunnels(&self) -> &HashSet<(Vector2<i32>, Direction)> {
self.tunnels.get_or_init(|| self.calculate_tunnels())
}

fn calculate_tunnels(&self) -> HashSet<(Vector2<i32>, Direction)> {
let mut tunnels = HashSet::new();
for x in 1..self.level.dimensions.x - 1 {
for y in 1..self.level.dimensions.y - 1 {
let player_position = Vector2::new(x, y);
if !self
.level
.get_unchecked(&player_position)
.intersects(Tile::Floor)
{
continue;
}

for (up, right, _down, left) in [
Direction::Up,
Direction::Right,
Direction::Down,
Direction::Left,
Direction::Up,
Direction::Right,
Direction::Down,
]
.iter()
.tuple_windows::<(_, _, _, _)>()
{
// #$#
// #@#
if self
.level
.get_unchecked(&(player_position + left.to_vector()))
.intersects(Tile::Wall)
&& self
.level
.get_unchecked(&(player_position + right.to_vector()))
.intersects(Tile::Wall)
&& self
.level
.get_unchecked(&(player_position + up.to_vector() + left.to_vector()))
.intersects(Tile::Wall)
&& self
.level
.get_unchecked(&(player_position + up.to_vector() + right.to_vector()))
.intersects(Tile::Wall)
&& self
.level
.get_unchecked(&(player_position + up.to_vector()))
.intersects(Tile::Floor)
&& !self
.level
.get_unchecked(&(player_position + up.to_vector()))
.intersects(Tile::Target)
{
tunnels.insert((player_position, *up));
}

// #$_ _$#
// #@# #@#
if self
.level
.get_unchecked(&(player_position + left.to_vector()))
.intersects(Tile::Wall)
&& self
.level
.get_unchecked(&(player_position + right.to_vector()))
.intersects(Tile::Wall)
&& (self
.level
.get_unchecked(&(player_position + up.to_vector() + right.to_vector()))
.intersects(Tile::Wall)
&& self
.level
.get_unchecked(
&(player_position + up.to_vector() + left.to_vector()),
)
.intersects(Tile::Floor)
|| self
.level
.get_unchecked(
&(player_position + up.to_vector() + right.to_vector()),
)
.intersects(Tile::Floor)
&& self
.level
.get_unchecked(
&(player_position + up.to_vector() + left.to_vector()),
)
.intersects(Tile::Wall))
&& self
.level
.get_unchecked(&(player_position + up.to_vector()))
.intersects(Tile::Floor)
&& !self
.level
.get_unchecked(&(player_position + up.to_vector()))
.intersects(Tile::Target)
{
tunnels.insert((player_position, *up));
}
}
}
}
tunnels
}

#[allow(dead_code)]
fn shrink_heap(heap: &mut BinaryHeap<State>) {
let max_pressure = 200_000;
Expand Down Expand Up @@ -382,19 +383,6 @@ impl Solver {
println!();
}
}

#[allow(dead_code)]
fn print_info(visited: &HashSet<State>, heap: &BinaryHeap<State>, state: &State) {
print!(
"Visited: {:<6}, Heuristic: {:<4}, Moves: {:<4}, Pushes: {:<4}, Pressure: {:<4}\r",
visited.len(),
state.heuristic(),
state.move_count(),
state.push_count(),
heap.len()
);
std::io::stdout().flush().unwrap();
}
}

#[derive(Copy, Clone, Eq, PartialEq, Hash)]
Expand Down
2 changes: 1 addition & 1 deletion src/solver/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl Hash for State {

impl Ord for State {
fn cmp(&self, other: &Self) -> Ordering {
other.heuristic.cmp(&self.heuristic)
self.heuristic.cmp(&other.heuristic).reverse()
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/systems/auto_solve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub fn update_solver(
let mut solver = solver.lock().unwrap();
let timeout = Duration::from_millis(50);
let timer = Instant::now();
match solver.solve(timeout) {
match solver.search(timeout) {
Ok(solution) => {
let mut verify_board = board.clone();
for movement in &*solution {
Expand Down
2 changes: 1 addition & 1 deletion src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ mod tests {
let level = levels[id].clone();
let mut solver =
Solver::new(level.clone(), Strategy::Fast, LowerBoundMethod::MinimumMove);
let solution = solver.solve(Duration::from_secs(time_limit));
let solution = solver.search(Duration::from_secs(time_limit));
if solution.is_err() {
println!("{}", level.export_map());
println!("{:?}\n\n", solution.clone().err());
Expand Down

0 comments on commit 1a3f016

Please sign in to comment.