improve pathfinder heuristics

This commit is contained in:
mat 2023-10-04 22:54:07 -05:00
parent 9db542f342
commit 074bb61983
4 changed files with 49 additions and 11 deletions

View file

@ -259,6 +259,7 @@ impl ChunkSectionPos {
block >> 4
}
}
/// The coordinates of a block inside a chunk.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ChunkBlockPos {

View file

@ -6,7 +6,7 @@ use std::{
time::{Duration, Instant},
};
use log::{trace, warn};
use log::{debug, trace, warn};
use priority_queue::PriorityQueue;
pub struct Path<P, M>
@ -55,8 +55,12 @@ where
let mut best_paths: [P; 7] = [start; 7];
let mut best_path_scores: [f32; 7] = [heuristic(start); 7];
let mut num_nodes = 0;
while let Some((current_node, _)) = open_set.pop() {
num_nodes += 1;
if success(current_node) {
debug!("Nodes considered: {num_nodes}");
return Path {
movements: reconstruct_path(nodes, current_node),
partial: false,
@ -99,7 +103,8 @@ where
}
}
if start_time.elapsed() > timeout {
// check for timeout every ~1ms
if num_nodes % 1000 == 0 && start_time.elapsed() > timeout {
// timeout, just return the best path we have so far
trace!("A* couldn't find a path in time, returning best path");
break;

View file

@ -1,6 +1,11 @@
use std::f32::consts::SQRT_2;
use azalea_core::position::{BlockPos, Vec3};
use super::Goal;
use super::{
costs::{FALL_ONE_BLOCK_COST, JUMP_ONE_BLOCK_COST},
Goal,
};
pub struct BlockPosGoal(pub BlockPos);
impl Goal for BlockPosGoal {
@ -8,13 +13,32 @@ impl Goal for BlockPosGoal {
let dx = (self.0.x - n.x) as f32;
let dy = (self.0.y - n.y) as f32;
let dz = (self.0.z - n.z) as f32;
dx * dx + dy * dy + dz * dz
xz_heuristic(dx, dz) + y_heuristic(dy)
}
fn success(&self, n: BlockPos) -> bool {
n == self.0
}
}
fn xz_heuristic(dx: f32, dz: f32) -> f32 {
let x = dx.abs();
let z = dz.abs();
let diagonal;
let straight;
if x < z {
straight = z - x;
diagonal = x;
} else {
straight = x - z;
diagonal = z;
}
diagonal * SQRT_2 + straight
}
pub struct XZGoal {
pub x: i32,
pub z: i32,
@ -23,20 +47,28 @@ impl Goal for XZGoal {
fn heuristic(&self, n: BlockPos) -> f32 {
let dx = (self.x - n.x) as f32;
let dz = (self.z - n.z) as f32;
dx * dx + dz * dz
xz_heuristic(dx, dz)
}
fn success(&self, n: BlockPos) -> bool {
n.x == self.x && n.z == self.z
}
}
fn y_heuristic(dy: f32) -> f32 {
if dy < 0.0 {
FALL_ONE_BLOCK_COST * -dy
} else {
*JUMP_ONE_BLOCK_COST * dy
}
}
pub struct YGoal {
pub y: i32,
}
impl Goal for YGoal {
fn heuristic(&self, n: BlockPos) -> f32 {
let dy = (self.y - n.y) as f32;
dy * dy
y_heuristic(dy)
}
fn success(&self, n: BlockPos) -> bool {
n.y == self.y

View file

@ -25,6 +25,11 @@ type Edge = astar::Edge<BlockPos, MoveData>;
pub type SuccessorsFn = fn(&mut Vec<Edge>, &PathfinderCtx, BlockPos);
pub fn default_move(edges: &mut Vec<Edge>, ctx: &PathfinderCtx, node: BlockPos) {
basic::basic_move(edges, ctx, node);
parkour::parkour_move(edges, ctx, node);
}
#[derive(Clone)]
pub struct MoveData {
/// Use the context to determine what events should be sent to complete this
@ -272,11 +277,6 @@ pub struct IsReachedCtx<'a> {
pub physics: &'a azalea_entity::Physics,
}
pub fn default_move(edges: &mut Vec<Edge>, ctx: &PathfinderCtx, node: BlockPos) {
basic::basic_move(edges, ctx, node);
parkour::parkour_move(edges, ctx, node);
}
/// Returns whether the entity is at the node and should start going to the
/// next node.
#[must_use]