diff options
Diffstat (limited to 'server/bot/src')
-rw-r--r-- | server/bot/src/algos/mod.rs | 6 | ||||
-rw-r--r-- | server/bot/src/algos/simple.rs | 108 | ||||
-rw-r--r-- | server/bot/src/algos/test.rs | 4 | ||||
-rw-r--r-- | server/bot/src/main.rs | 26 | ||||
-rw-r--r-- | server/bot/src/pathfinding.rs | 12 |
5 files changed, 147 insertions, 9 deletions
diff --git a/server/bot/src/algos/mod.rs b/server/bot/src/algos/mod.rs index 7b788c20..920230e5 100644 --- a/server/bot/src/algos/mod.rs +++ b/server/bot/src/algos/mod.rs @@ -1 +1,5 @@ -pub mod test; +mod simple; +pub use simple::Simple; + +mod test; +pub use test::Test; diff --git a/server/bot/src/algos/simple.rs b/server/bot/src/algos/simple.rs new file mode 100644 index 00000000..125f7e0b --- /dev/null +++ b/server/bot/src/algos/simple.rs @@ -0,0 +1,108 @@ +use crate::{ + pathfinding::{find_path_to_neighbour, Path}, + BotAlgo, BotInput, +}; +use hurrycurry_client_lib::Game; +use hurrycurry_protocol::{glam::IVec2, ItemIndex, Message, PlayerID}; + +#[derive(Default)] +pub struct Simple { + path: Option<(Path, IVec2)>, + cooldown: f32, +} + +struct SimpleContext<'a> { + game: &'a Game, + me: PlayerID, + own_position: IVec2, + state: &'a mut Simple, +} + +impl BotAlgo for Simple { + fn tick(&mut self, me: PlayerID, game: &Game, _dt: f32) -> BotInput { + let Some(player) = game.players.get(&me) else { + return BotInput::default(); + }; + let pos = player.movement.position; + + if self.cooldown > 0. { + self.cooldown -= _dt; + } + + if let Some((path, target)) = &mut self.path { + let direction = path.next_direction(pos); + let done = path.is_done(); + let target = *target; + if done { + self.path = None; + self.cooldown = 1.; + } + return BotInput { + direction, + boost: false, + interact: if done { Some(target) } else { None }, + }; + } + + SimpleContext { + game, + own_position: pos.as_ivec2(), + me, + state: self, + } + .update(); + + BotInput::default() + } +} + +impl SimpleContext<'_> { + pub fn is_hand_item(&self, item: ItemIndex) -> bool { + self.game + .players + .get(&self.me) + .map_or(false, |p| p.item.as_ref().map_or(false, |i| i.kind == item)) + } + fn find_demand(&self) -> Option<(ItemIndex, IVec2)> { + self.game + .players + .iter() + .find_map(|(_, pl)| match &pl.communicate_persist { + Some(Message::Item(item)) => { + let pos = pl.movement.position.as_ivec2(); + [IVec2::X, IVec2::Y, -IVec2::X, -IVec2::Y] + .into_iter() + .find(|off| { + self.game + .tiles + .get(&(pos + *off)) + .map_or(false, |t| self.game.data.tile_interact[t.kind.0]) + }) + .map(|off| pos + off) + .map(|pos| (*item, pos)) + } + _ => None, + }) + } + pub fn aquire_item(&mut self, item: ItemIndex) -> bool { + if self.is_hand_item(item) { + return true; + } + + false + } + pub fn update(&mut self) { + if let Some((item, table)) = self.find_demand() { + if !self.aquire_item(item) { + return; + } + if let Some(path) = + find_path_to_neighbour(&self.game.walkable, self.own_position, table) + { + self.state.path = Some((path, table)); + } + } + } +} + +// fn find_item_on_map(game: &Game, item: ItemIndex) -> Option<IVec2> {} diff --git a/server/bot/src/algos/test.rs b/server/bot/src/algos/test.rs index 17999f5e..d17b079d 100644 --- a/server/bot/src/algos/test.rs +++ b/server/bot/src/algos/test.rs @@ -1,5 +1,5 @@ use crate::{ - pathfinding::{find_path, Path}, + pathfinding::{find_path_to_neighbour, Path}, BotAlgo, BotInput, }; use hurrycurry_client_lib::Game; @@ -28,7 +28,7 @@ impl BotAlgo for Test { } else { if let Some((item, near)) = find_demand(game) { info!("demand {item:?} near {near}"); - if let Some(path) = find_path(&game.walkable, pos.as_ivec2(), near) { + if let Some(path) = find_path_to_neighbour(&game.walkable, pos.as_ivec2(), near) { self.path = Some(path); } } diff --git a/server/bot/src/main.rs b/server/bot/src/main.rs index 0e202894..08d17d19 100644 --- a/server/bot/src/main.rs +++ b/server/bot/src/main.rs @@ -20,6 +20,7 @@ pub mod algos; pub mod pathfinding; use anyhow::Result; +use clap::Parser; use hurrycurry_client_lib::{network::sync::Network, Game}; use hurrycurry_protocol::{ glam::{IVec2, Vec2}, @@ -38,17 +39,30 @@ pub trait BotAlgo { fn tick(&mut self, me: PlayerID, game: &Game, dt: f32) -> BotInput; } +#[derive(Parser)] +struct Args { + algo: String, + address: String, +} + fn main() -> Result<()> { env_logger::init_from_env("LOG"); rustls::crypto::ring::default_provider() .install_default() .unwrap(); - let mut network = Network::connect( - &std::env::args() - .nth(1) - .expect("usage: bot <websocket address>"), - )?; + let args = Args::parse(); + + let algo = args.algo.to_owned(); + let init_algo = move || -> Box<dyn BotAlgo> { + match algo.as_str() { + "test" => Box::new(algos::Test::default()), + "simple" => Box::new(algos::Simple::default()), + _ => panic!("unknown algo {algo:?}"), + } + }; + + let mut network = Network::connect(&args.address)?; let mut game = Game::default(); @@ -69,7 +83,7 @@ fn main() -> Result<()> { PacketC::Joined { id } => bots.push(BotDriver { id: *id, interacting: false, - state: Box::new(algos::test::Test::default()), + state: init_algo(), }), PacketC::Error { message } => { warn!("server error message: {message}"); diff --git a/server/bot/src/pathfinding.rs b/server/bot/src/pathfinding.rs index 87ccf391..ec557e28 100644 --- a/server/bot/src/pathfinding.rs +++ b/server/bot/src/pathfinding.rs @@ -42,6 +42,18 @@ impl Path { } } +pub fn find_path_to_neighbour(walkable: &HashSet<IVec2>, from: IVec2, to: IVec2) -> Option<Path> { + let mut paths = Vec::new(); + for xo in -1..=1 { + for yo in -1..=1 { + let to = to + IVec2::new(xo, yo); + if walkable.contains(&to) { + paths.extend(find_path(walkable, from, to)) + } + } + } + paths.into_iter().min_by_key(|p| p.0.len()) +} pub fn find_path(walkable: &HashSet<IVec2>, from: IVec2, to: IVec2) -> Option<Path> { #[derive(Debug, PartialEq, Eq)] struct Open(i32, IVec2, IVec2, i32); |