diff options
| author | metamuffin <metamuffin@disroot.org> | 2024-08-12 00:50:07 +0200 | 
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2024-08-12 00:50:07 +0200 | 
| commit | fe31730276aff0eeab5996fbdbf7da4cb82bd810 (patch) | |
| tree | 81a11af89824f1a08b3e42a2fdf686a4970d106d | |
| parent | 98ac3be875a30ad97162949aa929c7363d0bf2b3 (diff) | |
| download | hurrycurry-fe31730276aff0eeab5996fbdbf7da4cb82bd810.tar hurrycurry-fe31730276aff0eeab5996fbdbf7da4cb82bd810.tar.bz2 hurrycurry-fe31730276aff0eeab5996fbdbf7da4cb82bd810.tar.zst | |
simple works fine for everything except passive and other special cases
| -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> {} | 
