diff --git a/README.md b/README.md index 11e5c29..38b08b1 100644 --- a/README.md +++ b/README.md @@ -86,9 +86,10 @@ Broadcast \n | Command | Definition | Sent by | | -------- | ---------- | ------- | | ` assign ` | assign role to a new AI (by **default** considered a `Queen`) | `Queen`\|`Knight` | +| ` waiting` | Wait for AI `Queen` to join at same tile | `Queen` | | ` inc` | Incantating | `Queen` | -| ` mv` | Moved | `Queen` | | ` lvl ` | Notify level | `Queen` | +| ` mv` | Moved | `Queen` | | ` nf` | Need `food` | `Queen` | | ` nl` | Need `linemate` | `Queen` | | ` nd` | Need `deraumere` | `Queen` | diff --git a/ai/src/ai.rs b/ai/src/ai.rs index a28a1b6..c6264d7 100644 --- a/ai/src/ai.rs +++ b/ai/src/ai.rs @@ -102,6 +102,24 @@ fn init_from_broadcast(info: &AI, role: String) -> Result, St }) } +async fn init_and_update(ai: AI, role: String) -> io::Result<()> { + let mut rle = + init_from_broadcast(&ai, role).map_err(|e| std::io::Error::new(ErrorKind::NotFound, e))?; + + let ai_status = rle.update().await; + if let Err(CommandError::DeadReceived) = ai_status { + info!("~[{}] AI is dead, stopping the process...", ai.cli_id); + return Err(Error::new(ErrorKind::ConnectionAborted, "AI is now dead.")); + } else if let Err(e) = ai_status { + error!("~[{}] Error: {}", ai.cli_id, e); + return Err(Error::new( + ErrorKind::ConnectionAborted, + "AI received error from server.", + )); + } + Ok(()) +} + pub async fn fork_ai(info: AI) -> io::Result<()> { let client = match handle_tcp(info.address.clone(), info.team.clone(), info.cli_id).await { Ok(client) => { @@ -129,48 +147,31 @@ pub async fn fork_ai(info: AI) -> io::Result<()> { "~[{}] Handling assignment of role {} with id {}", info.cli_id, role, p_id ); - let mut rle = init_from_broadcast(&ai, role) - .map_err(|e| std::io::Error::new(ErrorKind::NotFound, e))?; - if let Err(e) = rle.update().await { - println!("~[{}] Error: {}", info.cli_id, e); - } + let ai_clone = ai.clone(); + tokio::spawn(async move { + if init_and_update(ai_clone, role).await.is_ok() { + info!( + "~[{}] AI successfully initialized and updated.", + info.cli_id + ) + } + }); return Ok(()); } warn!( "~[{}] No role assignment detected, turning to idle...", info.cli_id ); + return Err(Error::new( + ErrorKind::NotFound, + "No role detected, AI is slowly dying.", + )); } Err(e) => error!("=[{}] {}", info.cli_id, e), } Ok(()) } -pub async fn connect_ai(mut ai: AI) -> io::Result<()> { - debug!("~[{}] AI is being checked in...", ai.cli_id); - if let Some((c_id, role, p_id)) = ai.clone().wait_assignment().await { - ai.set_p_id(p_id); - ai.set_cli_id(c_id + 1); - info!( - "~[{}] Handling assignment of role {} with connection id {}", - ai.cli_id, - role, - c_id + 1 - ); - let mut rle = init_from_broadcast(&ai, role) - .map_err(|e| std::io::Error::new(ErrorKind::NotFound, e))?; - if let Err(e) = rle.update().await { - println!("~[{}] Error: {}", ai.cli_id, e); - } - return Ok(()); - } - warn!( - "~[{}] No role assignment detected, turning to idle...", - ai.cli_id - ); - Ok(()) -} - #[async_trait] pub trait AIHandler: Send { fn init(info: AI) -> Self @@ -223,7 +224,7 @@ impl AI { async fn wait_assignment(&mut self) -> Option<(usize, String, usize)> { let mut client = self.client().lock().await; let start_time = time::Instant::now(); - let overall_timeout = Duration::from_secs(5); + let overall_timeout = Duration::from_secs(1); loop { if start_time.elapsed() >= overall_timeout { @@ -335,8 +336,7 @@ async fn checkout_ai_info( 1, client_number, ); - println!("~[{}] New! >> {}", c_id, ai); - debug!("~[{}] AI is initialized.", ai.cli_id); + println!("~[{}] New AI! >> {}", c_id, ai); ai }) .map_err(|e: Error| { @@ -355,8 +355,8 @@ async fn init_ai( let ai = checkout_ai_info(client, response, team, address, (c_id, p_id)).await?; let mut queen = queen::Queen::init(ai.clone()); if let Err(e) = queen.update().await { - println!("QUEEN received error {}", e); - error!("[{}] !!!!Error: {}", queen.info().cli_id, e); + error!("[{}] Error: {}", queen.info().cli_id, e); + println!("[{}] QUEEN received error.", c_id); } Ok(ai) } @@ -380,7 +380,7 @@ async fn start_ai( println!("~[{}] server> {}", c_id, response); match response.trim_end() { "ko" => { - debug!("~[{}] Server doesn't handle any more connection.", c_id); + warn!("~[{}] Server doesn't handle any more connection.", c_id); Err(Error::new( ErrorKind::ConnectionRefused, "No room for player.", @@ -399,7 +399,7 @@ async fn start_ai( } } } else { - debug!("=[{}] Host not reachable.", c_id); + error!("=[{}] Host not reachable.", c_id); Err(Error::new( ErrorKind::ConnectionRefused, "Couldn't reach host.", @@ -416,7 +416,7 @@ pub async fn launch(address: String, team: String) -> io::Result<()> { loop { if stop_flag.load(Ordering::SeqCst) { - println!( + warn!( "=[AT {:?}] Stop flag is set, breaking the loop.", connection_id ); @@ -429,7 +429,6 @@ pub async fn launch(address: String, team: String) -> io::Result<()> { let stop_flag = Arc::clone(&stop_flag); let curr_id = connection_id.load(Ordering::SeqCst); - println!("=[{}] Attempting connection...", curr_id); match handle_tcp(address.to_string(), team.to_string(), curr_id).await { Ok(client) => { @@ -448,11 +447,11 @@ pub async fn launch(address: String, team: String) -> io::Result<()> { match result { Ok(_) => { - println!("=[{}] Connection handled successfully", id); + info!("=[{}] Connection handled successfully", id); Ok(()) } Err(e) => { - println!("=[{}] Connection failed: {}", id, e); + warn!("=[{}] Connection failed: {}", id, e); stop_flag.store(true, Ordering::SeqCst); Err(e) } @@ -468,7 +467,7 @@ pub async fn launch(address: String, team: String) -> io::Result<()> { } if handles.is_empty() { - warn!("Connection refused, handles is empty."); + eprintln!("Connection refused, handles is empty."); return Err(Error::new( ErrorKind::ConnectionRefused, "Couldn't reach host.", @@ -477,7 +476,7 @@ pub async fn launch(address: String, team: String) -> io::Result<()> { for (id, handle) in handles.into_iter().enumerate() { if let Err(e) = handle.await { - println!("=[{}] Task failed: {:?}", id, e); + error!("=[{}] Task failed: {:?}", id, e); } } diff --git a/ai/src/ai/bot.rs b/ai/src/ai/bot.rs index b251632..124de43 100644 --- a/ai/src/ai/bot.rs +++ b/ai/src/ai/bot.rs @@ -24,7 +24,6 @@ use crate::{ }; use rand::Rng; -use std::mem::swap; use async_trait::async_trait; @@ -33,17 +32,10 @@ use log::{debug, error, info, warn}; use zappy_macros::Bean; -pub const COLONY_PLAYER_COUNT: usize = 2; +const REFILL_FOOD: usize = 8; + +pub const COLONY_PLAYER_COUNT: usize = 8; const MAX_MOVEMENTS: usize = 10; -static ITEM_PRIORITY: [(&str, usize); 7] = [ - ("food", 6), - ("linemate", 1), - ("deraumere", 2), - ("sibur", 3), - ("mendiane", 4), - ("phiras", 5), - ("thystame", 7), -]; #[derive(Debug, Clone, Bean)] pub struct Bot { @@ -55,7 +47,7 @@ pub struct Bot { #[async_trait] impl AIHandler for Bot { fn init(info: AI) -> Self { - println!("-[{}] BOT HERE.", info.cli_id); + println!("-[{}] BOT here.", info.cli_id); Self::new(info) } @@ -71,7 +63,7 @@ impl AIHandler for Bot { if idex > MAX_MOVEMENTS { self.backtrack().await?; self.drop_items().await?; - for _ in 0..5 { + for _ in 0..=REFILL_FOOD { let mut client = self.info().client().lock().await; take_object(&mut client, "food").await?; } @@ -80,106 +72,16 @@ impl AIHandler for Bot { } let random: usize = rand::thread_rng().gen_range(1..=3); self.move_to_tile(random).await?; - { - let mut client = self.info().client().lock().await; - if let Ok(ResponseResult::Tiles(tiles)) = look_around(&mut client).await { - if player_count_on_tile(&tiles[0]) < COLONY_PLAYER_COUNT - && tiles[0].last().unwrap_or(&String::from("player")) != "player" - { - take_object(&mut client, tiles[0].last().unwrap().as_str()).await?; - } - } - } + self.seek_objects().await?; idex += 1; } } } -fn get_item_index(item: &str, inv: &[(String, i32)]) -> Option { - for (i, elem) in inv.iter().enumerate() { - if elem.0.as_str() == item { - return Some(i); - } - } - None -} - -fn make_item_prioritised(item: &str) { - swap( - &mut ITEM_PRIORITY - .iter() - .max_by(|a, b| a.1.cmp(&b.1)) - .unwrap_or(&ITEM_PRIORITY[6]), - &mut ITEM_PRIORITY - .iter() - .find(|i| i.0 == item) - .unwrap_or(&("", 0)), - ) -} - -fn get_item_priority(item: &str) -> usize { - ITEM_PRIORITY - .iter() - .find(|i| i.0 == item) - .unwrap_or(&("", 0)) - .1 -} - -fn get_best_item_in_tile(tile: &[String], inv: &[(String, i32)]) -> (usize, String) { - let mut best: usize = 0; - for obj in tile { - if obj.as_str() == "player" { - continue; - } - let in_inv = inv.iter().position(|(item, _)| item == obj).unwrap_or(0); // check for (index out of bounds: the len is 0 but the index is 0) - if inv[in_inv].0 != inv[best].0 - && inv[in_inv].1 < 3 - && get_item_priority(obj) > get_item_priority(inv[best].0.as_str()) - { - best = in_inv; - } - } - (best, inv[best].0.clone()) -} - fn player_count_on_tile(tile: &[String]) -> usize { tile.iter().filter(|obj| obj.as_str() == "player").count() } -async fn seek_best_item_index( - client: &mut TcpClient, - tiles: Vec>, - best_item: &mut String, -) -> Result { - match inventory(client).await? { - ResponseResult::Inventory(inv) => { - let mut idex = 0; - let mut tile_idex = 0; - for (i, tile) in tiles.iter().enumerate() { - let (_, item) = get_best_item_in_tile(tile, &inv); - if get_item_priority(item.as_str()) > get_item_priority(inv[idex].0.as_str()) - && player_count_on_tile(tile) < COLONY_PLAYER_COUNT - { - idex = get_item_index(item.as_str(), &inv).unwrap_or(idex); - tile_idex = i; - best_item.clone_from(&item); - } - } - Ok(tile_idex) - } - _ => Err(CommandError::InvalidResponse), - } -} - -fn done_dropping_items(inv: &[(String, i32)]) -> bool { - for (item, count) in inv { - if item.as_str() != "food" && *count > 0 { - return false; - } - } - true -} - impl Bot { fn new(info: AI) -> Self { Self { @@ -219,28 +121,22 @@ impl Bot { } } - pub async fn seek_objects(&mut self) -> Result { - let res = { - let mut client = self.info().client().lock().await; - look_around(&mut client).await? - }; - match res { - ResponseResult::Tiles(tiles) => { - let mut best_item = String::new(); - let tile = { - let mut client = self.info().client().lock().await; - seek_best_item_index(&mut client, tiles, &mut best_item).await? - }; - if best_item.is_empty() { - Ok(ResponseResult::KO) + pub async fn seek_objects(&mut self) -> Result<(), CommandError> { + let mut client = self.info().client().lock().await; + if let Ok(ResponseResult::Tiles(tiles)) = look_around(&mut client).await { + if player_count_on_tile(&tiles[0]) < COLONY_PLAYER_COUNT + && tiles[0].last().unwrap_or(&String::from("player")) != "player" + { + if tiles[0].contains(&"phiras".to_owned()) { + take_object(&mut client, "phiras").await?; + } else if tiles[0].contains(&"sibur".to_owned()) { + take_object(&mut client, "sibur").await?; } else { - self.move_to_tile(tile).await?; - let mut client = self.info().client().lock().await; - take_object(&mut client, &best_item).await + take_object(&mut client, tiles[0].last().unwrap().as_str()).await?; } } - res => Ok(res), } + Ok(()) } pub async fn drop_items(&mut self) -> Result { @@ -252,7 +148,7 @@ impl Bot { count -= 1; } } - println!( + info!( "-[{}] Bot {} done dropping items.", self.info.cli_id, self.info.p_id ); @@ -285,7 +181,7 @@ impl Bot { async fn analyse_messages(&mut self) -> Result { let mut client = self.info().client().lock().await; while let Some(message) = client.pop_message() { - info!( + debug!( "-[{}] Bot [Queen {}]: handling message: {}", self.info().cli_id, self.info().p_id, @@ -320,16 +216,8 @@ async fn handle_queen_message( client: &mut TcpClient, (dir, msg): (DirectionMessage, &str), ) -> Result { - match msg { - "mv" => return move_towards_broadcast(client, dir).await, - "nf" => make_item_prioritised("food"), - "nl" => make_item_prioritised("linemate"), - "nd" => make_item_prioritised("deraumere"), - "ns" => make_item_prioritised("sibur"), - "nm" => make_item_prioritised("mendiane"), - "np" => make_item_prioritised("phiras"), - "nt" => make_item_prioritised("thystame"), - _ => {} + if msg == "mv" { + return move_towards_broadcast(client, dir).await; } Ok(ResponseResult::OK) } diff --git a/ai/src/ai/fetus.rs b/ai/src/ai/fetus.rs index 0225a3a..2df35d0 100644 --- a/ai/src/ai/fetus.rs +++ b/ai/src/ai/fetus.rs @@ -44,7 +44,7 @@ impl AIHandler for Fetus { total += 1; } Err(CommandError::DeadReceived) | Ok(ResponseResult::KO) => { - println!( + info!( "-[{}] AI : Fetus died and dropped x{} food.", self.info.cli_id, total ); diff --git a/ai/src/ai/knight.rs b/ai/src/ai/knight.rs index ab8670d..cac3386 100644 --- a/ai/src/ai/knight.rs +++ b/ai/src/ai/knight.rs @@ -9,16 +9,15 @@ use crate::{ ai::{fork_ai, AIHandler, Incantationers, Listeners, AI}, commands::{ broadcast::broadcast, - drop_object::drop_object, fork::fork, - incantation::{handle_incantation, incantation}, + incantation::{self, handle_incantation}, inventory::inventory, look_around::look_around, take_object::take_object, }, move_towards_broadcast::{backtrack_eject, move_towards_broadcast}, tcp::{ - command_handle::{CommandError, CommandHandler, ResponseResult}, + command_handle::{CommandError, CommandHandler, DirectionMessage, ResponseResult}, TcpClient, }, }; @@ -34,14 +33,15 @@ use tokio::sync::Mutex; use log::{debug, error, info, warn}; use zappy_macros::Bean; +const FETUS_NEED: usize = 2; +const MIN_FOOD_ON_FLOOR: usize = 200; + #[derive(Debug, Clone, Bean)] pub struct Knight { info: AI, level_ref: Arc>, } -const MIN_FOOD_ON_FLOOR: usize = 200; - #[async_trait] impl AIHandler for Knight { fn init(info: AI) -> Self { @@ -63,36 +63,8 @@ impl AIHandler for Knight { } } self.handle_message().await?; - - { - let mut client = self.info().client().lock().await; - self.check_enough_food(&mut client, 10).await?; - } - if self.can_incantate().await { - { - let mut client = self.info().client().lock().await; - println!( - "[{}] Knight {} incantating...", - self.info.cli_id, self.info.p_id - ); - let res = incantation(&mut client).await; - println!( - "[{}] Knight {} incantation result: {:?}", - self.info.cli_id, self.info.p_id, res - ); - if let Ok(ResponseResult::Incantation(lvl)) = - self.knight_checkout_response(&mut client, res).await - { - let mut level = self.level_ref.lock().await; - *level = lvl; - println!( - "[{}] Knight {} done. Now level {}", - self.info.cli_id, self.info.p_id, *level - ); - } - } - continue; - } + let mut client = self.info().client().lock().await; + self.check_enough_food(&mut client, 20).await?; } } } @@ -105,7 +77,10 @@ impl Incantationers for Knight { ) -> Result { if let Ok(ResponseResult::Eject(ref dir)) = res { if backtrack_eject(client, dir.clone()).await { - let response = client.check_response().await; + let response = client + .check_response() + .await + .ok_or(CommandError::NoResponseReceived)?; client.handle_response(response).await?; } } @@ -141,26 +116,10 @@ impl Knight { Self { info, level_ref } } - async fn die(&mut self, id: usize) { - let mut client_lock = self.info.client.lock().await; - let mut total = 0; - - loop { - let command = drop_object(&mut client_lock, "food").await; - if let Ok(ResponseResult::OK) = command { - total += 1; - } - if command.is_err() { - info!("-[{}] Knight dropped x{} food", self.info.cli_id, total); - info!("-[{}] AI : Knight has killed himself.", id); - break; - } - } - } - async fn check_food(&self, client: &mut TcpClient, min: usize) -> Result<(), CommandError> { - let res = look_around(client).await; - if let ResponseResult::Tiles(tiles) = self.knight_checkout_response(client, res).await? { + let res = look_around(client).await?; + if let ResponseResult::Tiles(tiles) = self.knight_checkout_response(client, Ok(res)).await? + { if !tiles.is_empty() && tiles[0] .iter() @@ -173,22 +132,26 @@ impl Knight { self.info().cli_id, self.info().p_id ); - let res = fork(client).await?; - if let ResponseResult::OK = self.knight_checkout_response(client, Ok(res)).await? { - let info = self.info.clone(); - tokio::spawn(async move { - if let Err(err) = fork_ai(info.clone()).await { - error!("-[{}] AI fork error: {}", info.cli_id, err); - } else { - println!("-[{}] AI successfully forked.", info.cli_id); - } - }); - broadcast( - client, - format!("{} assign Fetus {}", self.info().cli_id, self.info().p_id) - .as_str(), - ) - .await?; + for _ in 0..FETUS_NEED { + let res = fork(client).await?; + if let ResponseResult::OK = + self.knight_checkout_response(client, Ok(res)).await? + { + let info = self.info.clone(); + tokio::spawn(async move { + if let Err(err) = fork_ai(info.clone()).await { + error!("-[{}] AI fork error: {}", info.cli_id, err); + } else { + println!("-[{}] AI successfully forked.", info.cli_id); + } + }); + broadcast( + client, + format!("{} assign Fetus {}", self.info().cli_id, self.info().p_id) + .as_str(), + ) + .await?; + } } } } @@ -201,15 +164,15 @@ impl Knight { min: usize, ) -> Result<(), CommandError> { self.check_food(client, MIN_FOOD_ON_FLOOR).await?; - let mut res = inventory(client).await; + let mut res = inventory(client).await?; if let ResponseResult::Inventory(mut inv) = - self.knight_checkout_response(client, res).await? + self.knight_checkout_response(client, Ok(res)).await? { if !inv.is_empty() && inv[0].0 == "food" { - res = Ok(ResponseResult::OK); - while inv[0].1 < min as i32 && res == Ok(ResponseResult::OK) { - res = take_object(client, "food").await; - res = Knight::handle_eject(client, res).await; + res = ResponseResult::OK; + while inv[0].1 < min as i32 && res == ResponseResult::OK { + res = take_object(client, "food").await?; + res = Knight::handle_eject(client, Ok(res)).await?; inv[0].1 += 1; } } @@ -221,13 +184,12 @@ impl Knight { let mut client = self.info().client().lock().await; while let Some(message) = client.pop_message() { - println!( + debug!( "-[{}] Knight [Queen {}]: handling message: {}", self.info().cli_id, self.info().p_id, message.1 ); - self.check_enough_food(&mut client, 10).await?; let (dir, msg) = message; if !msg.contains(' ') || msg.len() < 2 { continue; @@ -236,18 +198,35 @@ impl Knight { let content = msg.split_at(idex); if let Ok(id) = content.0.parse::() { if id == self.info().p_id && content.1.trim_start() == "mv" { - let res = move_towards_broadcast(&mut client, dir).await; - self.knight_checkout_response(&mut client, res).await?; + info!( + "-[{}] Knight {} moving towards Queen...", + self.info.cli_id, self.info.p_id + ); + let res = move_towards_broadcast(&mut client, dir.clone()).await?; + self.knight_checkout_response(&mut client, Ok(res)).await?; } if id == self.info().p_id && content.1.trim_start() == "inc" && self.info().level != 1 + && dir.clone() == DirectionMessage::Center { - let response = client.check_response().await; - let res = client.handle_response(response).await; - println!( - "Knight {} received \"inc\" from Queen, read response: {:?}", - self.info.p_id, res + debug!( + "Knight {} received \"inc\" from Queen, waiting for response...", + self.info.p_id + ); + let res = incantation::wait_for_incantation(&mut client).await; + if let Err(err) = res { + error!( + "-[{}] Knight [Queen {}]: incantation error: {}", + self.info().cli_id, + self.info.p_id, + err + ); + return Err(err); + } + debug!( + "[{}] Knight {} received \"inc\" from Queen, read response: {:?}", + self.info.cli_id, self.info.p_id, res ); self.knight_checkout_response(&mut client, res).await?; } @@ -257,26 +236,6 @@ impl Knight { Ok(ResponseResult::OK) } - async fn can_incantate(&mut self) -> bool { - if self.info().level != 1 { - return false; - } - let mut client = self.info().client().lock().await; - let res = look_around(&mut client).await; - println!( - "-[{}] Knight {} Look returned: {:?}", - self.info.cli_id, self.info.p_id, res - ); - if let Ok(ResponseResult::Tiles(tiles)) = - self.knight_checkout_response(&mut client, res).await - { - if tiles[0].iter().any(|tile| tile.as_str() == "linemate") { - return true; - } - } - false - } - async fn knight_checkout_response( &self, client: &mut TcpClient, diff --git a/ai/src/ai/queen.rs b/ai/src/ai/queen.rs index 64bf3f7..d474bff 100644 --- a/ai/src/ai/queen.rs +++ b/ai/src/ai/queen.rs @@ -10,14 +10,14 @@ use crate::{ commands::{ broadcast::broadcast, fork::fork, - incantation::{handle_incantation, incantation, wait_for_incantation}, + incantation::{handle_incantation, incantation}, inventory::inventory, look_around::look_around, - move_up::move_up, take_object::take_object, + turn::{turn, DirectionTurn}, }, elevation::{Config, Inventory}, - move_towards_broadcast::{backtrack_eject, move_towards_broadcast, turn_towards_broadcast}, + move_towards_broadcast::{backtrack_eject, move_towards_broadcast}, tcp::{ command_handle::{CommandError, CommandHandler, DirectionMessage, ResponseResult}, TcpClient, @@ -37,7 +37,8 @@ use zappy_macros::Bean; use super::Listeners; const NB_INIT_BOTS: usize = 2; -const QUEENS_IDS: [usize; 4] = [2, 1, 4, 3]; +const NB_INIT_QUEENS: usize = 8; +const QUEENS_IDS: [usize; 4] = [1, 0, 3, 2]; #[derive(Debug, Clone, Default)] struct LookInfo { @@ -64,142 +65,33 @@ impl AIHandler for Queen { Self::new(info) } - // async fn update(&mut self) -> Result<(), CommandError> { - // { - // let mut client = self.info().client().lock().await; - // if let Err(CommandError::DeadReceived) = client.get_broadcast().await { - // return Err(CommandError::DeadReceived); - // } - // } - // if let Err(CommandError::DeadReceived) = self.handle_message().await { - // return Err(CommandError::DeadReceived); - // } - // if let Err(CommandError::DeadReceived) = self.fork_servants().await { - // return Err(CommandError::DeadReceived); - // } - // loop { - // if let Err(CommandError::DeadReceived) = self.handle_message().await { - // break; - // } - // if let Err(CommandError::DeadReceived) = self.check_move_elevation().await { - // break; - // } - - // let look_res = { - // let mut cli = self.info.client.lock().await; - // println!("Queen {} calling Look...", self.info.p_id); - // let res = look_around::look_around(&mut cli).await; - // println!("Queen {} Look returned: {:?}", self.info.p_id, res); - // Queen::queen_checkout_response(&mut cli, res).await - // }; - // if let Ok(ResponseResult::Tiles(vec)) = look_res { - // self.convert_to_look_info(vec[0].clone()); - // } - - // let inventory_res = { - // let mut cli = self.info.client.lock().await; - // println!("Queen {} calling Inventory...", self.info.p_id); - // let res = inventory::inventory(&mut cli).await; - // println!("Queen {} Inventory returned: {:?}", self.info.p_id, res); - // Queen::queen_checkout_response(&mut cli, res).await - // }; - // if let Ok(ResponseResult::Inventory(vec)) = inventory_res { - // self.convert_to_inv(vec); - // } - - // if let Err(CommandError::DeadReceived) = self.check_enough_food(40).await { - // break; - // } - - // if let Err(CommandError::DeadReceived) = self.create_bot().await { - // break; - // } - - // if self.check_requirement() { - // println!("Ai Queen #{} is incantating", self.info.p_id); - // if let Err(e) = self.incantate().await { - // warn!("Error from incantation: {}", e); - // println!("Error with Queen #{} incantating.", self.info.p_id); - // if e == CommandError::DeadReceived || e == CommandError::RequestError { - // break; - // } - // } - // } - // } - // Err(CommandError::DeadReceived) - // } - async fn update(&mut self) -> Result<(), CommandError> { - { - let mut client = self.info().client().lock().await; - let info = self.info().clone(); - info!( - "[{}] Blocking, checking requirements of all queens...", - info.cli_id - ); - debug!( - "[{}] Unused slot checked: {} | Number of queens created: {}", - info.cli_id, - info.slots, - info.cli_id + 1 - ); - if info.slots == 0 && info.cli_id < 3 { - Queen::spawn_queen(info.clone(), info.cli_id, &mut client).await?; - info!("[{}] Spawned queen.", info.cli_id); - } - if info.slots >= 0 && info.cli_id > 3 { - info!("[{}] Identified as NPC.", info.cli_id); - Queen::connect_leftovers(info.clone()).await?; - } - info!("[{}] Unblocked.", info.cli_id); - } - self.info.set_p_id(self.info().cli_id); - println!( - "[{}] Queen is now certified and verified.", - self.info.cli_id - ); - - self.handle_message().await?; + self.start_queen().await?; + self.join_queens().await?; self.fork_servants().await?; - loop { - self.handle_message().await?; - self.check_move_elevation().await?; - - let look_res = { - let mut cli = self.info.client.lock().await; - let res = look_around(&mut cli).await; - Queen::handle_eject(&mut cli, res).await - }; - if let Ok(ResponseResult::Tiles(vec)) = look_res { - self.convert_to_look_info(vec[0].clone()); - } + println!( + "[{}] Queen {} ready to go!", + self.info.cli_id, self.info.p_id + ); - let inventory_res = { - let mut cli = self.info.client.lock().await; - let res = inventory(&mut cli).await; - Queen::handle_eject(&mut cli, res).await - }; - if let Ok(ResponseResult::Inventory(vec)) = inventory_res { - self.convert_to_inv(vec); - } + while self.info.level < 8 { + self.get_look_infos().await?; + self.get_inv_infos().await?; - self.check_enough_food(5).await?; + self.check_enough_food(30).await?; if self.check_requirement() { - println!( - "[{}] Ai Queen #{} is incantating", - self.info.cli_id, self.info.p_id - ); - if let Err(e) = self.incantate().await { - warn!("[{}] Error from incantation: {}", self.info.cli_id, e); - println!( - "[{}] Error with Queen #{} incantating.", - self.info.cli_id, self.info.p_id - ); - } + self.incantate().await?; } } + error!( + "[{}] Queen {} lvl {}", + self.info.cli_id, self.info.p_id, self.info.level + ); + loop { + self.check_enough_food(30).await?; + } } } @@ -211,7 +103,10 @@ impl Incantationers for Queen { ) -> Result { if let Ok(ResponseResult::Eject(ref dir)) = res { if backtrack_eject(client, dir.clone()).await { - let response = client.check_response().await; + let response = client + .check_response() + .await + .ok_or(CommandError::NoResponseReceived)?; client.handle_response(response).await?; } } @@ -234,11 +129,75 @@ impl Incantationers for Queen { #[async_trait] impl Listeners for Queen { async fn handle_message(&mut self) -> Result { - self.analyse_messages().await + Ok(ResponseResult::OK) } } impl Queen { + /// + /// Meant to initialize the connected Queen, if there is no more room for players, new Queens are forked. + /// If there is too many room for players, Queen just dies to get rid of leftover eggs. + /// + async fn start_queen(&mut self) -> Result<(), CommandError> { + { + let mut client = self.info().client().lock().await; + let info = self.info().clone(); + info!( + "[{}] Blocking, checking requirements of all queens...", + info.cli_id + ); + debug!( + "[{}] Unused slot checked: {} | Number of queens created: {}", + info.cli_id, + info.slots, + info.cli_id + 1 + ); + if info.slots == 0 && info.cli_id < NB_INIT_QUEENS - 1 { + Queen::spawn_queen(info.clone(), info.cli_id, &mut client).await?; + info!("[{}] Spawned queen.", info.cli_id); + } + if info.slots >= 0 && info.cli_id > NB_INIT_QUEENS - 1 { + info!("[{}] Identified as NPC.", info.cli_id); + Queen::connect_leftovers(info.clone()).await?; + } + info!("[{}] Unblocked.", info.cli_id); + } + self.info.set_p_id(self.info().cli_id); + println!( + "[{}] Queen {} is now certified and verified.", + self.info.cli_id, self.info.p_id + ); + Ok(()) + } + + /// + /// All Queens move towards first Queen logged in (p_id 0). + /// + /// First Queen logged in just broadcasts. + /// + async fn join_queens(&mut self) -> Result<(), CommandError> { + if self.info.p_id == 0 { + let mut client = self.info.client.lock().await; + broadcast(&mut client, format!("{} waiting", self.info.p_id).as_str()).await?; + } else { + let mut client = self.info.client.lock().await; + println!( + "[{}] Queen {} moving towards Queen 0.", + self.info.cli_id, self.info.p_id + ); + loop { + if let ResponseResult::Message((dir, msg)) = client.get_broadcast().await? { + if dir == DirectionMessage::Center && msg.contains('0') { + break; + } + move_towards_broadcast(&mut client, dir.clone()).await?; + } + } + } + Ok(()) + } + + /// Checks for an "eject" of "Elevation underway" response async fn queen_checkout_response( &self, client: &mut TcpClient, @@ -272,9 +231,8 @@ impl Queen { async fn spawn_queen(info: AI, id: usize, client: &mut TcpClient) -> Result<(), CommandError> { let info_clone = info.clone(); - move_up(client).await?; fork(client).await?; - inventory(client).await?; + turn(client, DirectionTurn::Left).await?; tokio::spawn(async move { if let Err(err) = fork_ai(info_clone).await { error!("[{}] AI executing task fork error: {}", info.cli_id, err); @@ -309,145 +267,70 @@ impl Queen { } /// - /// Move [`Queen`] at level 4, - /// we assume that all the queens have the same direction + /// Launches Queen's incantation process to level up /// - async fn move_queen_first_step(&mut self) -> Result<(), CommandError> { - println!("Queen {} handling can move.", self.info.p_id); - if self.info.p_id == 2 || self.info.p_id == 4 { - println!( - "Queen {} level {} broadcasting...", - self.info.p_id, self.info.level - ); - { - let mut client = self.info().client().lock().await; - broadcast( - &mut client, - format!("{} waiting", self.info().p_id).as_str(), - ) - .await?; - } - println!( - "Queen {} level {} broadcast \"waiting\".", - self.info.p_id, self.info.level - ); - self.set_can_move(false); - self.set_moved_lvl4(true); - return Ok(()); - } else if self.partner_dir.is_some() { - { - let mut client = self.info().client().lock().await; - move_towards_broadcast( - &mut client, - self.partner_dir.clone().unwrap_or(DirectionMessage::Center), - ) - .await?; - broadcast(&mut client, format!("{} mv", self.info().p_id).as_str()).await?; - println!("Queen {} level {} moved!", self.info.p_id, self.info.level); - } - self.set_can_move(false); - self.set_partner_dir(None); - } else { - println!( - "Queen {} level {} trying to receive broadcast \"waiting\".", - self.info.p_id, self.info.level - ); - self.analyse_messages().await?; - } - //{ - // let mut cli = self.info.client.lock().await; - // move_up::move_up(&mut cli).await?; - // let broad_res = - // broadcast::broadcast(&mut cli, format!("{} mv", self.info.p_id).as_str()) - // .await?; - // Queen::queen_checkout_response(&mut cli, Ok(broad_res)).await?; - //} - Ok(()) - } - - /// - /// Move [`Queen`] at level 6, we will move queen's direction and then reunite them in a single tile - /// - async fn move_queen_second_step(&mut self) -> Result<(), CommandError> { - { - let mut client = self.info().client().lock().await; - broadcast( - &mut client, - format!("{} waiting", self.info().p_id).as_str(), - ) - .await?; - } - self.analyse_messages().await?; - Ok(()) - } - - async fn check_move_elevation(&mut self) -> Result<(), CommandError> { - if !self.can_move() { - return Ok(()); - } - match self.info.level { - 4 => self.move_queen_first_step().await, - 6 => self.move_queen_second_step().await, - _ => Ok(()), - } - } - async fn incantate(&mut self) -> Result<(), CommandError> { let mut level = self.info().level; { let mut cli = self.info.client.lock().await; - broadcast(&mut cli, format!("{} inc", self.info().p_id).as_str()).await?; println!( - "[{}] Ai Queen #{} launching incantation", + "[{}] Queen {} launching incantation", self.info.cli_id, self.info.p_id ); - let incant_res = incantation(&mut cli).await; + let incant_res = incantation(&mut cli).await?; println!( - "[{}] Ai Queen #{} done incantating.", + "[{}] Queen {} done incantating.", self.info.cli_id, self.info.p_id ); - println!( - "Queen {} Incantation result: {:?}", - self.info.p_id, incant_res - ); if let Ok(ResponseResult::Incantation(lvl)) = - self.queen_checkout_response(&mut cli, incant_res).await + self.queen_checkout_response(&mut cli, Ok(incant_res)).await { level = lvl; - println!("Queen {} done. Now level {}", self.info.p_id, level); + println!( + "[{}] Queen {} done. Now level {}", + self.info.cli_id, self.info.p_id, level + ); if level == 4 || level == 6 { - error!( + info!( "[{}] Queen {} lvl {}", self.info.cli_id, self.info.p_id, level ); - broadcast( - &mut cli, - format!("{} lvl {}", self.info().p_id, level).as_str(), - ) - .await?; } - } - if level == 4 || level == 6 { - error!( - "[{}] Queen {} lvl {}", - self.info.cli_id, self.info.p_id, level - ); - broadcast( - &mut cli, - format!("{} lvl {}", self.info().p_id, level).as_str(), - ) - .await?; + self.create_bot(&mut cli).await?; } } self.info.set_level(level); Ok(()) } + /// + /// Checks if Queen has enough food compared to `min` + /// + /// If the amount held is lower than `min`, she picks up food + /// async fn check_enough_food(&mut self, min: usize) -> Result<(), CommandError> { while *self.inv.food() < min { - let mut cli = self.info.client.lock().await; - if let Ok(ResponseResult::OK) = take_object(&mut cli, "food").await { - self.inv.set_food(self.inv.food() + 1); + let res = { + let mut cli = self.info.client.lock().await; + let res = take_object(&mut cli, "food").await; + self.queen_checkout_response(&mut cli, res).await? + }; + match res { + ResponseResult::OK => self.inv.set_food(self.inv.food() + 1), + ResponseResult::Incantation(lvl) => { + self.info.set_level(lvl); + if self.info.level == 4 || self.info.level == 6 { + error!( + "[{}] Queen {} lvl {}", + self.info.cli_id, self.info.p_id, self.info.level + ); + } + println!( + "[{}] Queen {} done. Now level {}", + self.info.cli_id, self.info.p_id, self.info.level + ); + } + _ => {} } } Ok(()) @@ -475,40 +358,41 @@ impl Queen { self.info.cli_id, self.info.p_id ); - for _ in 0..NB_INIT_BOTS { - fork(&mut cli).await?; - let info = self.info.clone(); - tokio::spawn(async move { - if let Err(err) = fork_ai(info.clone()).await { - error!("[{}] AI fork error: {}", info.cli_id, err); - } else { - println!("[{}] AI successfully forked.", info.cli_id); - } - }); - broadcast( - &mut cli, - format!("{} assign Bot {}", self.info().cli_id, self.info().p_id).as_str(), - ) - .await?; - } - info!("[{}] Miserable peasants... SERVE ME.\n", self.info.cli_id); + fork(&mut cli).await?; + let info = self.info.clone(); + tokio::spawn(async move { + if let Err(err) = fork_ai(info.clone()).await { + error!("[{}] AI fork error: {}", info.cli_id, err); + } else { + println!("[{}] AI successfully forked.", info.cli_id); + } + }); + broadcast( + &mut cli, + format!("{} assign Bot {}", self.info().cli_id, self.info().p_id).as_str(), + ) + .await?; Ok(()) } + /// + /// Checks if Queen can level up based on `self.look` and `self.inv` infos + /// fn check_requirement(&self) -> bool { - let idx = self.info.level - 1; + let idx = if self.info.level == 8 { + 0 + } else { + self.info.level - 1 + }; let require = &self.requirement.elevation[idx]; let r_inv = require.inv(); let look = &self.look; - if self.info().level >= 4 - && self.moved_lvl4 - && (self.info().p_id == 1 || self.info().p_id == 3) - { + if self.info.level >= 4 && self.info.p_id % 3 != 0 { return false; } - if self.info().level >= 6 && self.moved_lvl6 && (self.info().p_id == 2) { + if self.info.level >= 6 && self.info.p_id % 6 != 0 { return false; } @@ -522,7 +406,39 @@ impl Queen { && look.inv.thystame() >= r_inv.thystame() } - fn convert_to_look_info(&mut self, vec: Vec) { + /// + /// Transform Look info to exploit them later + /// + async fn get_look_infos(&mut self) -> Result<(), CommandError> { + let res = { + let mut cli = self.info.client.lock().await; + let res = look_around(&mut cli).await; + self.queen_checkout_response(&mut cli, res).await? + }; + let vec = match &res { + ResponseResult::Incantation(lvl) => { + self.info.set_level(*lvl); + if self.info.level == 4 || self.info.level == 6 { + warn!( + "[{}] Queen {} lvl {}", + self.info.cli_id, self.info.p_id, self.info.level + ); + } + println!( + "[{}] Queen {} done. Now level {}", + self.info.cli_id, self.info.p_id, self.info.level + ); + return Ok(()); + } + ResponseResult::Tiles(tiles) => { + if tiles.is_empty() { + return Ok(()); + } + tiles[0].clone() + } + _ => return Ok(()), + }; + self.look.inv.clear(); let inv: &mut Inventory = &mut self.look.inv; @@ -540,12 +456,42 @@ impl Queen { _ => (), } } + Ok(()) } /// /// Transform Inventory info to exploit them later. /// - fn convert_to_inv(&mut self, vec: Vec<(String, i32)>) { + async fn get_inv_infos(&mut self) -> Result<(), CommandError> { + let res = { + let mut cli = self.info.client.lock().await; + let res = inventory(&mut cli).await?; + self.queen_checkout_response(&mut cli, Ok(res)).await? + }; + let vec: Vec<(String, i32)> = match &res { + ResponseResult::Incantation(lvl) => { + self.info.set_level(*lvl); + if self.info.level == 4 || self.info.level == 6 { + error!( + "[{}] Queen {} lvl {}", + self.info.cli_id, self.info.p_id, self.info.level + ); + } + println!( + "[{}] Queen {} done. Now level {}", + self.info.cli_id, self.info.p_id, self.info.level + ); + return Ok(()); + } + ResponseResult::Inventory(inv) => { + if inv.is_empty() { + return Ok(()); + } + inv.clone() + } + _ => return Ok(()), + }; + for elem in vec.iter() { match elem.0.as_str() { "food" => self.inv.set_food(elem.1 as usize), @@ -558,138 +504,12 @@ impl Queen { _ => (), } } + Ok(()) } - fn is_paired_queen(&self, id: usize, lvl: i32) -> bool { - error!("[{}] id = {}, level = {}", self.info.cli_id, id, lvl); - (lvl == 4 && id == QUEENS_IDS[self.info().p_id - 1]) - || (lvl == 6 - && ((id == 1 | 2 && self.info().p_id == 3 | 4) - || (id == 3 | 4 && self.info().p_id == 1 | 2))) - } - - async fn handle_message_content( - &self, - client: &mut TcpClient, - id: usize, - (dir, msg): (DirectionMessage, &str), - can_move: &mut bool, - level: &mut usize, - save_dir: &mut Option, - ) -> Result { - match msg { - level if level.starts_with("lvl ") => { - if let Ok(lvl) = msg.split_at(3).1.trim_start().parse::() { - if self.is_paired_queen(id, lvl) { - *can_move = true; - println!( - "Queen {} can now move towards Queen {}!", - self.info.p_id, id - ); - } - } - } - "Done" => { - turn_towards_broadcast(client, dir.clone()).await?; - } - "waiting" => { - if self.is_paired_queen(id, self.info().level as i32) { - if (self.info().level == 4 && self.moved_lvl4) - || (self.info().level == 6 && self.moved_lvl6) - { - println!( - "Queen {} level {} already moved.", - self.info.p_id, self.info.level - ); - return Ok(ResponseResult::OK); - } - if self.info().level == 4 && (self.info().p_id == 2 || self.info().p_id == 4) { - println!("Queen {} is already waiting.", self.info.p_id); - return Ok(ResponseResult::OK); - } - if !self.can_move - && ((self.info.p_id == 1 && id == 2) || (self.info.p_id == 3 && id == 4)) - { - *save_dir = Some(dir.clone()); - return Ok(ResponseResult::OK); - } - move_towards_broadcast(client, dir).await?; - broadcast(client, format!("{} mv", self.info().p_id).as_str()).await?; - println!("Queen {} level {} moved!", self.info.p_id, self.info.level); - *can_move = false; - } - } - "inc" => { - if (self.info.level == 4 || self.info.level == 5) - && self.moved_lvl4 - && ((self.info.p_id == 1 && id == 2) || (self.info.p_id == 3 && id == 4)) - { - if let ResponseResult::Incantation(lvl) = wait_for_incantation(client).await? { - *level = lvl; - } - } - if (self.info.level == 6 || self.info.level == 7) - && self.moved_lvl6 - && ((self.info.p_id >= 1 && self.info.p_id <= 3) && id == 4) - { - if let ResponseResult::Incantation(lvl) = wait_for_incantation(client).await? { - *level = lvl; - } - } - } - _ => {} - } - Ok(ResponseResult::OK) - } - - async fn analyse_messages(&mut self) -> Result { - let mut can_move = self.can_move; - let mut level = self.info().level; - let mut save_dir: Option = None; - { - let mut client = self.info.client.lock().await; - while let Some((dir, msg)) = client.pop_message() { - info!("Queen {}: handling message: {}", self.info().p_id, msg); - let content = if let Some(idex) = msg.trim_end_matches('\n').find(' ') { - msg.split_at(idex) - } else { - ("0", msg.trim_end_matches('\n')) - }; - if let Ok(id) = content.0.parse::() { - self.handle_message_content( - &mut client, - id, - (dir.clone(), content.1.trim_start()), - &mut can_move, - &mut level, - &mut save_dir, - ) - .await?; - } - } - } - if self.can_move && !can_move { - if self.info().level == 4 { - println!("Queen {} set moved lvl 4 to true.", self.info.p_id); - self.set_moved_lvl4(true); - } - if self.info().level == 6 { - println!("Queen {} set moved lvl 6 to true.", self.info.p_id); - self.set_moved_lvl6(true); - } - } - if save_dir.is_some() { - self.set_partner_dir(save_dir); - } - self.info.set_level(level); - self.set_can_move(can_move); - Ok(ResponseResult::OK) - } - - async fn create_bot(&mut self) -> Result { - let mut client = self.info().client().lock().await; - let res = fork(&mut client).await; - if let Ok(ResponseResult::OK) = self.queen_checkout_response(&mut client, res).await { + async fn create_bot(&self, client: &mut TcpClient) -> Result { + let res = fork(client).await?; + if let Ok(ResponseResult::OK) = self.queen_checkout_response(client, Ok(res)).await { let info = self.info.clone(); tokio::spawn(async move { if let Err(err) = fork_ai(info.clone()).await { @@ -699,7 +519,7 @@ impl Queen { } }); broadcast( - &mut client, + client, format!("{} assign Bot {}", self.info().cli_id, self.info().p_id).as_str(), ) .await?; diff --git a/ai/src/commands/incantation.rs b/ai/src/commands/incantation.rs index fecd1a0..72d8b1e 100644 --- a/ai/src/commands/incantation.rs +++ b/ai/src/commands/incantation.rs @@ -41,8 +41,15 @@ pub async fn handle_incantation(client: &mut TcpClient) -> Result Result { debug!("Waiting for incantation..."); - let response = client.check_response().await; - let res = client.handle_response(response).await?; + let response = client + .check_response() + .await + .ok_or(CommandError::NoResponseReceived); + if let Err(CommandError::NoResponseReceived) = response { + let response = client.check_dead("Incantation\n").await?; + return client.handle_response(response).await; + } + let res = client.handle_response(response.unwrap()).await?; if res == ResponseResult::Elevating { return handle_incantation(client).await; } @@ -59,8 +66,12 @@ pub async fn incantation(client: &mut TcpClient) -> Result Vec<(String, i32)> { } pub async fn inventory(client: &mut TcpClient) -> Result { - debug!("Checking inventory..."); - let mut response = client.check_dead("Inventory\n").await?; loop { let res = client.handle_response(response).await?; if let ResponseResult::Inventory(_) = res { return Ok(res); } - response = client.check_response().await; + response = client + .check_response() + .await + .ok_or(CommandError::NoResponseReceived)?; } } diff --git a/ai/src/commands/look_around.rs b/ai/src/commands/look_around.rs index 1ebfb3b..4d1669c 100644 --- a/ai/src/commands/look_around.rs +++ b/ai/src/commands/look_around.rs @@ -32,8 +32,6 @@ pub fn read_look_output(raw: String) -> Vec> { acc }, ); - - debug!("Tiles: {:?}", tiles); tiles } @@ -46,7 +44,10 @@ pub async fn look_around(client: &mut TcpClient) -> Result 0 { move_right(client).await?; } - move_up::move_up(client).await + move_up::move_up(client).await?; + if y < 0 { + turn::turn(client, DirectionTurn::Right).await?; + turn::turn(client, DirectionTurn::Right).await?; + } + Ok(ResponseResult::OK) } async fn undo_eject(client: &mut TcpClient, x: i32) -> bool { @@ -97,7 +102,7 @@ async fn undo_eject(client: &mut TcpClient, x: i32) -> bool { } pub async fn backtrack_eject(client: &mut TcpClient, dir: DirectionEject) -> bool { - info!("Backtracking back from ejection {}...", dir); + warn!("Backtracking back from ejection {}...", dir); let (mut x, y) = get_eject_coordinates(&dir); if y < 0 { x = -x; diff --git a/ai/src/tcp.rs b/ai/src/tcp.rs index dfd5edc..9acae95 100644 --- a/ai/src/tcp.rs +++ b/ai/src/tcp.rs @@ -96,7 +96,7 @@ impl TcpClient { if let Some(receiver) = &mut self.response_receiver { receiver.recv().await } else { - warn!( + error!( "=[{}] No response received, response receiver not available.", self.id ); diff --git a/ai/src/tcp/command_handle.rs b/ai/src/tcp/command_handle.rs index ef87bc4..27ab38c 100644 --- a/ai/src/tcp/command_handle.rs +++ b/ai/src/tcp/command_handle.rs @@ -78,7 +78,7 @@ pub trait CommandHandler { async fn send_command(&mut self, command: &str) -> Result; async fn check_dead(&mut self, command: &str) -> Result; async fn handle_response(&mut self, response: String) -> Result; - async fn check_response(&mut self) -> String; + async fn check_response(&mut self) -> Option; async fn get_broadcast(&mut self) -> Result; } @@ -90,19 +90,19 @@ impl CommandHandler for TcpClient { } match self.get_response().await { Some(res) => Ok(res), - None => Ok(String::from("")), + None => Err(CommandError::NoResponseReceived), } } - async fn check_response(&mut self) -> String { + async fn check_response(&mut self) -> Option { match self.get_response().await { Some(res) => { debug!("Response checked gives: ({})", res); - res + Some(res) } None => { warn!("No response received."); - String::from("") + None } } } @@ -117,8 +117,10 @@ impl CommandHandler for TcpClient { } async fn get_broadcast(&mut self) -> Result { - warn!("[{}] from here get broadcast", self.id); - let res = self.check_response().await; + let res = self + .check_response() + .await + .ok_or(CommandError::NoResponseReceived)?; if res.starts_with("message ") { if let ResponseResult::Message(msg) = handle_message_response(res.clone(), self.crypt())? @@ -134,9 +136,12 @@ impl CommandHandler for TcpClient { if response.starts_with("message ") { if let ResponseResult::Message(msg) = handle_message_response(response, self.crypt())? { self.push_message(msg); - debug!("Message pushed to queue."); + debug!("~[{}] Message pushed to queue.", self.id); } - let response = self.check_response().await; + let response = self + .check_response() + .await + .ok_or(CommandError::NoResponseReceived)?; return self.handle_response(response).await; } @@ -162,8 +167,8 @@ impl CommandHandler for TcpClient { }, x if x.starts_with("ko\n") => Ok(ResponseResult::KO), _ => { - warn!("Invalid Response: ({}).", response.trim_end()); - Ok(ResponseResult::Unknown) + error!("Invalid Response: ({}).", response.trim_end()); + Ok(ResponseResult::KO) } } } diff --git a/server/core/src/ai/commands/incantation_end.c b/server/core/src/ai/commands/incantation_end.c index ff98895..45c64f9 100644 --- a/server/core/src/ai/commands/incantation_end.c +++ b/server/core/src/ai/commands/incantation_end.c @@ -104,7 +104,7 @@ static void end_ais_elevation( for (size_t i = 0; i < clis->size; i++) { oth = clis->data[i]; - if (oth->type == GUI) + if (oth->type != AI) continue; if (cli->ai->id != oth->ai->id && is_coord_equal(&cli->ai->pos, &oth->ai->pos) && diff --git a/server/core/src/ai/commands/incantation_start.c b/server/core/src/ai/commands/incantation_start.c index 022770d..46c8cd1 100644 --- a/server/core/src/ai/commands/incantation_start.c +++ b/server/core/src/ai/commands/incantation_start.c @@ -19,7 +19,11 @@ #include "logger.h" #include "utils.h" -static size_t count_nb_ai_available(game_t *g, struct tile_s *tile) +static size_t count_nb_ai_available( + game_t *g, + struct tile_s *tile, + size_t level +) { struct vector_ai_t *ais = g->ais; ai_t *ai = NULL; @@ -28,7 +32,8 @@ static size_t count_nb_ai_available(game_t *g, struct tile_s *tile) for (size_t i = 0; i < ais->size; i++) { ai = ais->data[i]; for (size_t k = 0; k < tile->players->size; k++) { - count += ai->id == tile->players->data[k] && !ai->incant.is_incant; + count += ai->id == tile->players->data[k] && + !ai->incant.is_incant && ai->level == level; } } return count; @@ -38,8 +43,9 @@ static bool verif_start_specification(ai_t *ai, game_t *game) { pos_t *pos = &ai->pos; size_t idx = ai->level - 1; - size_t nb_player = - count_nb_ai_available(game, &game->map->arena[pos->y][pos->x]); + size_t nb_player = count_nb_ai_available( + game, &game->map->arena[pos->y][pos->x], ai->level + ); if (nb_player < incant_req[idx].nb_player) { logs(DEBUG, "Not enough player for start_incantation.\n"); @@ -57,17 +63,23 @@ static bool verif_start_specification(ai_t *ai, game_t *game) static void start_ais_elevation(struct client_list *clis, client_t *cli) { client_t *oth = NULL; + size_t idx = cli->ai->level - 1; + size_t n = 0; - for (size_t i = 0; i < clis->size; i++) { + for (size_t i = 0; n < incant_req[idx].nb_player - 1 && i < clis->size; + i++) { oth = clis->data[i]; if (oth->type != AI || oth->ai->id == cli->ai->id) continue; if (is_coord_equal(&cli->ai->pos, &oth->ai->pos) && !oth->ai->incant.is_incant && oth->ai->level == cli->ai->level) { + n++; freeze_ai(oth->ai, cli->ai->id); prepare_response_cat(&oth->io, "Elevation underway\n"); } } + freeze_ai(cli->ai, cli->ai->id); + prepare_response_cat(&cli->io, "Elevation underway\n"); } static void send_incantation_start(client_t *cli, struct client_list *clients) @@ -93,6 +105,8 @@ void handle_start_incantation(client_t *cli, command_state_t *s) { str_t *end_incant = NULL; + if (cli->ai->incant.is_incant) + return prepare_response_cat(&cli->io, "ko\n"); cli->ai->incant.last_verif = verif_start_specification(cli->ai, s->game); if (cli->ai->incant.last_verif) { start_ais_elevation(s->clients, cli);