mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
remove normal d* lite
This commit is contained in:
parent
eb4ea3aecc
commit
e1254c32b6
5 changed files with 161 additions and 514 deletions
120
Cargo.lock
generated
120
Cargo.lock
generated
|
@ -116,7 +116,7 @@ dependencies = [
|
|||
name = "azalea-auth"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-buf 0.1.0",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
@ -125,7 +125,7 @@ name = "azalea-block"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"azalea-block-macros",
|
||||
"azalea-buf",
|
||||
"azalea-buf 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -145,7 +145,21 @@ version = "0.1.0"
|
|||
name = "azalea-buf"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"azalea-buf-macros",
|
||||
"azalea-buf-macros 0.1.0",
|
||||
"byteorder",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "azalea-buf"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7b2b996a5642abaac7de05a9a1416c5fbaad28bdfe12d446f71a5f7a3075aa7"
|
||||
dependencies = [
|
||||
"azalea-buf-macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
|
@ -162,12 +176,36 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "azalea-buf-macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5d949019583d78ab38ca00305bdf7107a94a12496e5af2df45ebc6994a7fddd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "azalea-chat"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-language",
|
||||
"azalea-buf 0.1.0",
|
||||
"azalea-language 0.1.0",
|
||||
"lazy_static",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "azalea-chat"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ad571c2c007c414fdbc59becf49008ab28a745db55908169dcff3856cf6a04a"
|
||||
dependencies = [
|
||||
"azalea-buf 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"azalea-language 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -180,8 +218,8 @@ dependencies = [
|
|||
"anyhow",
|
||||
"azalea-auth",
|
||||
"azalea-block",
|
||||
"azalea-chat",
|
||||
"azalea-core",
|
||||
"azalea-chat 0.1.1",
|
||||
"azalea-core 0.1.0",
|
||||
"azalea-crypto",
|
||||
"azalea-physics",
|
||||
"azalea-protocol",
|
||||
|
@ -197,9 +235,21 @@ dependencies = [
|
|||
name = "azalea-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-chat",
|
||||
"azalea-nbt",
|
||||
"azalea-buf 0.1.0",
|
||||
"azalea-chat 0.1.1",
|
||||
"azalea-nbt 0.1.0",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "azalea-core"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e49ec44a7bc0627d66e417b645b35d58c42313cdf827dee98981fc4f99e3fc97"
|
||||
dependencies = [
|
||||
"azalea-buf 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"azalea-chat 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"azalea-nbt 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
@ -208,7 +258,7 @@ name = "azalea-crypto"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"azalea-buf",
|
||||
"azalea-buf 0.1.0",
|
||||
"cfb8",
|
||||
"criterion",
|
||||
"num-bigint",
|
||||
|
@ -227,12 +277,23 @@ dependencies = [
|
|||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "azalea-language"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e54337d15218c60db4bfcf7e0af1247f3dfabccb908fc75424656120380be78b"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "azalea-nbt"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"azalea-buf",
|
||||
"azalea-buf 0.1.0",
|
||||
"byteorder",
|
||||
"criterion",
|
||||
"flate2",
|
||||
|
@ -240,6 +301,20 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "azalea-nbt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bfb3dfcfac6014fdb75e95d36760985633300f8246e646979050f762f88f61a"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"azalea-buf 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder",
|
||||
"flate2",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "azalea-pathfinder"
|
||||
version = "0.1.0"
|
||||
|
@ -248,6 +323,7 @@ dependencies = [
|
|||
"async-trait",
|
||||
"azalea",
|
||||
"azalea-client",
|
||||
"azalea-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits",
|
||||
"priority-queue",
|
||||
]
|
||||
|
@ -257,7 +333,7 @@ name = "azalea-physics"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"azalea-block",
|
||||
"azalea-core",
|
||||
"azalea-core 0.1.0",
|
||||
"azalea-world",
|
||||
"lazy_static",
|
||||
"uuid",
|
||||
|
@ -272,11 +348,11 @@ dependencies = [
|
|||
"azalea-auth",
|
||||
"azalea-block",
|
||||
"azalea-brigadier",
|
||||
"azalea-buf",
|
||||
"azalea-chat",
|
||||
"azalea-core",
|
||||
"azalea-buf 0.1.0",
|
||||
"azalea-chat 0.1.1",
|
||||
"azalea-core 0.1.0",
|
||||
"azalea-crypto",
|
||||
"azalea-nbt",
|
||||
"azalea-nbt 0.1.0",
|
||||
"azalea-protocol-macros",
|
||||
"azalea-registry",
|
||||
"azalea-world",
|
||||
|
@ -308,7 +384,7 @@ dependencies = [
|
|||
name = "azalea-registry"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"azalea-buf 0.1.0",
|
||||
"azalea-registry-macros",
|
||||
]
|
||||
|
||||
|
@ -326,10 +402,10 @@ name = "azalea-world"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"azalea-block",
|
||||
"azalea-buf",
|
||||
"azalea-chat",
|
||||
"azalea-core",
|
||||
"azalea-nbt",
|
||||
"azalea-buf 0.1.0",
|
||||
"azalea-chat 0.1.1",
|
||||
"azalea-core 0.1.0",
|
||||
"azalea-nbt 0.1.0",
|
||||
"azalea-registry",
|
||||
"log",
|
||||
"nohash-hasher",
|
||||
|
|
|
@ -10,5 +10,6 @@ anyhow = "1.0.65"
|
|||
async-trait = "0.1.57"
|
||||
azalea = {version = "0.1", path = "../azalea"}
|
||||
azalea-client = { version = "0.1.0", path = "../azalea-client" }
|
||||
azalea-core = "0.1.0"
|
||||
num-traits = "0.2.15"
|
||||
priority-queue = "1.2.3"
|
||||
|
|
|
@ -1,418 +0,0 @@
|
|||
//! An implementation of D* Lite: second version (optimized version) as
|
||||
//! described in <https://www.cs.cmu.edu/~maxim/files/dlite_tro05.pdf>
|
||||
//!
|
||||
//! Future optimization attempt ideas:
|
||||
//! - Use a different priority queue (e.g. fibonacci heap)
|
||||
//! - Use FxHash instead of the default hasher
|
||||
//! - Have a `cost(a: Vertex, b: Vertex)` function instead of having the cost be stored in `Edge`
|
||||
|
||||
use priority_queue::PriorityQueue;
|
||||
use std::error::Error;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
cmp,
|
||||
collections::HashMap,
|
||||
hash::Hash,
|
||||
ops::{Add, Deref},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VertexScore<W: Default + num_traits::Bounded + Debug> {
|
||||
pub g: W,
|
||||
pub rhs: W,
|
||||
}
|
||||
|
||||
impl<W: Default + num_traits::Bounded + Debug> Default for VertexScore<W> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
g: W::max_value(),
|
||||
rhs: W::max_value(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The D* Lite pathfinding algorithm
|
||||
pub struct DStarLite<
|
||||
'a,
|
||||
N: Eq + Hash + Clone,
|
||||
W: PartialOrd + Eq + Default + Copy + num_traits::Bounded + Debug,
|
||||
HeuristicFn: Fn(&N, &N) -> W,
|
||||
SuccessorsFn: Fn(&N) -> Vec<EdgeTo<N, W>>,
|
||||
PredecessorsFn: Fn(&N) -> Vec<EdgeTo<N, W>>,
|
||||
> {
|
||||
/// Rough estimate of how close we are to the goal. Lower = closer.
|
||||
pub heuristic: HeuristicFn,
|
||||
/// Get the nodes that can be reached from the current one
|
||||
pub successors: SuccessorsFn,
|
||||
/// Get the nodes that would direct us to the current node
|
||||
pub predecessors: PredecessorsFn,
|
||||
|
||||
pub start: Cow<'a, N>,
|
||||
start_last: Cow<'a, N>,
|
||||
|
||||
goal: N,
|
||||
|
||||
queue: PriorityQueue<N, Priority<W>>,
|
||||
k_m: W,
|
||||
vertex_scores: HashMap<N, VertexScore<W>>,
|
||||
/// This is just here so we can reference it. It should never be modified.
|
||||
default_score: VertexScore<W>,
|
||||
|
||||
/// A list of edges and costs that we'll be updating next time.
|
||||
pub updated_edge_costs: Vec<(Edge<'a, N, W>, W)>,
|
||||
}
|
||||
|
||||
pub struct Edge<'a, N: Eq + Hash + Clone, W: PartialOrd + Copy> {
|
||||
pub predecessor: Cow<'a, N>,
|
||||
pub successor: Cow<'a, N>,
|
||||
pub cost: W,
|
||||
}
|
||||
|
||||
pub struct EdgeTo<N: Eq + Hash + Clone, W: PartialOrd + Copy> {
|
||||
pub target: N,
|
||||
pub cost: W,
|
||||
}
|
||||
|
||||
// rust does lexicographic ordering by default when we derive Ord
|
||||
#[derive(Eq, 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 + Eq> Ord for Priority<W> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.partial_cmp(other)
|
||||
.expect("Partial compare should not fail for Priority")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NoPathError;
|
||||
impl Error for NoPathError {}
|
||||
impl Display for NoPathError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "No path found")
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
'a,
|
||||
N: Eq + Hash + Clone + Debug,
|
||||
W: PartialOrd + Eq + Add<Output = W> + Default + Copy + num_traits::bounds::Bounded + Debug,
|
||||
HeuristicFn: Fn(&N, &N) -> W,
|
||||
SuccessorsFn: Fn(&N) -> Vec<EdgeTo<N, W>>,
|
||||
PredecessorsFn: Fn(&N) -> Vec<EdgeTo<N, W>>,
|
||||
> DStarLite<'a, N, W, HeuristicFn, SuccessorsFn, PredecessorsFn>
|
||||
{
|
||||
fn score(&self, node: &N) -> &VertexScore<W> {
|
||||
self.vertex_scores.get(node).unwrap_or(&self.default_score)
|
||||
}
|
||||
fn score_mut(&mut self, node: &N) -> &mut VertexScore<W> {
|
||||
self.vertex_scores.entry(node.clone()).or_default()
|
||||
}
|
||||
|
||||
fn calculate_key(&self, s: &N) -> Priority<W> {
|
||||
let s_score = self.score(s);
|
||||
let min_score = if s_score.g < s_score.rhs {
|
||||
s_score.g
|
||||
} else {
|
||||
s_score.rhs
|
||||
};
|
||||
Priority(
|
||||
if min_score == W::max_value() {
|
||||
min_score
|
||||
} else {
|
||||
min_score + (self.heuristic)(&self.start, s) + self.k_m
|
||||
},
|
||||
min_score,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
start: N,
|
||||
goal: N,
|
||||
heuristic: HeuristicFn,
|
||||
successors: SuccessorsFn,
|
||||
predecessors: PredecessorsFn,
|
||||
) -> Self {
|
||||
let mut queue = PriorityQueue::with_capacity(1);
|
||||
// Vertex<N, W>, Priority<W>
|
||||
|
||||
let mut vertex_scores = HashMap::new();
|
||||
vertex_scores.insert(
|
||||
goal.clone(),
|
||||
VertexScore {
|
||||
g: W::max_value(),
|
||||
rhs: W::default(),
|
||||
},
|
||||
);
|
||||
queue.push(
|
||||
goal.clone(),
|
||||
Priority(heuristic(&start, &goal), W::default()),
|
||||
);
|
||||
|
||||
let mut s = Self {
|
||||
start: Cow::Owned(start.clone()),
|
||||
start_last: Cow::Owned(start),
|
||||
|
||||
goal,
|
||||
|
||||
heuristic,
|
||||
successors,
|
||||
predecessors,
|
||||
default_score: VertexScore::default(),
|
||||
|
||||
queue,
|
||||
k_m: W::default(),
|
||||
vertex_scores,
|
||||
|
||||
updated_edge_costs: Vec::new(),
|
||||
};
|
||||
s.compute_shortest_path();
|
||||
s
|
||||
}
|
||||
|
||||
pub fn update_vertex(&mut self, u: &N) {
|
||||
let VertexScore { g, rhs } = self.score(u);
|
||||
// if(g(u)) != rhs(u) AND u is in U) U.Update(u, calculate_key(u))
|
||||
if g != rhs && self.queue.get(u).is_some() {
|
||||
self.queue.change_priority(u, self.calculate_key(u));
|
||||
} else if g != rhs && self.queue.get(u).is_none() {
|
||||
self.queue.push(u.clone(), self.calculate_key(u));
|
||||
} else if g == rhs && self.queue.get(u).is_some() {
|
||||
self.queue.remove(u);
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_shortest_path(&mut self) {
|
||||
while {
|
||||
let score = self.score(&self.start);
|
||||
if let Some(queue_top) = self.queue.peek() {
|
||||
(queue_top.1 < &self.calculate_key(&self.start)) || (score.rhs > score.g)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} {
|
||||
let (u, k_old) = self.queue.pop().unwrap();
|
||||
let k_new = self.calculate_key(&u);
|
||||
if k_old < k_new {
|
||||
self.queue.change_priority(&u, k_new);
|
||||
continue;
|
||||
}
|
||||
let u_score = self.score_mut(&u);
|
||||
if u_score.g > u_score.rhs {
|
||||
u_score.g = u_score.rhs;
|
||||
let g_u = u_score.g;
|
||||
self.queue.remove(&u);
|
||||
for s in (self.predecessors)(&u) {
|
||||
let target_score = self.score_mut(&s.target);
|
||||
if s.cost + g_u < target_score.rhs {
|
||||
target_score.rhs = s.cost + g_u;
|
||||
}
|
||||
// TODO: i think this can be moved up, but i'm not 100% sure it won't break anything
|
||||
self.update_vertex(&s.target);
|
||||
}
|
||||
} else {
|
||||
let g_old = u_score.g;
|
||||
u_score.g = W::max_value();
|
||||
for s in ((self.predecessors)(&u)).into_iter().chain(
|
||||
[EdgeTo {
|
||||
target: u,
|
||||
cost: W::default(),
|
||||
}]
|
||||
.into_iter(),
|
||||
) {
|
||||
if self.score(&s.target).rhs == s.cost + g_old && s.target != self.goal {
|
||||
let mut lowest_score = W::max_value();
|
||||
for s_prime in (self.successors)(&s.target) {
|
||||
let s_prime_score = s_prime.cost + self.score(&s_prime.target).g;
|
||||
if s_prime_score < lowest_score {
|
||||
lowest_score = s_prime_score;
|
||||
}
|
||||
}
|
||||
self.score_mut(&s.target).rhs = lowest_score;
|
||||
}
|
||||
self.update_vertex(&s.target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_from_updated_edges(&mut self) {
|
||||
self.k_m = self.k_m + (self.heuristic)(&self.start, &self.start_last);
|
||||
self.start_last = self.start.clone();
|
||||
|
||||
while let Some((mut edge, new_cost)) = self.updated_edge_costs.pop() {
|
||||
let old_cost = edge.cost;
|
||||
edge.cost = new_cost;
|
||||
let target_score = self.score_mut(&edge.successor);
|
||||
if old_cost > new_cost {
|
||||
if edge.cost + target_score.g < target_score.rhs {
|
||||
target_score.rhs = edge.cost + target_score.g;
|
||||
}
|
||||
} else if target_score.rhs == old_cost + target_score.g {
|
||||
let g_score = target_score.g;
|
||||
if edge.successor.deref() != &self.goal {
|
||||
let successors = (self.successors)(&edge.successor);
|
||||
let mut lowest_score = W::max_value();
|
||||
for s in successors {
|
||||
let score = s.cost + g_score;
|
||||
if score < lowest_score {
|
||||
lowest_score = score;
|
||||
}
|
||||
}
|
||||
self.score_mut(&edge.successor).rhs = lowest_score;
|
||||
}
|
||||
}
|
||||
self.update_vertex(&edge.successor);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the next vertex to visit and set our current position to be there.
|
||||
pub fn try_next(&mut self) -> Result<Option<&N>, NoPathError> {
|
||||
if self.start.deref() == &self.goal {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let start_score = self.score(&self.start);
|
||||
if start_score.rhs == W::max_value() {
|
||||
return Err(NoPathError);
|
||||
}
|
||||
|
||||
let get_score = |edge: &EdgeTo<N, W>| -> W {
|
||||
let g_score = self.score(&edge.target).g;
|
||||
if g_score == W::max_value() {
|
||||
W::max_value()
|
||||
} else {
|
||||
edge.cost + g_score
|
||||
}
|
||||
};
|
||||
|
||||
*self.start.to_mut() = (self.successors)(&self.start)
|
||||
.into_iter()
|
||||
.min_by(|a, b| get_score(a).partial_cmp(&get_score(b)).unwrap())
|
||||
.expect("No possible successors")
|
||||
.target;
|
||||
return Ok(Some(self.start.as_ref()));
|
||||
}
|
||||
|
||||
// /// Change our current position.
|
||||
// pub fn set_start(&mut self, s: Vertex<N, W>) {
|
||||
// *self.start.to_mut() = s;
|
||||
// }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_dstarlite() {
|
||||
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();
|
||||
|
||||
fn heuristic(a: &(usize, usize), b: &(usize, usize)) -> usize {
|
||||
((a.0 as isize - b.0 as isize).abs() + (a.1 as isize - b.1 as isize).abs()) as usize
|
||||
}
|
||||
let successors = |a: &(usize, usize)| -> Vec<EdgeTo<(usize, usize), usize>> {
|
||||
let mut successors = Vec::with_capacity(4);
|
||||
let (x, y) = *a;
|
||||
|
||||
if x > 0 && maze[y][x - 1] == 0 {
|
||||
successors.push(EdgeTo {
|
||||
target: ((x - 1, y)),
|
||||
cost: 1,
|
||||
});
|
||||
}
|
||||
if x < width - 1 && maze[y][x + 1] == 0 {
|
||||
successors.push(EdgeTo {
|
||||
target: ((x + 1, y)),
|
||||
cost: 1,
|
||||
});
|
||||
}
|
||||
if y > 0 && maze[y - 1][x] == 0 {
|
||||
successors.push(EdgeTo {
|
||||
target: ((x, y - 1)),
|
||||
cost: 1,
|
||||
});
|
||||
}
|
||||
if y < height - 1 && maze[y + 1][x] == 0 {
|
||||
successors.push(EdgeTo {
|
||||
target: ((x, y + 1)),
|
||||
cost: 1,
|
||||
});
|
||||
}
|
||||
|
||||
successors
|
||||
};
|
||||
let predecessors = |a: &(usize, usize)| -> Vec<EdgeTo<(usize, usize), usize>> {
|
||||
let mut predecessors = Vec::with_capacity(4);
|
||||
let (x, y) = *a;
|
||||
|
||||
if x > 0 && maze[y][x - 1] == 0 {
|
||||
predecessors.push(EdgeTo {
|
||||
target: ((x - 1, y)),
|
||||
cost: 1,
|
||||
});
|
||||
}
|
||||
if x < width - 1 && maze[y][x + 1] == 0 {
|
||||
predecessors.push(EdgeTo {
|
||||
target: ((x + 1, y)),
|
||||
cost: 1,
|
||||
});
|
||||
}
|
||||
if y > 0 && maze[y - 1][x] == 0 {
|
||||
predecessors.push(EdgeTo {
|
||||
target: ((x, y - 1)),
|
||||
cost: 1,
|
||||
});
|
||||
}
|
||||
if y < height - 1 && maze[y + 1][x] == 0 {
|
||||
predecessors.push(EdgeTo {
|
||||
target: ((x, y + 1)),
|
||||
cost: 1,
|
||||
});
|
||||
}
|
||||
|
||||
predecessors
|
||||
};
|
||||
|
||||
let mut dstar = DStarLite::new((0, 0), (4, 4), heuristic, successors, predecessors);
|
||||
assert!(dstar.try_next().unwrap() == Some(&(0, 1)));
|
||||
assert!(dstar.try_next().unwrap() == Some(&(0, 2)));
|
||||
assert!(dstar.try_next().unwrap() == Some(&(1, 2)));
|
||||
assert!(dstar.try_next().unwrap() == Some(&(2, 2)));
|
||||
assert!(dstar.try_next().unwrap() == Some(&(2, 1)));
|
||||
assert!(dstar.try_next().unwrap() == Some(&(2, 0)));
|
||||
assert!(dstar.try_next().unwrap() == Some(&(3, 0)));
|
||||
assert!(dstar.try_next().unwrap() == Some(&(4, 0)));
|
||||
assert!(dstar.try_next().unwrap() == Some(&(4, 1)));
|
||||
assert!(dstar.try_next().unwrap() == Some(&(4, 2)));
|
||||
assert!(dstar.try_next().unwrap() == Some(&(4, 3)));
|
||||
assert!(dstar.try_next().unwrap() == Some(&(4, 4)));
|
||||
assert!(dstar.try_next().unwrap() == None);
|
||||
}
|
||||
}
|
|
@ -1,45 +1,49 @@
|
|||
#![feature(let_chains)]
|
||||
|
||||
mod dstarlite;
|
||||
mod mtdstarlite;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use azalea::{Client, Event};
|
||||
pub use dstarlite::DStarLite;
|
||||
use azalea_core::BlockPos;
|
||||
pub use mtdstarlite::MTDStarLite;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
// #[derive(Default)]
|
||||
// pub struct Plugin {
|
||||
// pub state: Arc<Mutex<State>>,
|
||||
// }
|
||||
#[derive(Default)]
|
||||
pub struct Plugin {
|
||||
pub state: Arc<Mutex<State>>,
|
||||
}
|
||||
|
||||
// #[derive(Default)]
|
||||
// pub struct State {
|
||||
// // pathfinder: Option<DStarLite< Node, FloatOrd<f32>>>,
|
||||
// }
|
||||
#[derive(Default)]
|
||||
pub struct State {
|
||||
// pathfinder: Option<MTDStarLite<Node, f32>>,
|
||||
}
|
||||
|
||||
// #[async_trait]
|
||||
// impl azalea::Plugin for Plugin {
|
||||
// async fn handle(self: Arc<Self>, bot: Client, event: Arc<Event>) {
|
||||
// // match *
|
||||
// }
|
||||
// }
|
||||
#[async_trait]
|
||||
impl azalea::Plugin for Plugin {
|
||||
async fn handle(self: Arc<Self>, bot: Client, event: Arc<Event>) {
|
||||
// match *
|
||||
}
|
||||
}
|
||||
|
||||
// pub trait Trait {
|
||||
// fn goto(&self, goal: impl Goal);
|
||||
// }
|
||||
pub trait Trait {
|
||||
fn goto(&self, goal: impl Goal);
|
||||
}
|
||||
|
||||
// impl Trait for azalea_client::Client {
|
||||
// fn goto(&self, goal: impl Goal) {
|
||||
// let start = BlockPos::from(self.position());
|
||||
impl Trait for azalea_client::Client {
|
||||
fn goto(&self, goal: impl Goal) {
|
||||
// let start = Node {
|
||||
// pos: BlockPos::from(self.position()),
|
||||
// };
|
||||
|
||||
// let pf = DStarLite::new();
|
||||
// }
|
||||
// }
|
||||
// let pf = MTDStarLite::new(start);
|
||||
}
|
||||
}
|
||||
|
||||
// // fn heuristt
|
||||
pub struct Node {
|
||||
pub pos: BlockPos,
|
||||
}
|
||||
|
||||
// pub trait Goal {
|
||||
// fn heuristic(&self, x: i32, y: i32) -> f32;
|
||||
// }
|
||||
pub trait Goal {
|
||||
fn heuristic(&self, x: i32, y: i32, z: i32) -> f32;
|
||||
fn success(&self, x: i32, y: i32, z: i32) -> bool;
|
||||
}
|
||||
|
|
|
@ -9,12 +9,7 @@
|
|||
//! - Store edge costs in their own map
|
||||
|
||||
use priority_queue::DoublePriorityQueue;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt::Debug,
|
||||
hash::Hash,
|
||||
ops::{Add, Sub},
|
||||
};
|
||||
use std::{collections::HashMap, fmt::Debug, hash::Hash, ops::Add};
|
||||
|
||||
/// Nodes are coordinates.
|
||||
pub struct MTDStarLite<
|
||||
|
@ -34,12 +29,6 @@ pub struct MTDStarLite<
|
|||
start: N,
|
||||
goal: N,
|
||||
|
||||
// TODO: these are only used because the paper does it like this
|
||||
// we should get rid of these and only rely on `start` and `goal` in the
|
||||
// future
|
||||
pub new_start: N,
|
||||
pub new_goal: N,
|
||||
|
||||
old_start: N,
|
||||
old_goal: N,
|
||||
|
||||
|
@ -93,9 +82,6 @@ impl<
|
|||
start,
|
||||
goal,
|
||||
|
||||
new_start: start,
|
||||
new_goal: goal,
|
||||
|
||||
old_start: start,
|
||||
old_goal: goal,
|
||||
|
||||
|
@ -149,7 +135,6 @@ impl<
|
|||
let u = self.state_mut(&u_node);
|
||||
if u.g > u.rhs {
|
||||
u.g = u.rhs;
|
||||
let u = self.state(&u_node);
|
||||
self.open.remove(&u_node);
|
||||
for edge in (self.successors)(&u_node) {
|
||||
let s_node = edge.target;
|
||||
|
@ -207,34 +192,7 @@ impl<
|
|||
return None;
|
||||
}
|
||||
|
||||
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 {
|
||||
// hunter follows path from self.start to self.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();
|
||||
|
||||
self.start = self.new_start;
|
||||
self.goal = self.new_goal;
|
||||
|
||||
//
|
||||
self.k_m = self.k_m + (self.heuristic)(&self.goal, &self.old_goal);
|
||||
|
||||
if self.old_start != self.start {
|
||||
|
@ -274,6 +232,32 @@ impl<
|
|||
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 {
|
||||
// hunter follows path from self.start to self.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)
|
||||
}
|
||||
|
@ -378,7 +362,7 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_dstarlite() {
|
||||
fn test_mtdstarlite() {
|
||||
let maze = [
|
||||
[0, 1, 0, 0, 0],
|
||||
[0, 1, 0, 1, 0],
|
||||
|
|
Loading…
Add table
Reference in a new issue