diff options
Diffstat (limited to 'server')
| -rw-r--r-- | server/bot/Cargo.toml | 1 | ||||
| -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 | 
6 files changed, 148 insertions, 9 deletions
| diff --git a/server/bot/Cargo.toml b/server/bot/Cargo.toml index e00855cd..2afc1e59 100644 --- a/server/bot/Cargo.toml +++ b/server/bot/Cargo.toml @@ -10,3 +10,4 @@ log = "0.4.22"  anyhow = "1.0.86"  env_logger = "0.11.5"  rustls = { version = "0.23.12", features = ["ring"] } +clap = { version = "4.5.15", features = ["derive"] } 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); | 
