aboutsummaryrefslogtreecommitdiff
path: root/server/bot/src/main.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-08-11 13:35:15 +0200
committermetamuffin <metamuffin@disroot.org>2024-08-11 13:35:15 +0200
commit52d99b16534631e293a23ddbc18c4ea70b71392f (patch)
treef596887a976540ab553e69105ab192cbbb2dd753 /server/bot/src/main.rs
parent63d5a3ff37d1e3972d34ccc9e1d26c3b4bc05efb (diff)
downloadhurrycurry-52d99b16534631e293a23ddbc18c4ea70b71392f.tar
hurrycurry-52d99b16534631e293a23ddbc18c4ea70b71392f.tar.bz2
hurrycurry-52d99b16534631e293a23ddbc18c4ea70b71392f.tar.zst
add recipes back to protocol
Diffstat (limited to 'server/bot/src/main.rs')
-rw-r--r--server/bot/src/main.rs134
1 files changed, 127 insertions, 7 deletions
diff --git a/server/bot/src/main.rs b/server/bot/src/main.rs
index 18c4617a..864141b0 100644
--- a/server/bot/src/main.rs
+++ b/server/bot/src/main.rs
@@ -15,13 +15,21 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+#![feature(isqrt)]
+pub mod pathfinding;
use anyhow::Result;
use hurrycurry_client_lib::{network::sync::Network, Game};
-use hurrycurry_protocol::{glam::Vec2, PacketC, PacketS, PlayerID};
+use hurrycurry_protocol::{
+ glam::{IVec2, Vec2},
+ ItemIndex, Message, PacketC, PacketS, PlayerID, RecipeIndex,
+};
+use log::{info, warn};
+use pathfinding::{find_path, Path};
use std::{thread::sleep, time::Duration};
fn main() -> Result<()> {
+ env_logger::init_from_env("LOG");
let mut network = Network::connect("ws://127.0.0.1")?;
let mut game = Game::default();
@@ -46,11 +54,19 @@ fn main() -> Result<()> {
game.apply_packet(packet);
}
- for b in &bots {
+ for b in &mut bots {
+ let (dir, boost, interact) = b.tick(&game);
+ if interact.is_some() != b.interacting {
+ b.interacting = interact.is_some();
+ network.queue_out.push_back(PacketS::Interact {
+ player: b.id,
+ pos: interact,
+ })
+ }
network.queue_out.push_back(PacketS::Movement {
player: b.id,
- dir: Vec2::ONE,
- boost: true,
+ dir,
+ boost,
pos: None,
});
}
@@ -60,14 +76,118 @@ fn main() -> Result<()> {
}
pub struct Bot {
+ pub interacting: bool,
+
id: PlayerID,
+ want: Option<ItemIndex>,
+ take: Option<IVec2>,
+ put: Option<IVec2>,
+ path: Option<Path>,
}
impl Bot {
pub fn new(id: PlayerID) -> Self {
- Self { id }
+ Self {
+ id,
+ want: None,
+ path: None,
+ take: None,
+ put: None,
+ interacting: false,
+ }
}
- pub fn tick(&self, game: &Game) {
- if let Some(player) = game.players.get(&self.id) {}
+ pub fn tick(&mut self, game: &Game) -> (Vec2, bool, Option<IVec2>) {
+ if let Some(player) = game.players.get(&self.id) {
+ let pos = player.movement.position;
+
+ if let Some(path) = &mut self.path {
+ let dir = path.next_direction(pos);
+ if path.is_done() {
+ self.path = None;
+ }
+ return (dir, false, None);
+ }
+ if let Some(interact) = self.take.take() {
+ return (Vec2::ZERO, false, Some(interact));
+ }
+ if let Some(item) = &player.item {
+ if Some(item.kind) == self.want {
+ if let Some(interact) = self.put.take() {
+ return (Vec2::ZERO, false, Some(interact));
+ }
+ }
+ }
+
+ if let Some(item) = self.want {
+ if let Some((path, target)) = find_item_on_map(game, pos.as_ivec2(), item) {
+ info!("target={target}");
+ info!("path found");
+ self.path = Some(path);
+ self.take = Some(target);
+ } else if let Some(recipe) = find_item_as_recipe_output(game, item) {
+ info!("recipe={recipe:?}");
+ self.want = game.data.recipes[recipe.0].outputs().first().copied();
+ info!("want={:?}", self.want)
+ } else {
+ warn!("stuck");
+ }
+ } else {
+ if let Some((item, dest)) = select_demand(game) {
+ info!("want={item:?}");
+ self.want = Some(item);
+ self.put = Some(dest);
+ }
+ }
+ }
+ (Vec2::ZERO, false, None)
}
}
+
+fn find_item_as_recipe_output(game: &Game, item: ItemIndex) -> Option<RecipeIndex> {
+ game.data
+ .recipes
+ .iter()
+ .enumerate()
+ .find(|(_, r)| r.inputs().contains(&item))
+ .map(|r| RecipeIndex(r.0))
+}
+
+fn find_item_on_map(game: &Game, player: IVec2, item: ItemIndex) -> Option<(Path, IVec2)> {
+ game.tiles.iter().find_map(|(pos, tile)| {
+ if let Some(i) = &tile.item {
+ if i.kind == item {
+ for xo in -1..=1 {
+ for yo in -1..=1 {
+ let t = *pos + IVec2::new(xo, yo);
+ if let Some(path) = find_path(&game.walkable, player, t) {
+ return Some((path, *pos));
+ }
+ }
+ }
+ }
+ }
+ None
+ })
+}
+
+fn select_demand(game: &Game) -> Option<(ItemIndex, IVec2)> {
+ game.players
+ .iter()
+ .find_map(|(_, pl)| match &pl.communicate_persist {
+ Some(Message::Item(item)) => {
+ let pos = pl.movement.position.as_ivec2();
+ for xo in -1..=1 {
+ for yo in -1..=1 {
+ let t = pos + IVec2::new(xo, yo);
+ if let Some(tile) = game.tiles.get(&t) {
+ if game.data.tile_interact[tile.kind.0] {
+ return Some((*item, t));
+ }
+ }
+ }
+ }
+ None
+ }
+ _ => None,
+ })
+}