aboutsummaryrefslogtreecommitdiff
path: root/server/bot/src
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-08-11 19:22:29 +0200
committermetamuffin <metamuffin@disroot.org>2024-08-11 19:22:29 +0200
commit98ac3be875a30ad97162949aa929c7363d0bf2b3 (patch)
tree028ab01c471ca4d13ab659a7d5b9fd3a45a03e90 /server/bot/src
parent012fbc3e6382a92de60a191690000b0e83653cc6 (diff)
downloadhurrycurry-98ac3be875a30ad97162949aa929c7363d0bf2b3.tar
hurrycurry-98ac3be875a30ad97162949aa929c7363d0bf2b3.tar.bz2
hurrycurry-98ac3be875a30ad97162949aa929c7363d0bf2b3.tar.zst
improve framework and start work on simple algo
Diffstat (limited to 'server/bot/src')
-rw-r--r--server/bot/src/algos/mod.rs6
-rw-r--r--server/bot/src/algos/simple.rs108
-rw-r--r--server/bot/src/algos/test.rs4
-rw-r--r--server/bot/src/main.rs26
-rw-r--r--server/bot/src/pathfinding.rs12
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);