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 24, 2024
1 parent 17d00fe commit 9bbfb4c
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 32 deletions.
9 changes: 9 additions & 0 deletions src/board.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub struct Board {
}

impl Board {
/// Creates a new board with the specified level.
pub fn with_level(level: Level) -> Self {
Self {
level,
Expand All @@ -21,6 +22,7 @@ impl Board {
}
}

/// Checks if the player can move or push in the specified direction.
pub fn moveable(&self, direction: Direction) -> bool {
let player_next_position = self.level.player_position + direction.to_vector();
if self
Expand All @@ -47,6 +49,7 @@ impl Board {
return true;
}

/// Moves the player or pushes a crate in the specified direction.
pub fn move_or_push(&mut self, direction: Direction) {
let direction_vector = direction.to_vector();
let player_next_position = self.level.player_position + direction_vector;
Expand Down Expand Up @@ -80,6 +83,7 @@ impl Board {
self.undone_movements.clear();
}

/// Undoes the last push.
pub fn undo_push(&mut self) {
while let Some(history) = self.movements.last() {
if history.is_push {
Expand All @@ -90,6 +94,7 @@ impl Board {
}
}

/// Undoes the last move.
pub fn undo_move(&mut self) {
debug_assert!(!self.movements.is_empty());
let history = self.movements.pop().unwrap();
Expand All @@ -103,6 +108,7 @@ impl Board {
self.undone_movements.push(history);
}

/// Redoes the last push.
pub fn redo_push(&mut self) {
while let Some(history) = self.undone_movements.last() {
if history.is_push {
Expand All @@ -113,6 +119,7 @@ impl Board {
}
}

/// Redoes the last move.
pub fn redo_move(&mut self) {
debug_assert!(!self.undone_movements.is_empty());
let history = self.undone_movements.pop().unwrap();
Expand All @@ -121,10 +128,12 @@ impl Board {
self.undone_movements = undone_movements;
}

/// Checks if the level is solved.
pub fn is_solved(&self) -> bool {
self.level.crate_positions == self.level.target_positions
}

/// Returns the player's current orientation.
pub fn player_orientation(&self) -> Direction {
self.movements
.last()
Expand Down
22 changes: 14 additions & 8 deletions src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@ pub struct Database {
}

impl Database {
#[allow(dead_code)]
pub fn from_memory() -> Self {
/// Creates a new Database instance with a connection to a file-based database.
pub fn from_file<P: AsRef<Path>>(path: P) -> Self {
Self {
connection: Connection::open_in_memory().expect("failed to open database"),
connection: Connection::open(path).expect("failed to open database"),
}
}

pub fn from_file<P: AsRef<Path>>(path: P) -> Self {
/// Creates a new Database instance with an in-memory connection.
#[allow(dead_code)]
pub fn from_memory() -> Self {
Self {
connection: Connection::open(path).expect("failed to open database"),
connection: Connection::open_in_memory().expect("failed to open database"),
}
}

Expand Down Expand Up @@ -86,6 +88,7 @@ impl Database {
);
}

/// Returns the level ID by the provided level.
pub fn get_level_id(&self, level: &Level) -> Option<u64> {
let hash = Database::normalized_hash(level);
match self.connection.query_row(
Expand All @@ -98,6 +101,7 @@ impl Database {
}
}

/// Returns a level based by ID.
pub fn get_level_by_id(&self, id: u64) -> Option<Level> {
let mut statement = self
.connection
Expand All @@ -124,6 +128,7 @@ impl Database {
Some(level)
}

/// Returns the ID of the next unsolved level after the provided ID.
pub fn next_unsolved_level_id(&self, id: u64) -> Option<u64> {
let mut statement = self.connection.prepare(
"SELECT id FROM tb_level WHERE id > ? AND id NOT IN (SELECT level_id FROM tb_solution) ORDER BY id ASC LIMIT 1",
Expand Down Expand Up @@ -205,27 +210,28 @@ impl Database {
Some(best_push)
}

/// Retrieves the maximum level ID.
/// Returns the maximum level ID.
pub fn max_level_id(&self) -> Option<u64> {
self.connection
.query_row("SELECT MAX(id) FROM tb_level", [], |row| row.get(0))
.unwrap()
}

/// Retrieves the minimum level ID.
/// Returns the minimum level ID.
pub fn min_level_id(&self) -> Option<u64> {
self.connection
.query_row("SELECT MIN(id) FROM tb_level", [], |row| row.get(0))
.unwrap()
}

/// Computes a normalized hash for the provided level.
fn normalized_hash(level: &Level) -> String {
let mut hasher = SipHasher24::new();
let mut normalized_level = level.clone();
normalized_level.normalize();
normalized_level.hash(&mut hasher);
let hash = hasher.finish();
// 必须先将 hash 转为字符串, 否则 rusqlite 可能报错
// Must convert the hash to a string first, otherwise rusqlite may throw an error.
hash.to_string()
}
}
5 changes: 5 additions & 0 deletions src/level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ impl fmt::Display for ParseMapError {
type Result<T> = std::result::Result<T, ParseMapError>;

impl Level {
/// Creates a new level.
pub fn new(
map: Vec<String>,
dimensions: Vector2<i32>,
Expand Down Expand Up @@ -257,6 +258,7 @@ impl Level {
&mut self.data[(position.y * self.dimensions.x + position.x) as usize]
}

/// Exports the map layout as a XSB format string.
pub fn export_map(&self) -> String {
let mut result = String::new();
for y in 0..self.dimensions.y {
Expand All @@ -283,6 +285,7 @@ impl Level {
result
}

/// Exports metadata as a XSB format string.
pub fn export_metadata(&self) -> String {
let mut result = String::new();
for (key, value) in self.metadata.iter() {
Expand All @@ -291,6 +294,7 @@ impl Level {
result
}

/// Normalizes the level.
pub fn normalize(&mut self) {
assert!(self
.get_unchecked(&self.player_position)
Expand Down Expand Up @@ -616,6 +620,7 @@ impl Level {
.collect();
}

/// Checks if a position is within the bounds of the level.
pub fn in_bounds(&self, position: &Vector2<i32>) -> bool {
0 <= position.x
&& position.x < self.dimensions.x
Expand Down
18 changes: 11 additions & 7 deletions src/solver/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl Solver {
instance
}

/// Search for solution using the A* algorithm.
/// Searches for solution using the A* algorithm.
pub fn search(&mut self, timeout: Duration) -> Result<Movements> {
let timer = Instant::now();
self.visited
Expand Down Expand Up @@ -116,10 +116,12 @@ impl Solver {
self.heap.peek()
}

/// Returns a reference to the set of tunnels.
pub fn tunnels(&self) -> &HashSet<(Vector2<i32>, Direction)> {
self.tunnels.get_or_init(|| self.calculate_tunnels())
}

/// Calculates and returns the set of tunnels in the level.
fn calculate_tunnels(&self) -> HashSet<(Vector2<i32>, Direction)> {
let mut tunnels = HashSet::new();
for x in 1..self.level.dimensions.x - 1 {
Expand Down Expand Up @@ -225,11 +227,13 @@ impl Solver {
tunnels
}

/// Returns a reference to the set of lower bounds.
pub fn lower_bounds(&self) -> &HashMap<Vector2<i32>, usize> {
self.lower_bounds
.get_or_init(|| self.calculate_lower_bounds())
}

/// Calculates and returns the set of lower bounds.
fn calculate_lower_bounds(&self) -> HashMap<Vector2<i32>, usize> {
match self.lower_bound_method {
LowerBoundMethod::MinimumPush => self.minimum_push_lower_bounds(),
Expand All @@ -238,6 +242,7 @@ impl Solver {
}
}

/// Calculates and returns the lower bounds using the minimum push method.
fn minimum_push_lower_bounds(&self) -> HashMap<Vector2<i32>, usize> {
let mut lower_bounds = HashMap::new();
for target_position in &self.level.target_positions {
Expand Down Expand Up @@ -287,6 +292,7 @@ impl Solver {
}
}

/// Calculates and returns the lower bounds using the minimum move method.
fn minimum_move_lower_bounds(&self) -> HashMap<Vector2<i32>, usize> {
let mut lower_bounds = HashMap::new();
for x in 1..self.level.dimensions.x - 1 {
Expand Down Expand Up @@ -325,6 +331,7 @@ impl Solver {
lower_bounds
}

/// Calculates and returns the lower bounds using the Manhattan distance method.
fn manhattan_distance_lower_bounds(&self) -> HashMap<Vector2<i32>, usize> {
let mut lower_bounds = HashMap::new();
for x in 1..self.level.dimensions.x - 1 {
Expand All @@ -351,24 +358,21 @@ impl Solver {
lower_bounds
}

/// Shrinks the heap by retaining only a subset of states based on heuristics.
#[allow(dead_code)]
fn shrink_heap(heap: &mut BinaryHeap<State>) {
let max_pressure = 200_000;
if heap.len() > max_pressure {
let mut heuristics: Vec<_> = heap.iter().map(|state| state.heuristic()).collect();
heuristics.sort_unstable();
let mut costs: Vec<_> = heap.iter().map(|state| state.move_count()).collect();
costs.sort_unstable();

let alpha = 0.8;
let heuristic_median = heuristics[(heuristics.len() as f32 * alpha) as usize];
let cost_median = costs[(costs.len() as f32 * alpha) as usize];
heap.retain(|state| {
state.heuristic() <= heuristic_median && state.move_count() <= cost_median
});
heap.retain(|state| state.heuristic() <= heuristic_median);
}
}

/// Prints the lower bounds for each position in the level.
#[allow(dead_code)]
pub fn print_lower_bounds(&self) {
for y in 0..self.level.dimensions.y {
Expand Down
Loading

0 comments on commit 9bbfb4c

Please sign in to comment.