mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
astar
This commit is contained in:
parent
53d51a5ca9
commit
e1e1063d15
5 changed files with 326 additions and 463 deletions
1
azalea-nbt/src/serde/mod.rs
Normal file
1
azalea-nbt/src/serde/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
mod serializer;
|
205
azalea-nbt/src/serde/serializer.rs
Normal file
205
azalea-nbt/src/serde/serializer.rs
Normal file
|
@ -0,0 +1,205 @@
|
|||
use serde::{ser, Serialize};
|
||||
|
||||
use crate::Nbt;
|
||||
|
||||
use std;
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
use serde::de;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
// This is a bare-bones implementation. A real library would provide additional
|
||||
// information in its error type, for example the line and column at which the
|
||||
// error occurred, the byte offset into the input, or the current key being
|
||||
// processed.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Message(String),
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Error::Message(msg) => formatter.write_str(msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::Error for Error {
|
||||
fn custom<T: Display>(msg: T) -> Self {
|
||||
Error::Message(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl de::Error for Error {
|
||||
fn custom<T: Display>(msg: T) -> Self {
|
||||
Error::Message(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
impl<'a> ser::Serializer for &'a mut Nbt {
|
||||
type Ok = ();
|
||||
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Self;
|
||||
|
||||
type SerializeTuple = Self;
|
||||
|
||||
type SerializeTupleStruct = Self;
|
||||
|
||||
type SerializeTupleVariant = Self;
|
||||
|
||||
type SerializeMap = Self;
|
||||
|
||||
type SerializeStruct = Self;
|
||||
|
||||
type SerializeStructVariant = Self;
|
||||
|
||||
fn serialize_bool(self, v: bool) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_i8(self, v: i8) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_i16(self, v: i16) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_i32(self, v: i32) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_i64(self, v: i64) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_u8(self, v: u8) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_u16(self, v: u16) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_u32(self, v: u32) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_u64(self, v: u64) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_f32(self, v: f32) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_f64(self, v: f64) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_char(self, v: char) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_str(self, v: &str) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, v: &[u8]) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_some<T: ?Sized>(self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(self, name: &'static str) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
name: &'static str,
|
||||
variant_index: u32,
|
||||
variant: &'static str,
|
||||
) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct<T: ?Sized>(self, name: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T: ?Sized>(
|
||||
self,
|
||||
name: &'static str,
|
||||
variant_index: u32,
|
||||
variant: &'static str,
|
||||
value: &T,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_seq(self, len: Option<usize>) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, len: usize) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
name: &'static str,
|
||||
variant_index: u32,
|
||||
variant: &'static str,
|
||||
len: usize,
|
||||
) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_map(self, len: Option<usize>) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_struct(self, name: &'static str, len: usize) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
name: &'static str,
|
||||
variant_index: u32,
|
||||
variant: &'static str,
|
||||
len: usize,
|
||||
) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
106
azalea/src/pathfinder/astar.rs
Normal file
106
azalea/src/pathfinder/astar.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
use std::{cmp::Reverse, collections::HashMap, fmt::Debug, hash::Hash, ops::Add};
|
||||
|
||||
use priority_queue::PriorityQueue;
|
||||
|
||||
pub fn a_star<N, W, HeuristicFn, SuccessorsFn, SuccessFn>(
|
||||
start: N,
|
||||
heuristic: HeuristicFn,
|
||||
successors: SuccessorsFn,
|
||||
success: SuccessFn,
|
||||
) -> Option<Vec<N>>
|
||||
where
|
||||
N: Eq + Hash + Copy + Debug,
|
||||
W: PartialOrd + Default + Copy + num_traits::Bounded + Debug + Add<Output = W>,
|
||||
HeuristicFn: Fn(&N) -> W,
|
||||
SuccessorsFn: Fn(&N) -> Vec<Edge<N, W>>,
|
||||
SuccessFn: Fn(&N) -> bool,
|
||||
{
|
||||
let mut open_set = PriorityQueue::new();
|
||||
open_set.push(start, Reverse(Weight(W::default())));
|
||||
let mut nodes: HashMap<N, Node<N, W>> = HashMap::new();
|
||||
nodes.insert(
|
||||
start,
|
||||
Node {
|
||||
data: start,
|
||||
came_from: None,
|
||||
g_score: W::default(),
|
||||
f_score: W::max_value(),
|
||||
},
|
||||
);
|
||||
|
||||
while let Some((current_node, _)) = open_set.pop() {
|
||||
if success(¤t_node) {
|
||||
return Some(reconstruct_path(&nodes, current_node));
|
||||
}
|
||||
|
||||
let current_g_score = nodes
|
||||
.get(¤t_node)
|
||||
.map(|n| n.g_score)
|
||||
.unwrap_or(W::max_value());
|
||||
|
||||
for neighbor in successors(¤t_node) {
|
||||
let tentative_g_score = current_g_score + neighbor.cost;
|
||||
let neighbor_g_score = nodes
|
||||
.get(&neighbor.target)
|
||||
.map(|n| n.g_score)
|
||||
.unwrap_or(W::max_value());
|
||||
if tentative_g_score < neighbor_g_score {
|
||||
let f_score = tentative_g_score + heuristic(&neighbor.target);
|
||||
nodes.insert(
|
||||
neighbor.target,
|
||||
Node {
|
||||
data: neighbor.target,
|
||||
came_from: Some(current_node),
|
||||
g_score: tentative_g_score,
|
||||
f_score,
|
||||
},
|
||||
);
|
||||
open_set.push(neighbor.target, Reverse(Weight(f_score)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn reconstruct_path<N, W>(nodes: &HashMap<N, Node<N, W>>, current: N) -> Vec<N>
|
||||
where
|
||||
N: Eq + Hash + Copy + Debug,
|
||||
W: PartialOrd + Default + Copy + num_traits::Bounded + Debug,
|
||||
{
|
||||
let mut path = vec![current];
|
||||
let mut current = current;
|
||||
while let Some(node) = nodes.get(¤t) {
|
||||
if let Some(came_from) = node.came_from {
|
||||
path.push(came_from);
|
||||
current = came_from;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
path.reverse();
|
||||
path
|
||||
}
|
||||
|
||||
pub struct Node<N, W> {
|
||||
pub data: N,
|
||||
pub came_from: Option<N>,
|
||||
pub g_score: W,
|
||||
pub f_score: W,
|
||||
}
|
||||
|
||||
pub struct Edge<N: Eq + Hash + Copy, W: PartialOrd + Copy> {
|
||||
pub target: N,
|
||||
pub cost: W,
|
||||
}
|
||||
|
||||
#[derive(PartialOrd, PartialEq)]
|
||||
pub struct Weight<W: PartialOrd + Debug>(W);
|
||||
impl<W: PartialOrd + Debug> Ord for Weight<W> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.0
|
||||
.partial_cmp(&other.0)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
}
|
||||
}
|
||||
impl<W: PartialOrd + Debug> Eq for Weight<W> {}
|
|
@ -1,7 +1,8 @@
|
|||
mod astar;
|
||||
mod moves;
|
||||
mod mtdstarlite;
|
||||
|
||||
use crate::bot::{JumpEvent, LookAtEvent};
|
||||
use crate::pathfinder::astar::a_star;
|
||||
use crate::{SprintDirection, WalkDirection};
|
||||
|
||||
use crate::app::{App, CoreSchedule, IntoSystemAppConfig, Plugin};
|
||||
|
@ -13,6 +14,7 @@ use crate::ecs::{
|
|||
schedule::IntoSystemConfig,
|
||||
system::{Commands, Query, Res},
|
||||
};
|
||||
use astar::Edge;
|
||||
use azalea_client::{StartSprintEvent, StartWalkEvent};
|
||||
use azalea_core::{BlockPos, CardinalDirection};
|
||||
use azalea_physics::PhysicsSet;
|
||||
|
@ -25,8 +27,6 @@ use azalea_world::{
|
|||
use bevy_tasks::{AsyncComputeTaskPool, Task};
|
||||
use futures_lite::future;
|
||||
use log::{debug, error};
|
||||
use mtdstarlite::Edge;
|
||||
pub use mtdstarlite::MTDStarLite;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -152,17 +152,22 @@ fn goto_listener(
|
|||
edges
|
||||
};
|
||||
|
||||
let mut pf = MTDStarLite::new(
|
||||
// let mut pf = MTDStarLite::new(
|
||||
// start,
|
||||
// end,
|
||||
// |n| goal.heuristic(n),
|
||||
// successors,
|
||||
// successors,
|
||||
// |n| goal.success(n),
|
||||
// );
|
||||
|
||||
let start_time = std::time::Instant::now();
|
||||
let p = a_star(
|
||||
start,
|
||||
end,
|
||||
|n| goal.heuristic(n),
|
||||
successors,
|
||||
successors,
|
||||
|n| goal.success(n),
|
||||
);
|
||||
|
||||
let start_time = std::time::Instant::now();
|
||||
let p = pf.find_path();
|
||||
let end_time = std::time::Instant::now();
|
||||
debug!("path: {p:?}");
|
||||
debug!("time: {:?}", end_time - start_time);
|
||||
|
|
|
@ -1,454 +0,0 @@
|
|||
//! An implementation of Moving Target D* Lite as described in
|
||||
//! <http://idm-lab.org/bib/abstracts/papers/aamas10a.pdf>
|
||||
//!
|
||||
//! Future optimization attempt ideas:
|
||||
//! - Use a different priority queue (e.g. fibonacci heap)
|
||||
//! - Use `FxHash` instead of the default hasher
|
||||
//! - Have `par` be a raw pointer
|
||||
//! - Try borrowing vs copying the Node in several places (like `state_mut`)
|
||||
//! - Store edge costs in their own map
|
||||
|
||||
use priority_queue::DoublePriorityQueue;
|
||||
use std::{collections::HashMap, fmt::Debug, hash::Hash, ops::Add};
|
||||
|
||||
/// Nodes are coordinates.
|
||||
pub struct MTDStarLite<
|
||||
N: Eq + Hash + Copy + Debug,
|
||||
W: PartialOrd + Default + Copy + num_traits::Bounded + Debug,
|
||||
HeuristicFn: Fn(&N) -> W,
|
||||
SuccessorsFn: Fn(&N) -> Vec<Edge<N, W>>,
|
||||
PredecessorsFn: Fn(&N) -> Vec<Edge<N, W>>,
|
||||
SuccessFn: Fn(&N) -> bool,
|
||||
> {
|
||||
/// Returns a rough estimate of how close we are to the goal. Lower =
|
||||
/// closer.
|
||||
pub heuristic: HeuristicFn,
|
||||
/// Returns the nodes that can be reached from the given node.
|
||||
pub successors: SuccessorsFn,
|
||||
/// Returns the nodes that would direct us to the given node. If the graph
|
||||
/// isn't directed (i.e. you can always return to the previous node), this
|
||||
/// can be the same as `successors`.
|
||||
pub predecessors: PredecessorsFn,
|
||||
/// Returns true if the given node is at the goal.
|
||||
/// A simple implementation is to check if the given node is equal to the
|
||||
/// goal.
|
||||
pub success: SuccessFn,
|
||||
|
||||
start: N,
|
||||
goal: N,
|
||||
|
||||
old_start: N,
|
||||
old_goal: N,
|
||||
|
||||
k_m: W,
|
||||
open: DoublePriorityQueue<N, Priority<W>>,
|
||||
node_states: HashMap<N, NodeState<N, W>>,
|
||||
updated_edge_costs: Vec<ChangedEdge<N, W>>,
|
||||
|
||||
/// This only exists so it can be referenced by `state()` when there's no
|
||||
/// state.
|
||||
default_state: NodeState<N, W>,
|
||||
}
|
||||
|
||||
impl<
|
||||
N: Eq + Hash + Copy + Debug,
|
||||
W: PartialOrd + Add<Output = W> + Default + Copy + num_traits::Bounded + Debug,
|
||||
HeuristicFn: Fn(&N) -> W,
|
||||
SuccessorsFn: Fn(&N) -> Vec<Edge<N, W>>,
|
||||
PredecessorsFn: Fn(&N) -> Vec<Edge<N, W>>,
|
||||
SuccessFn: Fn(&N) -> bool,
|
||||
> MTDStarLite<N, W, HeuristicFn, SuccessorsFn, PredecessorsFn, SuccessFn>
|
||||
{
|
||||
fn calculate_key(&self, n: &N) -> Priority<W> {
|
||||
let s = self.state(n);
|
||||
let min_score = if s.g < s.rhs { s.g } else { s.rhs };
|
||||
Priority(
|
||||
if min_score == W::max_value() {
|
||||
min_score
|
||||
} else {
|
||||
min_score + (self.heuristic)(n) + self.k_m
|
||||
},
|
||||
min_score,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
start: N,
|
||||
goal: N,
|
||||
heuristic: HeuristicFn,
|
||||
successors: SuccessorsFn,
|
||||
predecessors: PredecessorsFn,
|
||||
success: SuccessFn,
|
||||
) -> Self {
|
||||
let open = DoublePriorityQueue::default();
|
||||
let k_m = W::default();
|
||||
|
||||
let known_nodes = vec![start, goal];
|
||||
|
||||
let mut pf = MTDStarLite {
|
||||
heuristic,
|
||||
successors,
|
||||
predecessors,
|
||||
success,
|
||||
|
||||
start,
|
||||
goal,
|
||||
|
||||
old_start: start,
|
||||
old_goal: goal,
|
||||
|
||||
k_m,
|
||||
open,
|
||||
node_states: HashMap::new(),
|
||||
updated_edge_costs: Vec::new(),
|
||||
|
||||
default_state: NodeState::default(),
|
||||
};
|
||||
|
||||
for n in &known_nodes {
|
||||
*pf.state_mut(n) = NodeState::default();
|
||||
}
|
||||
pf.state_mut(&start).rhs = W::default();
|
||||
pf.open.push(start, pf.calculate_key(&start));
|
||||
|
||||
pf
|
||||
}
|
||||
|
||||
fn update_state(&mut self, n: &N) {
|
||||
let u = self.state_mut(n);
|
||||
if u.g != u.rhs {
|
||||
if self.open.get(n).is_some() {
|
||||
self.open.change_priority(n, self.calculate_key(n));
|
||||
} else {
|
||||
self.open.push(*n, self.calculate_key(n));
|
||||
}
|
||||
} else if self.open.get(n).is_some() {
|
||||
self.open.remove(n);
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_cost_minimal_path(&mut self) {
|
||||
while {
|
||||
if let Some((_, top_key)) = self.open.peek_min() {
|
||||
(top_key < &self.calculate_key(&self.goal)) || {
|
||||
let goal_state = self.state(&self.goal);
|
||||
goal_state.rhs > goal_state.g
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} {
|
||||
let (u_node, k_old) = self.open.pop_min().unwrap();
|
||||
let k_new = self.calculate_key(&u_node);
|
||||
if k_old < k_new {
|
||||
self.open.change_priority(&u_node, k_new);
|
||||
continue;
|
||||
}
|
||||
let u = self.state_mut(&u_node);
|
||||
if u.g > u.rhs {
|
||||
u.g = u.rhs;
|
||||
self.open.remove(&u_node);
|
||||
for edge in (self.successors)(&u_node) {
|
||||
let s_node = edge.target;
|
||||
let s = self.state(&s_node);
|
||||
let u = self.state(&u_node);
|
||||
if s_node != self.start && (s.rhs > u.g + edge.cost) {
|
||||
let s_rhs = u.g + edge.cost;
|
||||
let s = self.state_mut(&s_node);
|
||||
s.par = Some(u_node);
|
||||
s.rhs = s_rhs;
|
||||
self.update_state(&s_node);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
u.g = W::max_value();
|
||||
let u_edge = Edge {
|
||||
target: u_node,
|
||||
cost: W::default(),
|
||||
};
|
||||
for edge in (self.successors)(&u_node)
|
||||
.iter()
|
||||
.chain([&u_edge].into_iter())
|
||||
{
|
||||
let s_node = edge.target;
|
||||
let s = self.state(&s_node);
|
||||
if s_node != self.start && s.par == Some(u_node) {
|
||||
let mut min_pred = u_node;
|
||||
let mut min_score = W::max_value();
|
||||
|
||||
for edge in (self.predecessors)(&s_node) {
|
||||
let s = self.state(&edge.target);
|
||||
let score = s.g + edge.cost;
|
||||
if score < min_score {
|
||||
min_score = score;
|
||||
min_pred = edge.target;
|
||||
}
|
||||
}
|
||||
|
||||
let s = self.state_mut(&s_node);
|
||||
s.rhs = min_score;
|
||||
if s.rhs == W::max_value() {
|
||||
s.par = None;
|
||||
} else {
|
||||
s.par = Some(min_pred);
|
||||
}
|
||||
}
|
||||
self.update_state(&s_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_path(&mut self) -> Option<Vec<N>> {
|
||||
if (self.success)(&self.start) {
|
||||
return None;
|
||||
}
|
||||
|
||||
//
|
||||
self.k_m = self.k_m + (self.heuristic)(&self.old_goal);
|
||||
|
||||
if self.old_start != self.start {
|
||||
self.optimized_deletion();
|
||||
}
|
||||
|
||||
while let Some(edge) = self.updated_edge_costs.pop() {
|
||||
let (u_node, v_node) = (edge.predecessor, edge.successor);
|
||||
// update the edge cost c(u, v);
|
||||
if edge.old_cost > edge.cost {
|
||||
let u_g = self.state(&u_node).g;
|
||||
if v_node != self.start && self.state(&v_node).rhs > u_g + edge.cost {
|
||||
let v = self.state_mut(&v_node);
|
||||
v.par = Some(u_node);
|
||||
v.rhs = u_g + edge.cost;
|
||||
}
|
||||
} else if v_node != self.start && self.state(&v_node).par == Some(u_node) {
|
||||
let mut min_pred = u_node;
|
||||
let mut min_score = W::max_value();
|
||||
|
||||
for edge in (self.predecessors)(&v_node) {
|
||||
let s = self.state(&edge.target);
|
||||
let score = s.g + edge.cost;
|
||||
if score < min_score {
|
||||
min_score = score;
|
||||
min_pred = edge.target;
|
||||
}
|
||||
}
|
||||
|
||||
let v = self.state_mut(&v_node);
|
||||
v.rhs = min_score;
|
||||
if v.rhs == W::max_value() {
|
||||
v.par = None;
|
||||
} else {
|
||||
v.par = Some(min_pred);
|
||||
}
|
||||
self.update_state(&v_node);
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
self.old_start = self.start;
|
||||
self.old_goal = self.goal;
|
||||
|
||||
self.compute_cost_minimal_path();
|
||||
if self.state(&self.goal).rhs == W::max_value() {
|
||||
// no path exists
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut reverse_path = vec![self.goal];
|
||||
|
||||
// identify a path from sstart to sgoal using the parent pointers
|
||||
let mut target = self.state(&self.goal).par;
|
||||
while !(Some(self.start) == target) {
|
||||
let Some(this_target) = target else {
|
||||
break;
|
||||
};
|
||||
// hunter follows path from start to goal;
|
||||
reverse_path.push(this_target);
|
||||
target = self.state(&this_target).par;
|
||||
}
|
||||
|
||||
// if hunter caught target {
|
||||
// return None;
|
||||
// }
|
||||
|
||||
let path: Vec<N> = reverse_path.into_iter().rev().collect();
|
||||
|
||||
Some(path)
|
||||
}
|
||||
|
||||
fn optimized_deletion(&mut self) {
|
||||
let start = self.start;
|
||||
self.state_mut(&start).par = None;
|
||||
|
||||
let mut min_pred = self.old_start;
|
||||
let mut min_score = W::max_value();
|
||||
|
||||
for edge in (self.predecessors)(&self.old_start) {
|
||||
let s = self.state(&edge.target);
|
||||
let score = s.g + edge.cost;
|
||||
if score < min_score {
|
||||
min_score = score;
|
||||
min_pred = edge.target;
|
||||
}
|
||||
}
|
||||
|
||||
let old_start = self.old_start;
|
||||
let s = self.state_mut(&old_start);
|
||||
s.rhs = min_score;
|
||||
if s.rhs == W::max_value() {
|
||||
s.par = None;
|
||||
} else {
|
||||
s.par = Some(min_pred);
|
||||
}
|
||||
self.update_state(&old_start);
|
||||
}
|
||||
|
||||
fn state(&self, n: &N) -> &NodeState<N, W> {
|
||||
self.node_states.get(n).unwrap_or(&self.default_state)
|
||||
}
|
||||
|
||||
fn state_mut(&mut self, n: &N) -> &mut NodeState<N, W> {
|
||||
self.node_states.entry(*n).or_default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Priority<W>(W, W)
|
||||
where
|
||||
W: PartialOrd + Debug;
|
||||
|
||||
impl<W: PartialOrd + Debug> PartialOrd for Priority<W> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
if self.0 < other.0 {
|
||||
Some(std::cmp::Ordering::Less)
|
||||
} else if self.0 > other.0 {
|
||||
Some(std::cmp::Ordering::Greater)
|
||||
} else if self.1 < other.1 {
|
||||
Some(std::cmp::Ordering::Less)
|
||||
} else if self.1 > other.1 {
|
||||
Some(std::cmp::Ordering::Greater)
|
||||
} else {
|
||||
Some(std::cmp::Ordering::Equal)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<W: PartialOrd + Debug> Ord for Priority<W> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.partial_cmp(other)
|
||||
.expect("Partial compare should not fail for Priority")
|
||||
}
|
||||
}
|
||||
impl<W: PartialOrd + Debug> Eq for Priority<W> {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NodeState<N: Eq + Hash + Copy + Debug, W: Default + num_traits::Bounded + Debug> {
|
||||
pub g: W,
|
||||
pub rhs: W,
|
||||
// future possible optimization: try making this a pointer
|
||||
pub par: Option<N>,
|
||||
}
|
||||
|
||||
impl<N: Eq + Hash + Copy + Debug, W: Default + num_traits::Bounded + Debug> Default
|
||||
for NodeState<N, W>
|
||||
{
|
||||
fn default() -> Self {
|
||||
NodeState {
|
||||
g: W::max_value(),
|
||||
rhs: W::max_value(),
|
||||
par: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Edge<N: Eq + Hash + Copy, W: PartialOrd + Copy> {
|
||||
pub target: N,
|
||||
pub cost: W,
|
||||
}
|
||||
|
||||
pub struct ChangedEdge<N: Eq + Hash + Clone, W: PartialOrd + Copy> {
|
||||
pub predecessor: N,
|
||||
pub successor: N,
|
||||
pub old_cost: W,
|
||||
pub cost: W,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_mtdstarlite() {
|
||||
let maze = [
|
||||
[0, 1, 0, 0, 0],
|
||||
[0, 1, 0, 1, 0],
|
||||
[0, 0, 0, 1, 0],
|
||||
[0, 1, 0, 1, 0],
|
||||
[0, 0, 1, 0, 0],
|
||||
];
|
||||
let width = maze[0].len();
|
||||
let height = maze.len();
|
||||
|
||||
let goal = (4, 4);
|
||||
|
||||
let heuristic = |n: &(usize, usize)| -> usize {
|
||||
((n.0 as isize - goal.0 as isize).abs() + (n.1 as isize - goal.1 as isize).abs())
|
||||
as usize
|
||||
};
|
||||
let successors = |n: &(usize, usize)| -> Vec<Edge<(usize, usize), usize>> {
|
||||
let mut successors = Vec::with_capacity(4);
|
||||
let (x, y) = *n;
|
||||
|
||||
if x > 0 && maze[y][x - 1] == 0 {
|
||||
successors.push(Edge {
|
||||
target: ((x - 1, y)),
|
||||
cost: 1,
|
||||
});
|
||||
}
|
||||
if x < width - 1 && maze[y][x + 1] == 0 {
|
||||
successors.push(Edge {
|
||||
target: ((x + 1, y)),
|
||||
cost: 1,
|
||||
});
|
||||
}
|
||||
if y > 0 && maze[y - 1][x] == 0 {
|
||||
successors.push(Edge {
|
||||
target: ((x, y - 1)),
|
||||
cost: 1,
|
||||
});
|
||||
}
|
||||
if y < height - 1 && maze[y + 1][x] == 0 {
|
||||
successors.push(Edge {
|
||||
target: ((x, y + 1)),
|
||||
cost: 1,
|
||||
});
|
||||
}
|
||||
|
||||
successors
|
||||
};
|
||||
let predecessors =
|
||||
|n: &(usize, usize)| -> Vec<Edge<(usize, usize), usize>> { successors(n) };
|
||||
|
||||
let mut pf = MTDStarLite::new((0, 0), goal, heuristic, successors, predecessors, |n| {
|
||||
n == &goal
|
||||
});
|
||||
let path = pf.find_path().unwrap();
|
||||
assert_eq!(
|
||||
path,
|
||||
vec![
|
||||
(0, 1),
|
||||
(0, 2),
|
||||
(1, 2),
|
||||
(2, 2),
|
||||
(2, 1),
|
||||
(2, 0),
|
||||
(3, 0),
|
||||
(4, 0),
|
||||
(4, 1),
|
||||
(4, 2),
|
||||
(4, 3),
|
||||
(4, 4),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue