1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 06:16:04 +00:00

pathfinding works*

This commit is contained in:
mat 2022-11-02 22:02:01 -05:00
parent 6d1b58b868
commit 9f046adf9b
4 changed files with 193 additions and 50 deletions

View file

@ -42,31 +42,36 @@ impl Trait for azalea_client::Client {
fn goto(&self, goal: impl Goal) {
let start = Node {
pos: BlockPos::from(self.entity().pos()),
vertical_vel: VerticalVel::None,
};
let end = goal.goal_node();
println!("start: {:?}, end: {:?}", start, end);
let successors = |node: &Node| {
println!("successors for {:?}", node);
let mut edges = Vec::new();
let possible_moves: Vec<&dyn moves::Move> = vec![
&moves::NorthMove,
&moves::SouthMove,
&moves::EastMove,
&moves::WestMove,
&moves::JumpUpMove,
&moves::FallNorthMove,
&moves::FallSouthMove,
&moves::FallEastMove,
&moves::FallWestMove,
&moves::LandMove,
];
let dimension = self.dimension.read();
for possible_move in possible_moves.iter() {
let can_execute = possible_move.can_execute(&dimension, &node.pos);
edges.push(Edge {
target: Node {
pos: node.pos + possible_move.offset(),
target: possible_move.next_node(&node),
cost: if possible_move.can_execute(&dimension, node) {
possible_move.cost()
} else {
f32::INFINITY
},
cost: if can_execute { 1.0 } else { f32::INFINITY },
});
println!("can execute for {:?}: {}", node, can_execute);
}
println!("edges: {}", edges.len());
edges
};
@ -79,6 +84,7 @@ impl Trait for azalea_client::Client {
|n| goal.success(n),
);
let p = pf.find_path();
println!("path: {:?}", p);
let state = self.plugins.get::<Plugin>().unwrap().state.clone();
// convert the Option<Vec<Node>> to a VecDeque<Node>
@ -93,22 +99,39 @@ fn tick_execute_path(bot: &mut Client, path: &mut VecDeque<Node>) {
return;
};
let center = target.pos.center();
println!("going to {center:?} (at {pos:?})", pos = bot.entity().pos());
// println!("going to {center:?} (at {pos:?})", pos = bot.entity().pos());
bot.look_at(&center);
bot.walk(WalkDirection::Forward);
// check if we should jump
if target.pos.y > bot.entity().pos().y.floor() as i32 {
bot.jump();
}
if target.is_reached(&bot.entity()) {
println!("ok target reached");
println!("ok target {target:?} reached");
path.pop_front();
if path.is_empty() {
bot.walk(WalkDirection::None);
}
// tick again, maybe we already reached the next node!
tick_execute_path(bot, path);
}
}
/// Information about our vertical velocity
#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug)]
pub enum VerticalVel {
None,
/// No vertical velocity, but we're not on the ground
NoneMidair,
// less than 3 blocks (no fall damage)
FallingLittle,
}
#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug)]
pub struct Node {
pub pos: BlockPos,
pub vertical_vel: VerticalVel,
}
pub trait Goal {
@ -123,13 +146,19 @@ impl Node {
/// Returns whether the entity is at the node and should start going to the
/// next node.
pub fn is_reached(&self, entity: &EntityData) -> bool {
// println!(
// "entity.yya: {} {:?}=={:?}",
// entity.yya,
// BlockPos::from(entity.pos()),
// self.pos
// );
entity.yya == 0. && BlockPos::from(entity.pos()) == self.pos
println!(
"entity.delta.y: {} {:?}=={:?}, self.vertical_vel={:?}",
entity.delta.y,
BlockPos::from(entity.pos()),
self.pos,
self.vertical_vel
);
BlockPos::from(entity.pos()) == self.pos
&& match self.vertical_vel {
VerticalVel::NoneMidair => (entity.delta.y > -0.1 && entity.delta.y < 0.1),
VerticalVel::None => entity.on_ground,
VerticalVel::FallingLittle => entity.delta.y < -0.1,
}
}
}
@ -147,7 +176,10 @@ impl Goal for BlockPosGoal {
n.pos == self.pos
}
fn goal_node(&self) -> Node {
Node { pos: self.pos }
Node {
pos: self.pos,
vertical_vel: VerticalVel::None,
}
}
}

View file

@ -3,14 +3,11 @@ use azalea_core::BlockPos;
use azalea_physics::collision::{self, BlockWithShape};
use azalea_world::Dimension;
use crate::{Node, VerticalVel};
/// whether this block is passable
fn is_passable(pos: &BlockPos, dim: &Dimension) -> bool {
if let Some(block) = dim.get_block_state(pos) {
println!(
"is passable {pos:?} {} = {}",
Box::<dyn Block>::from(block).id(),
block.shape() == &collision::empty_shape()
);
block.shape() == &collision::empty_shape()
} else {
false
@ -20,11 +17,6 @@ fn is_passable(pos: &BlockPos, dim: &Dimension) -> bool {
/// whether this block has a solid hitbox (i.e. we can stand on it)
fn is_solid(pos: &BlockPos, dim: &Dimension) -> bool {
if let Some(block) = dim.get_block_state(pos) {
println!(
"is solid {pos:?} {} = {}",
Box::<dyn Block>::from(block).id(),
block.shape() == &collision::block_shape()
);
block.shape() == &collision::block_shape()
} else {
false
@ -38,15 +30,26 @@ fn is_standable(pos: &BlockPos, dim: &Dimension) -> bool {
}
pub trait Move {
fn can_execute(&self, dim: &Dimension, pos: &BlockPos) -> bool;
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool;
/// Returns by how much the entity's position should be changed when this move is executed.
fn offset(&self) -> BlockPos;
fn next_node(&self, node: &Node) -> Node {
Node {
pos: node.pos + self.offset(),
vertical_vel: VerticalVel::None,
}
}
fn cost(&self) -> f32 {
1.0
}
}
pub struct NorthMove;
impl Move for NorthMove {
fn can_execute(&self, dim: &Dimension, pos: &BlockPos) -> bool {
is_standable(&(pos + &self.offset()), dim)
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
is_standable(&(node.pos + self.offset()), dim)
&& (node.vertical_vel == VerticalVel::None
|| node.vertical_vel == VerticalVel::NoneMidair)
}
fn offset(&self) -> BlockPos {
BlockPos::new(0, 0, -1)
@ -55,8 +58,10 @@ impl Move for NorthMove {
pub struct SouthMove;
impl Move for SouthMove {
fn can_execute(&self, dim: &Dimension, pos: &BlockPos) -> bool {
is_standable(&(pos + &self.offset()), dim)
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
is_standable(&(node.pos + self.offset()), dim)
&& (node.vertical_vel == VerticalVel::None
|| node.vertical_vel == VerticalVel::NoneMidair)
}
fn offset(&self) -> BlockPos {
BlockPos::new(0, 0, 1)
@ -65,8 +70,10 @@ impl Move for SouthMove {
pub struct EastMove;
impl Move for EastMove {
fn can_execute(&self, dim: &Dimension, pos: &BlockPos) -> bool {
is_standable(&(pos + &self.offset()), dim)
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
is_standable(&(node.pos + self.offset()), dim)
&& (node.vertical_vel == VerticalVel::None
|| node.vertical_vel == VerticalVel::NoneMidair)
}
fn offset(&self) -> BlockPos {
BlockPos::new(1, 0, 0)
@ -75,14 +82,126 @@ impl Move for EastMove {
pub struct WestMove;
impl Move for WestMove {
fn can_execute(&self, dim: &Dimension, pos: &BlockPos) -> bool {
is_standable(&(pos + &self.offset()), dim)
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
is_standable(&(node.pos + self.offset()), dim)
&& (node.vertical_vel == VerticalVel::None
|| node.vertical_vel == VerticalVel::NoneMidair)
}
fn offset(&self) -> BlockPos {
BlockPos::new(-1, 0, 0)
}
}
pub struct JumpUpMove;
impl Move for JumpUpMove {
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
is_passable(&node.pos.up(2), dim) && node.vertical_vel == VerticalVel::None
}
fn offset(&self) -> BlockPos {
BlockPos::new(0, 1, 0)
}
fn next_node(&self, node: &Node) -> Node {
Node {
pos: node.pos + self.offset(),
vertical_vel: VerticalVel::NoneMidair,
}
}
}
pub struct FallNorthMove;
impl Move for FallNorthMove {
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
// check whether 3 blocks vertically forward are passable
is_passable(&(node.pos + self.offset().down(1)), dim)
&& is_passable(&(node.pos + self.offset()), dim)
&& is_passable(&(node.pos + self.offset().up(1)), dim)
&& node.vertical_vel == VerticalVel::None
}
fn offset(&self) -> BlockPos {
BlockPos::new(0, 0, -1)
}
fn next_node(&self, node: &Node) -> Node {
Node {
pos: node.pos + self.offset(),
vertical_vel: VerticalVel::NoneMidair,
}
}
}
pub struct FallSouthMove;
impl Move for FallSouthMove {
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
// check whether 3 blocks vertically forward are passable
is_passable(&(node.pos + self.offset().down(1)), dim)
&& is_passable(&(node.pos + self.offset()), dim)
&& is_passable(&(node.pos + self.offset().up(1)), dim)
&& node.vertical_vel == VerticalVel::None
}
fn offset(&self) -> BlockPos {
BlockPos::new(0, 0, 1)
}
fn next_node(&self, node: &Node) -> Node {
Node {
pos: node.pos + self.offset(),
vertical_vel: VerticalVel::NoneMidair,
}
}
}
pub struct FallEastMove;
impl Move for FallEastMove {
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
// check whether 3 blocks vertically forward are passable
is_passable(&(node.pos + self.offset().down(1)), dim)
&& is_passable(&(node.pos + self.offset()), dim)
&& is_passable(&(node.pos + self.offset().up(1)), dim)
&& node.vertical_vel == VerticalVel::None
}
fn offset(&self) -> BlockPos {
BlockPos::new(1, 0, 0)
}
fn next_node(&self, node: &Node) -> Node {
Node {
pos: node.pos + self.offset(),
vertical_vel: VerticalVel::NoneMidair,
}
}
}
pub struct FallWestMove;
impl Move for FallWestMove {
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
// check whether 3 blocks vertically forward are passable
is_passable(&(node.pos + self.offset().down(1)), dim)
&& is_passable(&(node.pos + self.offset()), dim)
&& is_passable(&(node.pos + self.offset().up(1)), dim)
&& node.vertical_vel == VerticalVel::None
}
fn offset(&self) -> BlockPos {
BlockPos::new(-1, 0, 0)
}
fn next_node(&self, node: &Node) -> Node {
Node {
pos: node.pos + self.offset(),
vertical_vel: VerticalVel::NoneMidair,
}
}
}
pub struct LandMove;
impl Move for LandMove {
fn can_execute(&self, dim: &Dimension, node: &Node) -> bool {
is_standable(&(node.pos + self.offset()), dim)
&& (node.vertical_vel == VerticalVel::NoneMidair
|| node.vertical_vel == VerticalVel::FallingLittle)
}
fn offset(&self) -> BlockPos {
BlockPos::new(0, -1, 0)
}
fn next_node(&self, node: &Node) -> Node {
Node {
pos: node.pos + self.offset(),
vertical_vel: VerticalVel::None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -15,21 +15,14 @@ pub struct State {
}
pub trait BotTrait {
fn jump(&self);
fn jump(&mut self);
fn look_at(&mut self, pos: &Vec3);
}
impl BotTrait for azalea_client::Client {
/// Queue a jump for the next tick.
fn jump(&self) {
{
let player_entity_id = self.player.read().entity_id;
let mut dimension_lock = self.dimension.write();
let mut player_entity = dimension_lock
.entity_mut(player_entity_id)
.expect("Player must exist");
player_entity.jumping = true;
}
fn jump(&mut self) {
self.set_jumping(true);
let state = self.plugins.get::<Plugin>().unwrap().state.clone();
*state.jumping_once.lock() = true;
}
@ -48,8 +41,7 @@ impl crate::Plugin for Plugin {
if *self.state.jumping_once.lock() {
if bot.jumping() {
*self.state.jumping_once.lock() = false;
} else {
bot.set_jumping(true);
bot.set_jumping(false);
}
}
}

View file

@ -40,8 +40,8 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<
.pos()
.clone();
let target_pos: BlockPos = (&target_pos_vec3).into();
bot.look_at(&target_pos_vec3);
// bot.goto(BlockPosGoal::from(target_pos));
// bot.look_at(&target_pos_vec3);
bot.goto(BlockPosGoal::from(target_pos));
// bot.walk(WalkDirection::Forward);
}
}