Skip to content

Commit

Permalink
add pathfinder benchmark
Browse files Browse the repository at this point in the history
  • Loading branch information
mat-1 committed Oct 2, 2023
1 parent 37146f4 commit 4f6ab28
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 33 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions azalea-world/src/chunk_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ impl Chunk {
get_block_state_from_sections(&self.sections, pos, min_y)
}

#[must_use = "Use Chunk::set instead if you don't need the previous state"]
pub fn get_and_set(
&mut self,
pos: &ChunkBlockPos,
Expand Down
2 changes: 1 addition & 1 deletion azalea-world/src/palette.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ impl PalettedContainer {
}
}
Palette::Linear(palette) => {
if let Some(index) = palette.iter().position(|v| *v == value) {
if let Some(index) = palette.iter().position(|&v| v == value) {
return index;
}
let capacity = 2usize.pow(self.bits_per_entry.into());
Expand Down
8 changes: 8 additions & 0 deletions azalea/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,15 @@ bevy_log = "0.11.2"
azalea-entity = { version = "0.8.0", path = "../azalea-entity" }
bevy_time = "0.11.2"

[dev-dependencies]
criterion = "0.5.1"
rand = "0.8.5"

[features]
default = ["log"]
# enables bevy_log::LogPlugin by default
log = ["azalea-client/log"]

[[bench]]
name = "pathfinder"
harness = false
98 changes: 98 additions & 0 deletions azalea/benches/pathfinder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use std::{hint::black_box, time::Duration};

use azalea::{
pathfinder::{
astar::{self, a_star},
goals::BlockPosGoal,
Goal,
},
BlockPos,
};
use azalea_core::position::{ChunkBlockPos, ChunkPos};
use azalea_world::{Chunk, ChunkStorage, PartialChunkStorage};
use criterion::{criterion_group, criterion_main, Criterion};
use rand::Rng;

fn generate_bedrock_world(
partial_chunks: &mut PartialChunkStorage,
size: u32,
) -> (ChunkStorage, BlockPos, BlockPos) {
let size = size as i32;

let mut chunks = ChunkStorage::default();
for chunk_x in -size..size {
for chunk_z in -size..size {
let chunk_pos = ChunkPos::new(chunk_x, chunk_z);
partial_chunks.set(&chunk_pos, Some(Chunk::default()), &mut chunks);
}
}

let mut rng = rand::thread_rng();

for chunk_x in -size..size {
for chunk_z in -size..size {
let chunk_pos = ChunkPos::new(chunk_x, chunk_z);
let chunk = chunks.get(&chunk_pos).unwrap();
let mut chunk = chunk.write();
for x in 0..16_u8 {
for z in 0..16_u8 {
chunk.set(
&ChunkBlockPos::new(x, 1, z),
azalea_registry::Block::Bedrock.into(),
chunks.min_y,
);
if rng.gen_bool(0.5) {
chunk.set(
&ChunkBlockPos::new(x, 2, z),
azalea_registry::Block::Bedrock.into(),
chunks.min_y,
);
}
}
}
}
}

let mut start = BlockPos::new(-64, 4, -64);
// move start down until it's on bedrock
while chunks.get_block_state(&start).unwrap().is_air() {
start = start.down(1);
}
start = start.up(1);

let mut end = BlockPos::new(63, 4, 63);
// move end down until it's on bedrock
while chunks.get_block_state(&end).unwrap().is_air() {
end = end.down(1);
}
end = end.up(1);

(chunks, start, end)
}

fn bench_pathfinder(c: &mut Criterion) {
c.bench_function("bedrock", |b| {
let mut partial_chunks = PartialChunkStorage::new(32);
let successors_fn = azalea::pathfinder::moves::parkour::parkour_move;

b.iter(|| {
let (world, start, end) = generate_bedrock_world(&mut partial_chunks, 4);
let goal = BlockPosGoal(end);

let successors = |pos: BlockPos| successors_fn(&world, pos);

let astar::Path { movements, partial } = a_star(
start,
|n| goal.heuristic(n),
successors,
|n| goal.success(n),
Duration::MAX,
);

black_box((movements, partial));
})
});
}

criterion_group!(benches, bench_pathfinder);
criterion_main!(benches);
18 changes: 8 additions & 10 deletions azalea/src/pathfinder/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//! A pathfinding plugin to make bots navigate the world. A lot of this code is
//! based on [Baritone](https://github.com/cabaletta/baritone).
mod astar;
pub mod astar;
pub mod costs;
pub mod goals;
mod moves;
pub mod moves;
pub mod simulation;

use crate::bot::{JumpEvent, LookAtEvent};
Expand Down Expand Up @@ -177,10 +177,8 @@ fn goto_listener(
let task = thread_pool.spawn(async move {
debug!("start: {start:?}");

let successors = |pos: BlockPos| {
let world = world_lock.read();
successors_fn(&world, pos)
};
let world = &world_lock.read().chunks;
let successors = |pos: BlockPos| successors_fn(world, pos);

let mut attempt_number = 0;

Expand Down Expand Up @@ -279,8 +277,8 @@ fn path_found_listener(
);
let successors_fn: moves::SuccessorsFn = event.successors_fn;
let successors = |pos: BlockPos| {
let world = world_lock.read();
successors_fn(&world, pos)
let world = &world_lock.read().chunks;
successors_fn(world, pos)
};

if successors(last_node.target)
Expand Down Expand Up @@ -442,8 +440,8 @@ fn tick_execute_path(
{
// obstruction check (the path we're executing isn't possible anymore)
let successors = |pos: BlockPos| {
let world = world_lock.read();
successors_fn(&world, pos)
let world = &world_lock.read().chunks;
successors_fn(world, pos)
};

if let Some(last_reached_node) = pathfinder.last_reached_node {
Expand Down
12 changes: 6 additions & 6 deletions azalea/src/pathfinder/moves/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use azalea_core::{
direction::CardinalDirection,
position::{BlockPos, Vec3},
};
use azalea_world::Instance;
use azalea_world::ChunkStorage;

use crate::{
pathfinder::{astar, costs::*},
Expand All @@ -17,7 +17,7 @@ use super::{
ExecuteCtx, IsReachedCtx, MoveData,
};

pub fn basic_move(world: &Instance, node: BlockPos) -> Vec<Edge> {
pub fn basic_move(world: &ChunkStorage, node: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
edges.extend(forward_move(world, node));
edges.extend(ascend_move(world, node));
Expand All @@ -26,7 +26,7 @@ pub fn basic_move(world: &Instance, node: BlockPos) -> Vec<Edge> {
edges
}

fn forward_move(world: &Instance, pos: BlockPos) -> Vec<Edge> {
fn forward_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
for dir in CardinalDirection::iter() {
let offset = BlockPos::new(dir.x(), 0, dir.z());
Expand Down Expand Up @@ -72,7 +72,7 @@ fn execute_forward_move(
});
}

fn ascend_move(world: &Instance, pos: BlockPos) -> Vec<Edge> {
fn ascend_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
for dir in CardinalDirection::iter() {
let offset = BlockPos::new(dir.x(), 1, dir.z());
Expand Down Expand Up @@ -156,7 +156,7 @@ pub fn ascend_is_reached(
BlockPos::from(position) == target || BlockPos::from(position) == target.down(1)
}

fn descend_move(world: &Instance, pos: BlockPos) -> Vec<Edge> {
fn descend_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
for dir in CardinalDirection::iter() {
let dir_delta = BlockPos::new(dir.x(), 0, dir.z());
Expand Down Expand Up @@ -258,7 +258,7 @@ pub fn descend_is_reached(
&& (position.y - target.y as f64) < 0.5
}

fn diagonal_move(world: &Instance, pos: BlockPos) -> Vec<Edge> {
fn diagonal_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
for dir in CardinalDirection::iter() {
let right = dir.right();
Expand Down
22 changes: 11 additions & 11 deletions azalea/src/pathfinder/moves/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ use super::astar;
use azalea_client::{StartSprintEvent, StartWalkEvent};
use azalea_core::position::{BlockPos, Vec3};
use azalea_physics::collision::{self, BlockWithShape};
use azalea_world::Instance;
use azalea_world::ChunkStorage;
use bevy_ecs::{entity::Entity, event::EventWriter};

type Edge = astar::Edge<BlockPos, MoveData>;

pub type SuccessorsFn =
fn(&azalea_world::Instance, BlockPos) -> Vec<astar::Edge<BlockPos, MoveData>>;
fn(&azalea_world::ChunkStorage, BlockPos) -> Vec<astar::Edge<BlockPos, MoveData>>;

#[derive(Clone)]
pub struct MoveData {
Expand All @@ -34,8 +34,8 @@ impl Debug for MoveData {
}

/// whether this block is passable
fn is_block_passable(pos: &BlockPos, world: &Instance) -> bool {
if let Some(block) = world.chunks.get_block_state(pos) {
fn is_block_passable(pos: &BlockPos, world: &ChunkStorage) -> bool {
if let Some(block) = world.get_block_state(pos) {
if block.shape() != &collision::empty_shape() {
return false;
}
Expand All @@ -58,35 +58,35 @@ fn is_block_passable(pos: &BlockPos, world: &Instance) -> bool {
}

/// whether this block has a solid hitbox (i.e. we can stand on it)
fn is_block_solid(pos: &BlockPos, world: &Instance) -> bool {
if let Some(block) = world.chunks.get_block_state(pos) {
fn is_block_solid(pos: &BlockPos, world: &ChunkStorage) -> bool {
if let Some(block) = world.get_block_state(pos) {
block.shape() == &collision::block_shape()
} else {
false
}
}

/// Whether this block and the block above are passable
fn is_passable(pos: &BlockPos, world: &Instance) -> bool {
fn is_passable(pos: &BlockPos, world: &ChunkStorage) -> bool {
is_block_passable(pos, world) && is_block_passable(&pos.up(1), world)
}

/// Whether we can stand in this position. Checks if the block below is solid,
/// and that the two blocks above that are passable.
fn is_standable(pos: &BlockPos, world: &Instance) -> bool {
fn is_standable(pos: &BlockPos, world: &ChunkStorage) -> bool {
is_block_solid(&pos.down(1), world) && is_passable(pos, world)
}

/// Get the amount of air blocks until the next solid block below this one.
fn fall_distance(pos: &BlockPos, world: &Instance) -> u32 {
fn fall_distance(pos: &BlockPos, world: &ChunkStorage) -> u32 {
let mut distance = 0;
let mut current_pos = pos.down(1);
while is_block_passable(&current_pos, world) {
distance += 1;
current_pos = current_pos.down(1);

if current_pos.y < world.chunks.min_y {
if current_pos.y < world.min_y {
return u32::MAX;
}
}
Expand Down Expand Up @@ -115,7 +115,7 @@ pub struct IsReachedCtx<'a> {
pub physics: &'a azalea_entity::Physics,
}

pub fn default_move(world: &Instance, node: BlockPos) -> Vec<Edge> {
pub fn default_move(world: &ChunkStorage, node: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
edges.extend(basic::basic_move(world, node));
edges.extend(parkour::parkour_move(world, node));
Expand Down
10 changes: 5 additions & 5 deletions azalea/src/pathfinder/moves/parkour.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use azalea_client::{SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection};
use azalea_core::{direction::CardinalDirection, position::BlockPos};
use azalea_world::Instance;
use azalea_world::ChunkStorage;

use crate::{
pathfinder::{astar, costs::*},
Expand All @@ -12,15 +12,15 @@ use super::{
ExecuteCtx, IsReachedCtx, MoveData,
};

pub fn parkour_move(world: &Instance, node: BlockPos) -> Vec<Edge> {
pub fn parkour_move(world: &ChunkStorage, node: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
edges.extend(parkour_forward_1_move(world, node));
edges.extend(parkour_headhitter_forward_1_move(world, node));
edges.extend(parkour_forward_2_move(world, node));
edges
}

fn parkour_forward_1_move(world: &Instance, pos: BlockPos) -> Vec<Edge> {
fn parkour_forward_1_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
for dir in CardinalDirection::iter() {
let gap_offset = BlockPos::new(dir.x(), 0, dir.z());
Expand Down Expand Up @@ -61,7 +61,7 @@ fn parkour_forward_1_move(world: &Instance, pos: BlockPos) -> Vec<Edge> {
edges
}

fn parkour_forward_2_move(world: &Instance, pos: BlockPos) -> Vec<Edge> {
fn parkour_forward_2_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
for dir in CardinalDirection::iter() {
let gap_1_offset = BlockPos::new(dir.x(), 0, dir.z());
Expand Down Expand Up @@ -112,7 +112,7 @@ fn parkour_forward_2_move(world: &Instance, pos: BlockPos) -> Vec<Edge> {
edges
}

fn parkour_headhitter_forward_1_move(world: &Instance, pos: BlockPos) -> Vec<Edge> {
fn parkour_headhitter_forward_1_move(world: &ChunkStorage, pos: BlockPos) -> Vec<Edge> {
let mut edges = Vec::new();
for dir in CardinalDirection::iter() {
let gap_offset = BlockPos::new(dir.x(), 0, dir.z());
Expand Down

0 comments on commit 4f6ab28

Please sign in to comment.