summaryrefslogtreecommitdiff
path: root/server/bot/src/algos/simple.rs
diff options
context:
space:
mode:
Diffstat (limited to 'server/bot/src/algos/simple.rs')
-rw-r--r--server/bot/src/algos/simple.rs155
1 files changed, 129 insertions, 26 deletions
diff --git a/server/bot/src/algos/simple.rs b/server/bot/src/algos/simple.rs
index 125f7e0b..149a46b2 100644
--- a/server/bot/src/algos/simple.rs
+++ b/server/bot/src/algos/simple.rs
@@ -1,13 +1,18 @@
+use std::sync::Arc;
+
use crate::{
pathfinding::{find_path_to_neighbour, Path},
BotAlgo, BotInput,
};
use hurrycurry_client_lib::Game;
-use hurrycurry_protocol::{glam::IVec2, ItemIndex, Message, PlayerID};
+use hurrycurry_protocol::{
+ glam::IVec2, ItemIndex, Message, PlayerID, Recipe, RecipeIndex, TileIndex,
+};
+use log::warn;
#[derive(Default)]
pub struct Simple {
- path: Option<(Path, IVec2)>,
+ path: Option<(Path, IVec2, f32)>,
cooldown: f32,
}
@@ -18,29 +23,35 @@ struct SimpleContext<'a> {
state: &'a mut Simple,
}
+type LogicRes<Out = ()> = Result<Out, ()>;
+
impl BotAlgo for Simple {
- fn tick(&mut self, me: PlayerID, game: &Game, _dt: f32) -> BotInput {
+ 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;
+ self.cooldown -= dt;
+ return BotInput::default();
}
- if let Some((path, target)) = &mut self.path {
+ if let Some((path, target, down)) = &mut self.path {
let direction = path.next_direction(pos);
- let done = path.is_done();
+ let arrived = path.is_done();
let target = *target;
- if done {
- self.path = None;
- self.cooldown = 1.;
+ if arrived {
+ *down -= dt;
+ if *down < 0. {
+ self.path = None;
+ self.cooldown = 1.;
+ }
}
return BotInput {
direction,
boost: false,
- interact: if done { Some(target) } else { None },
+ interact: if arrived { Some(target) } else { None },
};
}
@@ -50,7 +61,8 @@ impl BotAlgo for Simple {
me,
state: self,
}
- .update();
+ .update()
+ .ok();
BotInput::default()
}
@@ -84,25 +96,116 @@ impl SimpleContext<'_> {
_ => None,
})
}
- pub fn aquire_item(&mut self, item: ItemIndex) -> bool {
+ fn find_recipe_with_output(&self, item: ItemIndex) -> Option<RecipeIndex> {
+ self.game
+ .data
+ .recipes
+ .iter()
+ .enumerate()
+ .find(|(_, r)| r.outputs().contains(&item))
+ .map(|(i, _)| RecipeIndex(i))
+ }
+ fn find_item_on_map(&self, item: ItemIndex) -> Option<IVec2> {
+ self.game
+ .tiles
+ .iter()
+ .find(|(_, t)| t.item.as_ref().map_or(false, |t| t.kind == item))
+ .map(|(p, _)| *p)
+ }
+ fn find_tile(&self, tile: TileIndex) -> Option<IVec2> {
+ self.game
+ .tiles
+ .iter()
+ .find(|(_, t)| t.kind == tile)
+ .map(|(p, _)| *p)
+ }
+ fn find_empty_interactable_tile(&self) -> Option<IVec2> {
+ self.game
+ .tiles
+ .iter()
+ .find(|(_, t)| self.game.data.tile_interact[t.kind.0] && t.item.is_none())
+ .map(|(p, _)| *p)
+ }
+ pub fn aquire_placed_item(&mut self, item: ItemIndex) -> LogicRes<IVec2> {
+ if let Some(pos) = self.find_item_on_map(item) {
+ return Ok(pos);
+ }
+ self.aquire_item(item)?;
+ if let Some(pos) = self.find_empty_interactable_tile() {
+ if let Err(()) = self.interact_with(pos, 0.) {
+ return Ok(pos);
+ } else {
+ warn!("no path to empty space ");
+ Err(())
+ }
+ } else {
+ warn!("no empty space left");
+ Err(())
+ }
+ }
+ pub fn aquire_item(&mut self, item: ItemIndex) -> LogicRes {
if self.is_hand_item(item) {
- return true;
+ return Ok(());
+ }
+ if let Some(pos) = self.find_item_on_map(item) {
+ self.interact_with(pos, 0.)?;
+ return Ok(());
+ }
+ if let Some(recipe) = self.find_recipe_with_output(item) {
+ let r = &self.game.data.recipes[recipe.0];
+ match r {
+ Recipe::Instant {
+ tile: Some(tile),
+ inputs: [None, None],
+ outputs: [Some(_), None],
+ ..
+ } => {
+ if let Some(pos) = self.find_tile(*tile) {
+ self.interact_with(pos, 0.)?;
+ }
+ }
+ Recipe::Instant {
+ tile: None,
+ inputs: [Some(a), Some(b)],
+ ..
+ } => {
+ let apos = self.aquire_placed_item(*a)?;
+ self.aquire_item(*b)?;
+ self.interact_with(apos, 0.)?;
+ }
+ Recipe::Active {
+ tile: Some(tile),
+ input,
+ duration,
+ ..
+ } => {
+ self.aquire_item(*input)?;
+ if let Some(pos) = self.find_tile(*tile) {
+ self.interact_with(pos, duration + 0.5)?;
+ }
+ }
+ _ => warn!("recipe too hard {r:?}"),
+ }
+ }
+ warn!(
+ "stuck at making item {:?}",
+ self.game.data.item_names[item.0]
+ );
+ Err(())
+ }
+ pub fn interact_with(&mut self, tile: IVec2, duration: f32) -> LogicRes {
+ if let Some(path) = find_path_to_neighbour(&self.game.walkable, self.own_position, tile) {
+ self.state.path = Some((path, tile, duration));
+ Err(())
+ } else {
+ Ok(())
}
-
- false
}
- pub fn update(&mut self) {
+ pub fn update(&mut self) -> LogicRes {
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));
- }
+ self.aquire_item(item)?;
+ self.interact_with(table, 0.)?;
}
+ Ok(())
}
}
-
-// fn find_item_on_map(game: &Game, item: ItemIndex) -> Option<IVec2> {}