diff options
author | metamuffin <metamuffin@disroot.org> | 2024-08-12 16:09:40 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-08-12 16:09:40 +0200 |
commit | 7aa0f026f6f0f72efc5513688ecec21a1e43ea74 (patch) | |
tree | 12a1ed96f74725ba96a7b2514cf556a2b6d05770 /server/bot/src/algos/simple.rs | |
parent | ada25eeb227b201b1519ff46a888476a832256e6 (diff) | |
download | hurrycurry-7aa0f026f6f0f72efc5513688ecec21a1e43ea74.tar hurrycurry-7aa0f026f6f0f72efc5513688ecec21a1e43ea74.tar.bz2 hurrycurry-7aa0f026f6f0f72efc5513688ecec21a1e43ea74.tar.zst |
bot: generalize simple and add waiter
Diffstat (limited to 'server/bot/src/algos/simple.rs')
-rw-r--r-- | server/bot/src/algos/simple.rs | 119 |
1 files changed, 80 insertions, 39 deletions
diff --git a/server/bot/src/algos/simple.rs b/server/bot/src/algos/simple.rs index afa5764f..1288aaaf 100644 --- a/server/bot/src/algos/simple.rs +++ b/server/bot/src/algos/simple.rs @@ -14,11 +14,12 @@ pub struct Simple { cooldown: f32, } -struct SimpleContext<'a> { - game: &'a Game, - me: PlayerID, - own_position: IVec2, - state: &'a mut Simple, +pub struct Context<'a, State> { + pub game: &'a Game, + pub me: PlayerID, + pub own_position: IVec2, + pub state: &'a mut State, + pub recursion_abort: usize, } type LogicRes<Out = ()> = Result<Out, ()>; @@ -53,11 +54,12 @@ impl BotAlgo for Simple { }; } - SimpleContext { + Context { game, own_position: pos.as_ivec2(), me, state: self, + recursion_abort: 0, } .update() .ok(); @@ -67,7 +69,24 @@ impl BotAlgo for Simple { } } -impl SimpleContext<'_> { +pub trait State { + fn cooldown(&mut self, duration: f32); + fn queue_segment(&mut self, path: Path, tile: IVec2, duration: f32); + fn get_empty_tile_priority(&self) -> &'static [&'static str]; +} +impl State for Simple { + fn cooldown(&mut self, duration: f32) { + self.cooldown = duration; + } + fn queue_segment(&mut self, path: Path, tile: IVec2, duration: f32) { + self.path = Some((path, tile, duration)); + } + fn get_empty_tile_priority(&self) -> &'static [&'static str] { + &["counter", "counter-window"] + } +} + +impl<S> Context<'_, S> { pub fn is_hand_item(&self, item: ItemIndex) -> bool { self.game .players @@ -81,7 +100,7 @@ impl SimpleContext<'_> { .map(|p| p.item.is_some()) .unwrap_or(false) } - fn find_demand(&self) -> Option<(ItemIndex, IVec2)> { + pub fn find_demand(&self) -> Option<(ItemIndex, IVec2)> { self.game .players .iter() @@ -102,7 +121,7 @@ impl SimpleContext<'_> { _ => None, }) } - fn find_recipe_with_output(&self, item: ItemIndex) -> Option<RecipeIndex> { + pub fn find_recipe_with_output(&self, item: ItemIndex) -> Option<RecipeIndex> { self.game .data .recipes @@ -111,70 +130,84 @@ impl SimpleContext<'_> { .find(|(_, r)| r.outputs().contains(&item)) .map(|(i, _)| RecipeIndex(i)) } - fn find_item_on_map(&self, item: ItemIndex) -> Option<IVec2> { + pub 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> { + pub 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_by_name(&self, name: &str) -> Option<IVec2> { + pub fn find_occupied_table_or_floor(&self) -> Option<IVec2> { self.game .tiles .iter() .find(|(_, t)| { - self.game.data.tile_interact[t.kind.0] - && t.item.is_none() - && self.game.data.tile_names[t.kind.0] == name + t.item.is_some() + && matches!( + self.game.data.tile_names[t.kind.0].as_str(), + "table" | "floor" + ) }) .map(|(p, _)| *p) } - fn find_empty_interactable_tile(&self) -> Option<IVec2> { - if let Some(t) = self.find_empty_interactable_tile_by_name("counter") { - return Some(t); - } - if let Some(t) = self.find_empty_interactable_tile_by_name("counter-window") { - return Some(t); - } - warn!("all counters filled up"); + pub fn find_empty_interactable_tile_by_name(&self, name: &str) -> Option<IVec2> { self.game .tiles .iter() - .find(|(_, t)| self.game.data.tile_interact[t.kind.0] && t.item.is_none()) + .find(|(_, t)| { + self.game.data.tile_interact[t.kind.0] + && t.item.is_none() + && self.game.data.tile_names[t.kind.0] == name + }) .map(|(p, _)| *p) } - fn is_tile_occupied(&self, pos: IVec2) -> bool { + + pub fn is_tile_occupied(&self, pos: IVec2) -> bool { self.game .tiles .get(&pos) .map(|t| t.item.is_some()) .unwrap_or(true) } - fn clear_tile(&mut self, pos: IVec2) -> LogicRes { +} +impl<S: State> Context<'_, S> { + pub fn find_empty_interactable_tile(&self) -> Option<IVec2> { + for p in self.state.get_empty_tile_priority() { + if let Some(t) = self.find_empty_interactable_tile_by_name(p) { + return Some(t); + } + } + warn!("all counters filled up"); + self.game + .tiles + .iter() + .find(|(_, t)| self.game.data.tile_interact[t.kind.0] && t.item.is_none()) + .map(|(p, _)| *p) + } + pub fn clear_tile(&mut self, pos: IVec2) -> LogicRes { debug!("clear tile {pos}"); self.assert_hand_is_clear()?; self.interact_with(pos, 0.) } - fn assert_tile_is_clear(&mut self, pos: IVec2) -> LogicRes { + pub fn assert_tile_is_clear(&mut self, pos: IVec2) -> LogicRes { if self.is_tile_occupied(pos) { self.clear_tile(pos)?; } Ok(()) } - fn assert_hand_is_clear(&mut self) -> LogicRes { + pub fn assert_hand_is_clear(&mut self) -> LogicRes { if self.is_hand_occupied() { self.dispose_hand()?; } Ok(()) } - pub fn dispose_hand(&mut self) -> LogicRes { debug!("dispose hand"); if let Some(pos) = self.find_empty_interactable_tile() { @@ -186,6 +219,17 @@ impl SimpleContext<'_> { 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.queue_segment(path, tile, duration); + Err(()) + } else { + Ok(()) + } + } +} + +impl Context<'_, Simple> { pub fn aquire_placed_item(&mut self, item: ItemIndex) -> LogicRes<IVec2> { debug!("aquire placed item {:?}", self.game.data.item_names[item.0]); if let Some(pos) = self.find_item_on_map(item) { @@ -197,6 +241,11 @@ impl SimpleContext<'_> { } pub fn aquire_item(&mut self, item: ItemIndex) -> LogicRes { debug!("aquire item {:?}", self.game.data.item_names[item.0]); + self.recursion_abort += 1; + if self.recursion_abort > 32 { + warn!("too much recursion"); + return Err(()); + } if self.is_hand_item(item) { return Ok(()); } @@ -261,7 +310,7 @@ impl SimpleContext<'_> { if let Some(item) = &self.game.tiles.get(&pos).unwrap().item { if item.kind == *input { debug!("waiting for passive to finish at {pos}"); - self.state.cooldown = 0.5; + self.state.cooldown(0.5); return Err(()); // waiting for it to finish // TODO check progress } else { @@ -277,7 +326,7 @@ impl SimpleContext<'_> { } => { self.aquire_placed_item(*input)?; debug!("waiting for passive to finish"); - self.state.cooldown = 0.5; + self.state.cooldown(0.5); return Err(()); } _ => warn!("recipe too hard {r:?}"), @@ -289,14 +338,6 @@ impl SimpleContext<'_> { ); 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(()) - } - } pub fn update(&mut self) -> LogicRes { if let Some((item, table)) = self.find_demand() { self.assert_tile_is_clear(table)?; |