diff options
Diffstat (limited to 'server/bot/src/algos/simple.rs')
-rw-r--r-- | server/bot/src/algos/simple.rs | 155 |
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> {} |