diff options
Diffstat (limited to 'azalea')
| -rw-r--r-- | azalea/src/pathfinder/astar/heap.rs | 108 | ||||
| -rw-r--r-- | azalea/src/pathfinder/astar/mod.rs (renamed from azalea/src/pathfinder/astar.rs) | 108 | ||||
| -rw-r--r-- | azalea/src/pathfinder/goals.rs | 5 | ||||
| -rw-r--r-- | azalea/src/pathfinder/world.rs | 9 |
4 files changed, 117 insertions, 113 deletions
diff --git a/azalea/src/pathfinder/astar/heap.rs b/azalea/src/pathfinder/astar/heap.rs new file mode 100644 index 00000000..3b20c0d2 --- /dev/null +++ b/azalea/src/pathfinder/astar/heap.rs @@ -0,0 +1,108 @@ +use std::{ + cmp::{self, Reverse}, + collections::BinaryHeap, +}; + +use radix_heap::RadixHeapMap; + +#[derive(Default)] +pub struct PathfinderHeap { + /// Key is f_score.to_bits(), value is (g_score, index) + /// + /// As long as the f_score is positive, comparing it as bits is fine. Also, + /// it has to be `Reverse`d to make it a min-heap. + radix_heap: RadixHeapMap<Reverse<u32>, (f32, u32)>, + // fallback + binary_heap: BinaryHeap<WeightedNode>, +} +impl PathfinderHeap { + pub fn new() -> Self { + Self::default() + } + + pub fn push(&mut self, item: WeightedNode) { + if let Some(top) = self.radix_heap.top() { + // this can happen when the heuristic wasn't an underestimate, so just fall back + // to a binary heap in those cases + if item.f_score < f32::from_bits(top.0) { + self.binary_heap.push(item); + return; + } + } + self.radix_heap + .push(Reverse(item.f_score.to_bits()), (item.g_score, item.index)) + } + pub fn pop(&mut self) -> Option<WeightedNode> { + self.binary_heap.pop().or_else(|| { + self.radix_heap + .pop() + .map(|(f_score, (g_score, index))| WeightedNode { + f_score: f32::from_bits(f_score.0), + g_score, + index, + }) + }) + } +} + +#[derive(PartialEq, Debug)] +#[repr(C)] +pub struct WeightedNode { + /// Sum of the g_score and heuristic + pub f_score: f32, + /// The actual cost to get to this node + pub g_score: f32, + pub index: u32, +} + +impl Ord for WeightedNode { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + // intentionally inverted to make the BinaryHeap a min-heap + match other.f_score.total_cmp(&self.f_score) { + cmp::Ordering::Equal => self.g_score.total_cmp(&other.g_score), + s => s, + } + } +} +impl Eq for WeightedNode {} +impl PartialOrd for WeightedNode { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { + Some(self.cmp(other)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn weighted_node(f: f32, g: f32) -> WeightedNode { + WeightedNode { + f_score: f, + g_score: g, + index: 0, + } + } + + #[test] + fn test_weighted_node_eq() { + let a = weighted_node(0., 0.); + let b = weighted_node(0., 0.); + assert!(a == b); + } + #[test] + fn test_weighted_node_le() { + let a = weighted_node(1., 0.); + let b = weighted_node(0., 0.); + assert_eq!(a.cmp(&b), cmp::Ordering::Less); + assert!(a.le(&b)); + } + #[test] + fn test_weighted_node_le_g() { + let a = weighted_node(0., 1.); + let b = weighted_node(0., 0.); + assert_eq!(a.cmp(&b), cmp::Ordering::Greater); + assert!(!a.le(&b)); + } +} diff --git a/azalea/src/pathfinder/astar.rs b/azalea/src/pathfinder/astar/mod.rs index 8eecd016..484ab572 100644 --- a/azalea/src/pathfinder/astar.rs +++ b/azalea/src/pathfinder/astar/mod.rs @@ -1,6 +1,6 @@ +pub mod heap; + use std::{ - cmp::{self, Reverse}, - collections::BinaryHeap, fmt::{self, Debug}, hash::{BuildHasherDefault, Hash}, time::{Duration, Instant}, @@ -8,10 +8,11 @@ use std::{ use indexmap::IndexMap; use num_format::ToFormattedString; -use radix_heap::RadixHeapMap; use rustc_hash::FxHasher; use tracing::{debug, trace, warn}; +use crate::pathfinder::astar::heap::{PathfinderHeap, WeightedNode}; + pub struct Path<P, M> where P: Eq + Hash + Copy + Debug, @@ -287,73 +288,6 @@ impl<P: Hash + Copy + Clone, M: Clone> Clone for Movement<P, M> { } } -#[derive(Default)] -struct PathfinderHeap { - binary_heap: BinaryHeap<WeightedNode>, - /// Key is f_score.to_bits(), value is (g_score, index) - /// - /// As long as the f_score is positive, comparing it as bits is fine. Also, - /// it has to be `Reverse`d to make it a min-heap. - radix_heap: RadixHeapMap<Reverse<u32>, (f32, u32)>, -} -impl PathfinderHeap { - pub fn new() -> Self { - Self::default() - } - - pub fn push(&mut self, item: WeightedNode) { - if let Some(top) = self.radix_heap.top() { - // this can happen when the heuristic wasn't an underestimate, so just fall back - // to a binary heap in those cases - if item.f_score < f32::from_bits(top.0) { - self.binary_heap.push(item); - return; - } - } - self.radix_heap - .push(Reverse(item.f_score.to_bits()), (item.g_score, item.index)) - } - pub fn pop(&mut self) -> Option<WeightedNode> { - self.binary_heap.pop().or_else(|| { - self.radix_heap - .pop() - .map(|(f_score, (g_score, index))| WeightedNode { - f_score: f32::from_bits(f_score.0), - g_score, - index, - }) - }) - } -} - -#[derive(PartialEq, Debug)] -#[repr(C)] -pub struct WeightedNode { - /// Sum of the g_score and heuristic - pub f_score: f32, - /// The actual cost to get to this node - pub g_score: f32, - pub index: u32, -} - -impl Ord for WeightedNode { - #[inline] - fn cmp(&self, other: &Self) -> cmp::Ordering { - // intentionally inverted to make the BinaryHeap a min-heap - match other.f_score.total_cmp(&self.f_score) { - cmp::Ordering::Equal => self.g_score.total_cmp(&other.g_score), - s => s, - } - } -} -impl Eq for WeightedNode {} -impl PartialOrd for WeightedNode { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { - Some(self.cmp(other)) - } -} - /// A timeout that the pathfinder will consider when calculating a path. /// /// See [`PathfinderOpts::min_timeout`] and [`PathfinderOpts::max_timeout`] if @@ -379,37 +313,3 @@ impl Default for PathfinderTimeout { Self::Time(Duration::from_secs(1)) } } - -#[cfg(test)] -mod tests { - use super::*; - - fn weighted_node(f: f32, g: f32) -> WeightedNode { - WeightedNode { - f_score: f, - g_score: g, - index: 0, - } - } - - #[test] - fn test_weighted_node_eq() { - let a = weighted_node(0., 0.); - let b = weighted_node(0., 0.); - assert!(a == b); - } - #[test] - fn test_weighted_node_le() { - let a = weighted_node(1., 0.); - let b = weighted_node(0., 0.); - assert_eq!(a.cmp(&b), cmp::Ordering::Less); - assert!(a.le(&b)); - } - #[test] - fn test_weighted_node_le_g() { - let a = weighted_node(0., 1.); - let b = weighted_node(0., 0.); - assert_eq!(a.cmp(&b), cmp::Ordering::Greater); - assert!(!a.le(&b)); - } -} diff --git a/azalea/src/pathfinder/goals.rs b/azalea/src/pathfinder/goals.rs index 26915b2f..25db9c8e 100644 --- a/azalea/src/pathfinder/goals.rs +++ b/azalea/src/pathfinder/goals.rs @@ -98,6 +98,7 @@ impl From<BlockPos> for XZGoal { fn y_heuristic(dy: f32) -> f32 { if dy > 0. { if BARITONE_COMPAT { + // this is wrong because it can be an overestimate return *JUMP_ONE_BLOCK_COST * dy; } @@ -107,8 +108,8 @@ fn y_heuristic(dy: f32) -> f32 { (f32::max(*JUMP_ONE_BLOCK_COST, WALK_ONE_BLOCK_COST) + JUMP_PENALTY - SPRINT_ONE_BLOCK_COST) * dy } else if dy < 0. { - // this is an overestimate (copied from baritone), but fixing it makes perf - // worse + // this is also an overestimate (copied from baritone), but fixing it makes perf + // too much worse (FALL_N_BLOCKS_COST[2] / 2.) * -dy } else { 0. diff --git a/azalea/src/pathfinder/world.rs b/azalea/src/pathfinder/world.rs index ec2dbe80..9970de8d 100644 --- a/azalea/src/pathfinder/world.rs +++ b/azalea/src/pathfinder/world.rs @@ -29,12 +29,7 @@ pub struct CachedWorld { // we store `PalettedContainer`s instead of `Chunk`s or `Section`s because it doesn't contain // any unnecessary data like heightmaps or biomes. - cached_chunks: RefCell< - Vec<( - ChunkPos, - Vec<azalea_world::palette::PalettedContainer<BlockState>>, - )>, - >, + cached_chunks: RefCell<Vec<(ChunkPos, Box<[PalettedContainer<BlockState>]>)>>, last_chunk_cache_index: RefCell<Option<usize>>, cached_blocks: UnsafeCell<CachedSections>, @@ -194,7 +189,7 @@ impl CachedWorld { .sections .iter() .map(|section| section.states.clone()) - .collect::<Vec<PalettedContainer<BlockState>>>(); + .collect::<Box<[PalettedContainer<BlockState>]>>(); if section_index >= sections.len() { // y position is out of bounds |
