diff --git a/src/board.rs b/src/board.rs index bfb01f5..9b90d9a 100644 --- a/src/board.rs +++ b/src/board.rs @@ -1,17 +1,17 @@ -use soukoban::{direction::Direction, Action, Actions, Level, Tiles}; +use soukoban::{direction::Direction, Action, Actions, Map, Tiles}; #[derive(Clone)] pub struct Board { - pub level: Level, + pub map: Map, actions: Actions, undone_actions: Actions, } impl Board { /// Creates a new board with the specified level. - pub fn with_level(level: Level) -> Self { + pub fn with_map(map: Map) -> Self { Self { - level, + map, actions: Actions::new(), undone_actions: Actions::new(), } @@ -19,14 +19,13 @@ impl Board { /// Checks if the player can move or push in the specified direction. pub fn moveable(&self, direction: Direction) -> bool { - let map = self.level.map(); - let player_next_position = map.player_position() + &direction.into(); - if map[player_next_position].intersects(Tiles::Wall) { + let player_next_position = self.map.player_position() + &direction.into(); + if self.map[player_next_position].intersects(Tiles::Wall) { return false; } - if map[player_next_position].intersects(Tiles::Box) { + if self.map[player_next_position].intersects(Tiles::Box) { let box_next_position = player_next_position + &direction.into(); - if map[box_next_position].intersects(Tiles::Box | Tiles::Wall) { + if self.map[box_next_position].intersects(Tiles::Box | Tiles::Wall) { return false; } } @@ -34,25 +33,25 @@ impl Board { } /// Moves the player or pushes a box in the specified direction. - pub fn move_or_push(&mut self, direction: Direction) { - let map = self.level.map_mut(); + pub fn do_action(&mut self, direction: Direction) { let direction_vector = &direction.into(); - let player_next_position = map.player_position() + direction_vector; - if map[player_next_position].intersects(Tiles::Wall) { + let player_next_position = self.map.player_position() + direction_vector; + if self.map[player_next_position].intersects(Tiles::Wall) { return; } - if map[player_next_position].intersects(Tiles::Box) { + if self.map[player_next_position].intersects(Tiles::Box) { let box_next_position = player_next_position + direction_vector; - if map[box_next_position].intersects(Tiles::Wall | Tiles::Box) { + if self.map[box_next_position].intersects(Tiles::Wall | Tiles::Box) { return; } - map.set_box_position(player_next_position, box_next_position); + self.map + .set_box_position(player_next_position, box_next_position); self.actions.push(Action::Push(direction)); } else { self.actions.push(Action::Move(direction)); } - map.set_player_position(player_next_position); + self.map.set_player_position(player_next_position); self.undone_actions.clear(); } @@ -70,16 +69,15 @@ impl Board { /// Undoes the last move. pub fn undo_move(&mut self) { debug_assert!(!self.actions.is_empty()); - let map = self.level.map_mut(); let history = self.actions.pop().unwrap(); let direction = history.direction(); if history.is_push() { - let box_position = map.player_position() + &direction.into(); - let player_position = map.player_position(); - map.set_box_position(box_position, player_position); + let box_position = self.map.player_position() + &direction.into(); + let player_position = self.map.player_position(); + self.map.set_box_position(box_position, player_position); } - let player_prev_position = map.player_position() - &direction.into(); - map.set_player_position(player_prev_position); + let player_prev_position = self.map.player_position() - &direction.into(); + self.map.set_player_position(player_prev_position); self.undone_actions.push(history); } @@ -99,13 +97,13 @@ impl Board { debug_assert!(!self.undone_actions.is_empty()); let history = self.undone_actions.pop().unwrap(); let undone_actions = self.undone_actions.clone(); - self.move_or_push(history.direction()); + self.do_action(history.direction()); self.undone_actions = undone_actions; } /// Checks if the level is solved. pub fn is_solved(&self) -> bool { - self.level.map().box_positions() == self.level.map().goal_positions() + self.map.box_positions() == self.map.goal_positions() } pub fn actions(&self) -> &Actions { diff --git a/src/resources.rs b/src/resources.rs index 5cbc1d9..1c7517e 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -1,7 +1,7 @@ use bevy::{prelude::*, time::Stopwatch}; use nalgebra::Vector2; use serde::{Deserialize, Serialize}; -use soukoban::{direction::Direction, Level, Map}; +use soukoban::{direction::Direction, Map}; use crate::{board::Board, database, solve::solver::*, utils::PushState}; @@ -86,14 +86,12 @@ impl Default for SolverState { fn default() -> Self { Self { solver: Mutex::new(Solver::new( - Level::from_map(Map::with_dimensions(Vector2::new(0, 0))), + Map::with_dimensions(Vector2::new(0, 0)), Strategy::default(), LowerBoundMethod::default(), )), stopwatch: Stopwatch::new(), - origin_board: Board::with_level(Level::from_map(Map::with_dimensions(Vector2::new( - 0, 0, - )))), + origin_board: Board::with_map(Map::with_dimensions(Vector2::new(0, 0))), } } } diff --git a/src/solve/solver.rs b/src/solve/solver.rs index 74a0247..60aed66 100644 --- a/src/solve/solver.rs +++ b/src/solve/solver.rs @@ -9,7 +9,7 @@ use crate::{box_pushable_paths_with_positions, solve::state::*}; use itertools::Itertools; use nalgebra::Vector2; use serde::{Deserialize, Serialize}; -use soukoban::{direction::Direction, path_finding::reachable_area, Actions, Level, Tiles}; +use soukoban::{direction::Direction, path_finding::reachable_area, Actions, Map, Tiles}; #[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Serialize, Deserialize)] pub enum Strategy { @@ -41,7 +41,7 @@ pub enum LowerBoundMethod { } pub struct Solver { - pub level: Level, + pub map: Map, strategy: Strategy, lower_bound_method: LowerBoundMethod, lower_bounds: OnceCell, usize>>, @@ -60,9 +60,9 @@ type Result = std::result::Result; impl Solver { /// Creates a new solver. - pub fn new(level: Level, strategy: Strategy, lower_bound_method: LowerBoundMethod) -> Self { + pub fn new(map: Map, strategy: Strategy, lower_bound_method: LowerBoundMethod) -> Self { let mut instance = Self { - level, + map, strategy, lower_bound_method, lower_bounds: OnceCell::new(), @@ -71,8 +71,8 @@ impl Solver { heap: BinaryHeap::new(), }; instance.heap.push(State::new( - instance.level.map().player_position(), - instance.level.map().box_positions().clone(), + instance.map.player_position(), + instance.map.box_positions().clone(), Actions::new(), &instance, )); @@ -121,12 +121,11 @@ impl Solver { /// Calculates and returns the set of tunnels in the level. fn calculate_tunnels(&self) -> HashSet<(Vector2, Direction)> { - let map = self.level.map(); let mut tunnels = HashSet::new(); - for x in 1..map.dimensions().x - 1 { - for y in 1..map.dimensions().y - 1 { + for x in 1..self.map.dimensions().x - 1 { + for y in 1..self.map.dimensions().y - 1 { let box_position = Vector2::new(x, y); - if !map[box_position].intersects(Tiles::Floor) { + if !self.map[box_position].intersects(Tiles::Floor) { continue; } @@ -147,19 +146,19 @@ impl Solver { // . . . // #$# or #$_ or _$# // #@# #@# #@# - if map[player_position + &left.into()].intersects(Tiles::Wall) - && map[player_position + &right.into()].intersects(Tiles::Wall) - && (map[box_position + &left.into()].intersects(Tiles::Wall) - && map[box_position + &right.into()].intersects(Tiles::Wall) - || map[box_position + &right.into()].intersects(Tiles::Wall) - && map[box_position + &left.into()].intersects(Tiles::Floor) - || map[box_position + &right.into()].intersects(Tiles::Floor) - && map[box_position + &left.into()].intersects(Tiles::Wall)) - && map[box_position].intersects(Tiles::Floor) + if self.map[player_position + &left.into()].intersects(Tiles::Wall) + && self.map[player_position + &right.into()].intersects(Tiles::Wall) + && (self.map[box_position + &left.into()].intersects(Tiles::Wall) + && self.map[box_position + &right.into()].intersects(Tiles::Wall) + || self.map[box_position + &right.into()].intersects(Tiles::Wall) + && self.map[box_position + &left.into()].intersects(Tiles::Floor) + || self.map[box_position + &right.into()].intersects(Tiles::Floor) + && self.map[box_position + &left.into()].intersects(Tiles::Wall)) + && self.map[box_position].intersects(Tiles::Floor) && self .lower_bounds() .contains_key(&(box_position + &up.into())) - && !map[box_position].intersects(Tiles::Goal) + && !self.map[box_position].intersects(Tiles::Goal) { tunnels.insert((player_position, up)); } @@ -187,8 +186,7 @@ impl Solver { /// Calculates and returns the lower bounds using the minimum push method. fn minimum_push_lower_bounds(&self) -> HashMap, usize> { let mut lower_bounds = HashMap::new(); - let map = self.level.map(); - for goal_position in map.goal_positions() { + for goal_position in self.map.goal_positions() { lower_bounds.insert(*goal_position, 0); let mut player_position = None; for pull_direction in [ @@ -199,9 +197,9 @@ impl Solver { ] { let next_box_position = goal_position + &pull_direction.into(); let next_player_position = next_box_position + &pull_direction.into(); - if map.in_bounds(next_player_position) - && !map[next_player_position].intersects(Tiles::Wall) - && !map[next_box_position].intersects(Tiles::Wall) + if self.map.in_bounds(next_player_position) + && !self.map[next_player_position].intersects(Tiles::Wall) + && !self.map[next_box_position].intersects(Tiles::Wall) { player_position = Some(next_player_position); break; @@ -228,9 +226,8 @@ impl Solver { lower_bounds: &mut HashMap, usize>, visited: &mut HashSet<(Vector2, Direction)>, ) { - let map = self.level.map(); let player_reachable_area = reachable_area(player_position, |position| { - !map[position].intersects(Tiles::Wall) && position != box_position + !self.map[position].intersects(Tiles::Wall) && position != box_position }); for pull_direction in [ Direction::Up, @@ -239,13 +236,13 @@ impl Solver { Direction::Left, ] { let next_box_position = box_position + &pull_direction.into(); - if map[next_box_position].intersects(Tiles::Wall) { + if self.map[next_box_position].intersects(Tiles::Wall) { continue; } let next_player_position = next_box_position + &pull_direction.into(); - if !map.in_bounds(next_player_position) - || map[next_player_position].intersects(Tiles::Wall) + if !self.map.in_bounds(next_player_position) + || self.map[next_player_position].intersects(Tiles::Wall) { continue; } @@ -273,27 +270,26 @@ impl Solver { /// Calculates and returns the lower bounds using the minimum move method. fn minimum_move_lower_bounds(&self) -> HashMap, usize> { let mut lower_bounds = HashMap::new(); - let map = self.level.map(); - for x in 1..map.dimensions().x - 1 { - for y in 1..map.dimensions().y - 1 { + for x in 1..self.map.dimensions().x - 1 { + for y in 1..self.map.dimensions().y - 1 { let position = Vector2::new(x, y); // There may be situations in the level where the box is // already on the goal and cannot be reached by the player. - if map[position].intersects(Tiles::Goal) { + if self.map[position].intersects(Tiles::Goal) { lower_bounds.insert(position, 0); continue; } - if !map[position].intersects(Tiles::Floor) - // || map[position].intersects(Tiles::Deadlock) + if !self.map[position].intersects(Tiles::Floor) + // || self.map[position].intersects(Tiles::Deadlock) { continue; } let paths = - box_pushable_paths_with_positions(&self.level, &position, &HashSet::new()); + box_pushable_paths_with_positions(&self.map, &position, &HashSet::new()); if let Some(lower_bound) = paths .iter() - .filter(|path| map[path.0.box_position].intersects(Tiles::Goal)) + .filter(|path| self.map[path.0.box_position].intersects(Tiles::Goal)) .map(|path| path.1.len() - 1) .min() { @@ -307,22 +303,22 @@ impl Solver { /// Calculates and returns the lower bounds using the Manhattan distance method. fn manhattan_distance_lower_bounds(&self) -> HashMap, usize> { let mut lower_bounds = HashMap::new(); - let map = self.level.map(); - for x in 1..map.dimensions().x - 1 { - for y in 1..map.dimensions().y - 1 { + for x in 1..self.map.dimensions().x - 1 { + for y in 1..self.map.dimensions().y - 1 { let position = Vector2::new(x, y); // There may be situations in the level where the box is // already on the goal and cannot be reached by the player. - if map[position].intersects(Tiles::Goal) { + if self.map[position].intersects(Tiles::Goal) { lower_bounds.insert(position, 0); continue; } - if !map[position].intersects(Tiles::Floor) - // || map.get(&position).intersects(Tiles::Deadlock) + if !self.map[position].intersects(Tiles::Floor) + // || self.map.get(&position).intersects(Tiles::Deadlock) { continue; } - let lower_bound = map + let lower_bound = self + .map .goal_positions() .iter() .map(|box_pos| manhattan_distance(box_pos, &position)) @@ -351,9 +347,8 @@ impl Solver { /// Prints the lower bounds for each position in the level. #[expect(dead_code)] pub fn print_lower_bounds(&self) { - let map = self.level.map(); - for y in 0..map.dimensions().y { - for x in 0..map.dimensions().x { + for y in 0..self.map.dimensions().y { + for x in 0..self.map.dimensions().x { let position = Vector2::new(x, y); if let Some(lower_bound) = self.lower_bounds().get(&position) { print!("{:3} ", lower_bound); diff --git a/src/solve/state.rs b/src/solve/state.rs index 09364fb..9a2a25d 100644 --- a/src/solve/state.rs +++ b/src/solve/state.rs @@ -139,9 +139,9 @@ impl State { new_box_positions.insert(new_box_position); // skip deadlocks - if !solver.level.map()[new_box_position].intersects(Tiles::Goal) + if !solver.map[new_box_position].intersects(Tiles::Goal) && deadlock::is_freeze_deadlock( - solver.level.map(), + &solver.map, new_box_position, &new_box_positions, &mut HashSet::new(), @@ -205,13 +205,12 @@ impl State { /// Checks if a position can block the player's movement. fn can_block_player(&self, position: Vector2, solver: &Solver) -> bool { - solver.level.map()[position].intersects(Tiles::Wall) - || self.box_positions.contains(&position) + solver.map[position].intersects(Tiles::Wall) || self.box_positions.contains(&position) } /// Checks if a position can block a box's movement. fn can_block_box(&self, position: Vector2, solver: &Solver) -> bool { - solver.level.map()[position].intersects(Tiles::Wall /* | Tiles::Deadlock */) + solver.map[position].intersects(Tiles::Wall /* | Tiles::Deadlock */) || !solver.lower_bounds().contains_key(&position) || self.box_positions.contains(&position) } diff --git a/src/systems/auto_move.rs b/src/systems/auto_move.rs index 2071040..bc76cfd 100644 --- a/src/systems/auto_move.rs +++ b/src/systems/auto_move.rs @@ -14,7 +14,7 @@ pub fn spawn_auto_move_marks( mut next_state: ResMut>, ) { let Board { board, tile_size } = board.single(); - let map = board.level.map(); + let map = &board.map; const MARK_COLOR: Srgba = LIME; const HIGHLIGHT_COLOR: Srgba = TURQUOISE; @@ -24,7 +24,7 @@ pub fn spawn_auto_move_marks( position: box_position, paths, } => { - *paths = box_pushable_paths(&board.level, box_position); + *paths = box_pushable_paths(map, box_position); // remove static deadlock positions let static_deadlocks = calculate_static_deadlocks(map); diff --git a/src/systems/auto_solve.rs b/src/systems/auto_solve.rs index a0c0b26..208bc7e 100644 --- a/src/systems/auto_solve.rs +++ b/src/systems/auto_solve.rs @@ -19,7 +19,7 @@ pub fn load_solver( *origin_board = board.clone(); let solver = solver.get_mut().unwrap(); *solver = Solver::new( - origin_board.level.clone(), + origin_board.map.clone(), config.solver.strategy, config.solver.lower_bound_method, ); @@ -53,7 +53,7 @@ pub fn spawn_lowerbound_marks( ), Transform::from_xyz( position.x as f32 * tile_size.x as f32, - (board.level.map().dimensions().y - position.y) as f32 * tile_size.y as f32, + (board.map.dimensions().y - position.y) as f32 * tile_size.y as f32, 10.0, ), StateScoped(AppState::AutoSolve), @@ -81,7 +81,7 @@ pub fn update_solver( origin_board, } = &mut *solver_state; - *board = crate::board::Board::with_level(origin_board.level.clone()); + *board = crate::board::Board::with_map(origin_board.map.clone()); let solver = solver.get_mut().unwrap(); let timeout = Duration::from_millis(50); @@ -90,7 +90,7 @@ pub fn update_solver( Ok(solution) => { let mut verify_board = board.clone(); for action in &*solution { - verify_board.move_or_push(action.direction()); + verify_board.do_action(action.direction()); } assert!(verify_board.is_solved()); @@ -133,7 +133,7 @@ pub fn update_solver( // best_state.actions.pushes() // ); for action in &*best_state.actions { - board.move_or_push(action.direction()); + board.do_action(action.direction()); } } } @@ -145,7 +145,7 @@ pub fn update_tile_translation( let Board { board, tile_size } = &board.single(); for (mut transform, grid_position) in tiles.iter_mut() { transform.translation.x = grid_position.x as f32 * tile_size.x as f32; - transform.translation.y = board.level.map().dimensions().y as f32 * tile_size.y as f32 + transform.translation.y = board.map.dimensions().y as f32 * tile_size.y as f32 - grid_position.y as f32 * tile_size.y as f32; } } @@ -155,8 +155,7 @@ pub fn update_tile_grid_position( mut box_grid_positions: Query<&mut GridPosition, (With, Without)>, board: Query<&Board>, ) { - let board = &board.single().board; - let map = board.level.map(); + let map = &board.single().board.map; let mut player_grid_positions = player_grid_positions.single_mut(); **player_grid_positions = map.player_position(); diff --git a/src/systems/input.rs b/src/systems/input.rs index ea54a17..844c74c 100644 --- a/src/systems/input.rs +++ b/src/systems/input.rs @@ -99,8 +99,8 @@ fn player_move_to( player_movement: &mut PlayerMovement, board: &crate::board::Board, ) { - if let Some(path) = find_path(board.level.map().player_position(), *target, |position| { - !board.level.map()[position].intersects(Tiles::Wall | Tiles::Box) + if let Some(path) = find_path(board.map.player_position(), *target, |position| { + !board.map[position].intersects(Tiles::Wall | Tiles::Box) }) { let directions = path .windows(2) @@ -128,11 +128,9 @@ fn instant_player_move_to( board_clone: &mut crate::board::Board, player_movement: &mut PlayerMovement, ) { - if let Some(path) = find_path( - board_clone.level.map().player_position(), - *target, - |position| !board_clone.level.map()[position].intersects(Tiles::Wall | Tiles::Box), - ) { + if let Some(path) = find_path(board_clone.map.player_position(), *target, |position| { + !board_clone.map[position].intersects(Tiles::Wall | Tiles::Box) + }) { let directions = path .windows(2) .map(|pos| Direction::try_from(pos[1] - pos[0]).unwrap()); @@ -147,7 +145,7 @@ fn instant_player_move( board_clone: &mut crate::board::Board, player_movement: &mut PlayerMovement, ) { - board_clone.move_or_push(direction); + board_clone.do_action(direction); player_movement.directions.push_front(direction); } @@ -288,7 +286,7 @@ pub fn mouse_input( mut auto_move_state: ResMut, ) { let Board { board, tile_size } = &mut *board.single_mut(); - let map = board.level.map(); + let map = &board.map; let (camera, camera_transform) = camera.single_mut(); if mouse_buttons.just_pressed(MouseButton::Left) && player_movement.directions.is_empty() { diff --git a/src/systems/level.rs b/src/systems/level.rs index 5020d17..4d55de2 100644 --- a/src/systems/level.rs +++ b/src/systems/level.rs @@ -29,9 +29,9 @@ pub fn setup_database(mut commands: Commands) { pub fn setup_level(mut commands: Commands, database: Res) { let database = database.lock().unwrap(); - let level = database.get_level_by_id(1).unwrap(); + let map = database.get_level_by_id(1).unwrap().into(); commands.spawn(Board { - board: board::Board::with_level(level), + board: board::Board::with_map(map), tile_size: Vector2::zeros(), }); @@ -79,13 +79,13 @@ pub fn spawn_board( transform.translation.x = (board_size.x - tile_size.x) as f32 / 2.0; transform.translation.y = (board_size.y + tile_size.y) as f32 / 2.0; - main_camera.target_scale = calculate_camera_default_scale(window.single(), &level); + main_camera.target_scale = calculate_camera_default_scale(window.single(), level.map()); // despawn the previous `Board` commands.entity(board.single()).despawn_recursive(); // spawn new `Board` - let board = board::Board::with_level(level.clone()); + let board = board::Board::with_map(level.map().clone()); commands .spawn(( Name::new("Board"), @@ -180,7 +180,7 @@ pub fn import_from_clipboard(level_id: &mut LevelId, database: &database::Databa pub fn export_to_clipboard(board: &crate::board::Board) { let mut clipboard = Clipboard::new().unwrap(); - clipboard.set_text(board.level.to_string()).unwrap(); + clipboard.set_text(board.map.to_string()).unwrap(); } /// Switches to the next unsolved level based on the current level ID. diff --git a/src/systems/render.rs b/src/systems/render.rs index d15f351..3c58ee9 100644 --- a/src/systems/render.rs +++ b/src/systems/render.rs @@ -1,7 +1,7 @@ use benimator::{Animation, FrameRate}; use bevy::{prelude::*, window::WindowResized, winit::WinitWindows}; use nalgebra::Vector2; -use soukoban::{direction::Direction, Level}; +use soukoban::{direction::Direction, Map}; use crate::{components::*, events::*, resources::*}; @@ -98,17 +98,15 @@ pub fn handle_player_movement( } if let Some(direction) = player_movement.directions.pop_back() { let occupied_goals_count = board - .level - .map() + .map .goal_positions() - .intersection(board.level.map().box_positions()) + .intersection(board.map.box_positions()) .count(); - board.move_or_push(direction); + board.do_action(direction); let new_occupied_goals_count = board - .level - .map() + .map .goal_positions() - .intersection(board.level.map().box_positions()) + .intersection(board.map.box_positions()) .count(); match new_occupied_goals_count.cmp(&occupied_goals_count) { Ordering::Greater => drop(box_enter_goal_events.send_default()), @@ -132,7 +130,7 @@ pub fn handle_player_movement( } } else { while let Some(direction) = player_movement.directions.pop_back() { - board.move_or_push(direction); + board.do_action(direction); player_grid_position.x += Into::>::into(direction).x; player_grid_position.y += Into::>::into(direction).y; @@ -167,7 +165,7 @@ pub fn smooth_tile_motion( let lerp = |a: f32, b: f32, t: f32| a + (b - a) * t; let target_x = grid_position.x as f32 * tile_size.x as f32; - let target_y = board.level.map().dimensions().y as f32 * tile_size.y as f32 + let target_y = board.map.dimensions().y as f32 * tile_size.y as f32 - grid_position.y as f32 * tile_size.y as f32; if (transform.translation.x - target_x).abs() > 0.001 { @@ -182,7 +180,7 @@ pub fn smooth_tile_motion( } } else { transform.translation.x = grid_position.x as f32 * tile_size.x as f32; - transform.translation.y = board.level.map().dimensions().y as f32 * tile_size.y as f32 + transform.translation.y = board.map.dimensions().y as f32 * tile_size.y as f32 - grid_position.y as f32 * tile_size.y as f32; } } @@ -213,8 +211,7 @@ pub fn update_grid_position_from_board( ) { update_grid_position_events.clear(); - let board = &board.single().board; - let map = board.level.map(); + let map = &board.single().board.map; let player_grid_position = &mut player.single_mut().0; player_grid_position.x = map.player_position().x; @@ -227,9 +224,7 @@ pub fn update_grid_position_from_board( .collect::>() .first() { - let new_position = *board - .level - .map() + let new_position = *map .box_positions() .difference(&box_grid_positions) .collect::>() @@ -257,13 +252,13 @@ pub fn adjust_camera_scale( events.clear(); camera.single_mut().target_scale = - calculate_camera_default_scale(window.single(), &board.single().board.level); + calculate_camera_default_scale(window.single(), &board.single().board.map); } /// Adjust the camera zoom to fit the entire board. -pub fn calculate_camera_default_scale(window: &Window, level: &Level) -> f32 { +pub fn calculate_camera_default_scale(window: &Window, map: &Map) -> f32 { let tile_size = Vector2::new(128.0, 128.0); - let board_size = tile_size.x as f32 * level.map().dimensions().map(|x| x as f32); + let board_size = tile_size.x as f32 * map.dimensions().map(|x| x as f32); let width_scale = board_size.x / window.resolution.width(); let height_scale = board_size.y / window.resolution.height(); diff --git a/src/test.rs b/src/test.rs index 8ee4755..9d3afd6 100644 --- a/src/test.rs +++ b/src/test.rs @@ -24,8 +24,11 @@ mod tests { } println!("#{} ({})", id + 1, id); let level = levels[id].clone(); - let mut solver = - Solver::new(level.clone(), Strategy::Fast, LowerBoundMethod::MinimumMove); + let mut solver = Solver::new( + level.map().clone(), + Strategy::Fast, + LowerBoundMethod::MinimumMove, + ); let solution = solver.search(Duration::from_secs(time_limit)); if solution.is_err() { println!("{}", level.map()); @@ -35,9 +38,9 @@ mod tests { } let solution = solution.unwrap(); - let mut board = Board::with_level(level); + let mut board = Board::with_map(level.into()); for action in &*solution { - board.move_or_push(action.direction()); + board.do_action(action.direction()); } assert!(board.is_solved()); } @@ -84,11 +87,10 @@ mod tests { #[test] #[cfg(not(debug_assertions))] fn solve_box_world() { - let levels = Level::load_from_str( - &fs::read_to_string("assets/levels/box_world_100.xsb").unwrap(), - ) - .collect::, _>>() - .unwrap(); + let levels = + Level::load_from_str(&fs::read_to_string("assets/levels/box_world_100.xsb").unwrap()) + .collect::, _>>() + .unwrap(); assert!( solve( &levels, diff --git a/src/utils.rs b/src/utils.rs index 50417fd..b77ada6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,5 @@ use nalgebra::Vector2; -use soukoban::{direction::Direction, path_finding::reachable_area, Level, Tiles}; +use soukoban::{direction::Direction, path_finding::reachable_area, Map, Tiles}; use std::{ collections::{HashMap, HashSet, VecDeque}, @@ -13,7 +13,7 @@ pub struct PushState { } pub fn box_pushable_paths_with_positions( - level: &Level, + map: &Map, box_position: &Vector2, initial_box_positions: &HashSet>, ) -> HashMap>> { @@ -21,8 +21,8 @@ pub fn box_pushable_paths_with_positions( let mut visited = HashSet::new(); let mut queue = VecDeque::new(); - let player_reachable_area = reachable_area(level.map().player_position(), |position| { - !level.map()[position].intersects(Tiles::Wall) && !initial_box_positions.contains(&position) + let player_reachable_area = reachable_area(map.player_position(), |position| { + !map[position].intersects(Tiles::Wall) && !initial_box_positions.contains(&position) }); for push_direction in [ Direction::Up, @@ -31,7 +31,7 @@ pub fn box_pushable_paths_with_positions( Direction::Right, ] { let player_position = box_position - &push_direction.into(); - if level.map()[player_position].intersects(Tiles::Wall) + if map[player_position].intersects(Tiles::Wall) || !player_reachable_area.contains(&player_position) { continue; @@ -51,7 +51,7 @@ pub fn box_pushable_paths_with_positions( let player_position = state.box_position - &state.push_direction.into(); let player_reachable_area = reachable_area(player_position, |position| { - !level.map()[position].intersects(Tiles::Wall) && !box_positions.contains(&position) + !map[position].intersects(Tiles::Wall) && !box_positions.contains(&position) }); for push_direction in [ @@ -63,13 +63,13 @@ pub fn box_pushable_paths_with_positions( let new_box_position = state.box_position + &push_direction.into(); let player_position = state.box_position - &push_direction.into(); - if level.map()[new_box_position].intersects(Tiles::Wall /* | Tiles::Deadlock */) + if map[new_box_position].intersects(Tiles::Wall /* | Tiles::Deadlock */) || box_positions.contains(&new_box_position) { continue; } - if level.map()[player_position].intersects(Tiles::Wall) + if map[player_position].intersects(Tiles::Wall) || !player_reachable_area.contains(&player_position) { continue; @@ -98,9 +98,9 @@ pub fn box_pushable_paths_with_positions( /// Finds paths for pushing a box from `box_position` to other positions. pub fn box_pushable_paths( - level: &Level, + map: &Map, box_position: &Vector2, ) -> HashMap>> { - debug_assert!(level.map().box_positions().contains(box_position)); - box_pushable_paths_with_positions(level, box_position, level.map().box_positions()) + debug_assert!(map.box_positions().contains(box_position)); + box_pushable_paths_with_positions(map, box_position, map.box_positions()) }