Can play full game
This commit is contained in:
		| @@ -77,6 +77,32 @@ pub async fn run_client(server: &str, rules: &GameRules) -> Result<(), Box<dyn E | ||||
|                     )?)) | ||||
|                     .await?; | ||||
|             } | ||||
|             ServerMessage::StrikeResult { pos, result } => { | ||||
|                 log::debug!("Strike at {} result: {:?}", pos.human_print(), result) | ||||
|             } | ||||
|             ServerMessage::OpponentStrikeResult { pos, result } => log::debug!( | ||||
|                 "Opponent trike at {} result: {:?}", | ||||
|                 pos.human_print(), | ||||
|                 result | ||||
|             ), | ||||
|             ServerMessage::LostGame { | ||||
|                 your_map, | ||||
|                 opponent_map, | ||||
|             } => { | ||||
|                 log::debug!("We lost game :("); | ||||
|                 log::debug!("Other game:\n{}\n", opponent_map.get_map()); | ||||
|                 log::debug!("Our game:\n{}\n", your_map.get_map()); | ||||
|                 break; | ||||
|             } | ||||
|             ServerMessage::WonGame { | ||||
|                 your_map, | ||||
|                 opponent_map, | ||||
|             } => { | ||||
|                 log::debug!("We won the game !!!!"); | ||||
|                 log::debug!("Other game:\n{}\n", opponent_map.get_map()); | ||||
|                 log::debug!("Our game:\n{}\n", your_map.get_map()); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -17,3 +17,5 @@ pub const MULTI_PLAYER_MAP_HEIGHT: usize = 10; | ||||
| pub const MULTI_PLAYER_BOATS_CAN_TOUCH: bool = true; | ||||
| pub const MULTI_PLAYER_PLAYER_CAN_CONTINUE_AFTER_HIT: bool = true; | ||||
| pub const MULTI_PLAYER_PLAYER_BOATS: [usize; 5] = [2, 3, 3, 4, 5]; | ||||
|  | ||||
| pub const ALPHABET: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||||
|   | ||||
| @@ -2,9 +2,10 @@ use std::io::ErrorKind; | ||||
|  | ||||
| use rand::{Rng, RngCore}; | ||||
|  | ||||
| use crate::consts::ALPHABET; | ||||
| use crate::data::GameRules; | ||||
|  | ||||
| #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy)] | ||||
| #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, Eq, PartialEq)] | ||||
| pub enum BoatDirection { | ||||
|     Left, | ||||
|     Right, | ||||
| @@ -95,9 +96,20 @@ impl Coordinates { | ||||
|             && self.x < rules.map_width as i32 | ||||
|             && self.y < rules.map_height as i32 | ||||
|     } | ||||
|  | ||||
|     pub fn human_print(&self) -> String { | ||||
|         format!( | ||||
|             "{}:{}", | ||||
|             match self.y < 0 || self.y >= ALPHABET.len() as i32 { | ||||
|                 true => self.y.to_string(), | ||||
|                 false => ALPHABET.chars().nth(self.y as usize).unwrap().to_string(), | ||||
|             }, | ||||
|             self.x | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy)] | ||||
| #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, Eq, PartialEq)] | ||||
| pub struct BoatPosition { | ||||
|     start: Coordinates, | ||||
|     len: usize, | ||||
| @@ -256,6 +268,10 @@ impl BoatsLayout { | ||||
|     pub fn find_boat_at_position(&self, pos: Coordinates) -> Option<&BoatPosition> { | ||||
|         self.0.iter().find(|f| f.all_coordinates().contains(&pos)) | ||||
|     } | ||||
|  | ||||
|     pub fn number_of_boats(&self) -> usize { | ||||
|         self.0.len() | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
|   | ||||
							
								
								
									
										20
									
								
								sea_battle_backend/src/data/end_game_map.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								sea_battle_backend/src/data/end_game_map.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| use crate::data::{BoatsLayout, MapCellContent}; | ||||
|  | ||||
| #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] | ||||
| pub struct EndGameMap { | ||||
|     pub boats: BoatsLayout, | ||||
|     pub grid: Vec<Vec<MapCellContent>>, | ||||
| } | ||||
|  | ||||
| impl EndGameMap { | ||||
|     pub fn get_map(&self) -> String { | ||||
|         let mut s = String::new(); | ||||
|         for row in &self.grid { | ||||
|             for col in row { | ||||
|                 s.push_str(&format!("{} ", col.letter())); | ||||
|             } | ||||
|             s.push('\n'); | ||||
|         } | ||||
|         s | ||||
|     } | ||||
| } | ||||
| @@ -1,22 +1,34 @@ | ||||
| use crate::data::boats_layout::{BoatsLayout, Coordinates}; | ||||
| use crate::data::GameRules; | ||||
| use crate::data::{BoatPosition, EndGameMap, GameRules}; | ||||
|  | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)] | ||||
| pub enum FireResult { | ||||
|     Missed, | ||||
|     Hit, | ||||
|     Sunk, | ||||
|     Rejected, | ||||
|     AlreadyTargetedPosition, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Copy, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)] | ||||
| pub enum MapCellContent { | ||||
|     Invalid, | ||||
|     Nothing, | ||||
|     TouchedBoat, | ||||
|     SunkBoat, | ||||
|     Boat, | ||||
|     FailedStrike, | ||||
| } | ||||
|  | ||||
| impl MapCellContent { | ||||
|     fn letter(&self) -> &'static str { | ||||
|     pub fn letter(&self) -> &'static str { | ||||
|         match self { | ||||
|             MapCellContent::Invalid => "!", | ||||
|             MapCellContent::Nothing => ".", | ||||
|             MapCellContent::TouchedBoat => "T", | ||||
|             MapCellContent::SunkBoat => "S", | ||||
|             MapCellContent::Boat => "B", | ||||
|             MapCellContent::FailedStrike => "X", | ||||
|             MapCellContent::FailedStrike => "x", | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -24,6 +36,9 @@ impl MapCellContent { | ||||
| pub struct GameMap { | ||||
|     rules: GameRules, | ||||
|     boats_config: BoatsLayout, | ||||
|     failed_strikes: Vec<Coordinates>, | ||||
|     successful_strikes: Vec<Coordinates>, | ||||
|     sunk_boats: Vec<BoatPosition>, | ||||
| } | ||||
|  | ||||
| impl GameMap { | ||||
| @@ -31,19 +46,80 @@ impl GameMap { | ||||
|         Self { | ||||
|             rules, | ||||
|             boats_config, | ||||
|             failed_strikes: vec![], | ||||
|             successful_strikes: vec![], | ||||
|             sunk_boats: vec![], | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn get_cell_content(&self, c: Coordinates) -> MapCellContent { | ||||
|         //TODO : improve this | ||||
|         if !c.is_valid(&self.rules) { | ||||
|             return MapCellContent::Invalid; | ||||
|         } | ||||
|  | ||||
|         if self.boats_config.find_boat_at_position(c).is_some() { | ||||
|             return MapCellContent::Boat; | ||||
|         if self.failed_strikes.contains(&c) { | ||||
|             return MapCellContent::FailedStrike; | ||||
|         } | ||||
|  | ||||
|         if let Some(b) = self.boats_config.find_boat_at_position(c) { | ||||
|             if !self.successful_strikes.contains(&c) { | ||||
|                 return MapCellContent::Boat; | ||||
|             } | ||||
|  | ||||
|             if self.sunk_boats.contains(b) { | ||||
|                 return MapCellContent::SunkBoat; | ||||
|             } | ||||
|  | ||||
|             return MapCellContent::TouchedBoat; | ||||
|         } | ||||
|  | ||||
|         MapCellContent::Nothing | ||||
|     } | ||||
|  | ||||
|     pub fn fire(&mut self, c: Coordinates) -> FireResult { | ||||
|         if !c.is_valid(&self.rules) { | ||||
|             return FireResult::Rejected; | ||||
|         } | ||||
|  | ||||
|         if self.failed_strikes.contains(&c) || self.successful_strikes.contains(&c) { | ||||
|             return FireResult::AlreadyTargetedPosition; | ||||
|         } | ||||
|  | ||||
|         match self.boats_config.find_boat_at_position(c) { | ||||
|             None => { | ||||
|                 self.failed_strikes.push(c); | ||||
|                 FireResult::Missed | ||||
|             } | ||||
|             Some(b) => { | ||||
|                 self.successful_strikes.push(c); | ||||
|  | ||||
|                 if !b | ||||
|                     .all_coordinates() | ||||
|                     .iter() | ||||
|                     .all(|c| self.successful_strikes.contains(c)) | ||||
|                 { | ||||
|                     return FireResult::Hit; | ||||
|                 } | ||||
|  | ||||
|                 self.sunk_boats.push(*b); | ||||
|  | ||||
|                 if !self.rules.boats_can_touch { | ||||
|                     for c in b.neighbor_coordinates(&self.rules) { | ||||
|                         if !self.failed_strikes.contains(&c) { | ||||
|                             self.failed_strikes.push(c); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 FireResult::Sunk | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn are_all_boat_sunk(&self) -> bool { | ||||
|         self.sunk_boats.len() == self.boats_config.number_of_boats() | ||||
|     } | ||||
|  | ||||
|     pub fn print_map(&self) { | ||||
|         for y in 0..self.rules.map_height { | ||||
|             for x in 0..self.rules.map_width { | ||||
| @@ -56,4 +132,17 @@ impl GameMap { | ||||
|             println!(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn final_map(&self) -> EndGameMap { | ||||
|         EndGameMap { | ||||
|             boats: self.boats_config.clone(), | ||||
|             grid: (0..self.rules.map_height) | ||||
|                 .map(|y| { | ||||
|                     (0..self.rules.map_width) | ||||
|                         .map(|x| self.get_cell_content(Coordinates::new(x as i32, y as i32))) | ||||
|                         .collect::<Vec<_>>() | ||||
|                 }) | ||||
|                 .collect::<Vec<_>>(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| pub use boats_layout::*; | ||||
| pub use current_game_status::*; | ||||
| pub use end_game_map::*; | ||||
| pub use game_map::*; | ||||
| pub use game_rules::*; | ||||
| pub use play_config::*; | ||||
|  | ||||
| mod boats_layout; | ||||
| mod current_game_status; | ||||
| mod end_game_map; | ||||
| mod game_map; | ||||
| mod game_rules; | ||||
| mod play_config; | ||||
|   | ||||
| @@ -23,6 +23,7 @@ pub struct PlayConfiguration { | ||||
|     pub min_boats_number: usize, | ||||
|     pub max_boats_number: usize, | ||||
|     pub bot_types: Vec<BotDescription>, | ||||
|     pub ordinate_alphabet: &'static str, | ||||
| } | ||||
|  | ||||
| impl Default for PlayConfiguration { | ||||
| @@ -40,6 +41,7 @@ impl Default for PlayConfiguration { | ||||
|                 r#type: BotType::Random, | ||||
|                 description: "Random strike. All the time.".to_string(), | ||||
|             }], | ||||
|             ordinate_alphabet: ALPHABET, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -20,6 +20,14 @@ pub trait Player { | ||||
|     fn request_fire(&self, status: CurrentGameStatus); | ||||
|  | ||||
|     fn other_player_must_fire(&self, status: CurrentGameStatus); | ||||
|  | ||||
|     fn strike_result(&self, c: Coordinates, res: FireResult); | ||||
|  | ||||
|     fn other_player_strike_result(&self, c: Coordinates, res: FireResult); | ||||
|  | ||||
|     fn lost_game(&self, your_map: EndGameMap, opponent_map: EndGameMap); | ||||
|  | ||||
|     fn won_game(&self, your_map: EndGameMap, opponent_map: EndGameMap); | ||||
| } | ||||
|  | ||||
| fn opponent(index: usize) -> usize { | ||||
| @@ -36,6 +44,7 @@ enum GameStatus { | ||||
|     Created, | ||||
|     WaitingForBoatsDisposition, | ||||
|     Started, | ||||
|     Finished, | ||||
| } | ||||
|  | ||||
| pub struct Game { | ||||
| @@ -87,9 +96,65 @@ impl Game { | ||||
|             self.turn | ||||
|         ); | ||||
|  | ||||
|         self.request_fire(); | ||||
|     } | ||||
|  | ||||
|     fn request_fire(&self) { | ||||
|         self.players[self.turn].request_fire(self.get_game_status_for_player(self.turn)); | ||||
|         self.players[opponent(self.turn)] | ||||
|             .request_fire(self.get_game_status_for_player(opponent(self.turn))); | ||||
|             .other_player_must_fire(self.get_game_status_for_player(opponent(self.turn))); | ||||
|     } | ||||
|  | ||||
|     fn player_map(&self, id: usize) -> &GameMap { | ||||
|         match id { | ||||
|             0 => self.map_0.as_ref(), | ||||
|             1 => self.map_1.as_ref(), | ||||
|             _ => unreachable!(), | ||||
|         } | ||||
|         .unwrap() | ||||
|     } | ||||
|  | ||||
|     fn player_map_mut(&mut self, id: usize) -> &mut GameMap { | ||||
|         match id { | ||||
|             0 => self.map_0.as_mut(), | ||||
|             1 => self.map_1.as_mut(), | ||||
|             _ => unreachable!(), | ||||
|         } | ||||
|         .unwrap() | ||||
|     } | ||||
|  | ||||
|     /// Handle fire attempts | ||||
|     fn handle_fire(&mut self, c: Coordinates) { | ||||
|         let result = self.player_map_mut(opponent(self.turn)).fire(c); | ||||
|         self.players[self.turn].strike_result(c, result); | ||||
|         self.players[opponent(self.turn)].strike_result(c, result); | ||||
|  | ||||
|         // Easiest case : player missed his fire | ||||
|         if result == FireResult::Missed { | ||||
|             self.turn = opponent(self.turn); | ||||
|             self.request_fire(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if result == FireResult::Sunk && self.player_map(opponent(self.turn)).are_all_boat_sunk() { | ||||
|             self.status = GameStatus::Finished; | ||||
|  | ||||
|             let winner_map = self.player_map(self.turn).final_map(); | ||||
|             let looser_map = self.player_map(opponent(self.turn)).final_map(); | ||||
|  | ||||
|             self.players[self.turn].won_game(winner_map.clone(), looser_map.clone()); | ||||
|             self.players[opponent(self.turn)].lost_game(looser_map, winner_map); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (result == FireResult::Sunk || result == FireResult::Hit) | ||||
|             && !self.rules.player_continue_on_hit | ||||
|         { | ||||
|             self.turn = opponent(self.turn); | ||||
|         } | ||||
|  | ||||
|         self.request_fire(); | ||||
|     } | ||||
|  | ||||
|     /// Get current game status for a specific player | ||||
| @@ -164,6 +229,16 @@ impl Handler<Fire> for Game { | ||||
|     type Result = (); | ||||
|  | ||||
|     fn handle(&mut self, msg: Fire, _ctx: &mut Self::Context) -> Self::Result { | ||||
|         log::debug!("FIRE ===> {:?}", msg); | ||||
|         if self.status != GameStatus::Started { | ||||
|             log::error!("Player attempted to fire on invalid step!"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if msg.0 != self.players[self.turn].get_uid() { | ||||
|             log::error!("Player attempted to fire when it was not its turn!"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         self.handle_fire(msg.1) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| use actix::Addr; | ||||
| use uuid::Uuid; | ||||
|  | ||||
| use crate::data::{CurrentGameStatus, GameRules}; | ||||
| use crate::data::{Coordinates, CurrentGameStatus, EndGameMap, FireResult, GameRules}; | ||||
| use crate::game::{Fire, Game, Player, SetBoatsLayout}; | ||||
| use crate::human_player_ws::{ClientMessage, HumanPlayerWS, ServerMessage}; | ||||
|  | ||||
| @@ -43,15 +43,44 @@ impl Player for HumanPlayer { | ||||
|         self.player | ||||
|             .do_send(ServerMessage::OtherPlayerMustFire { status }); | ||||
|     } | ||||
|  | ||||
|     fn strike_result(&self, c: Coordinates, res: FireResult) { | ||||
|         self.player.do_send(ServerMessage::StrikeResult { | ||||
|             pos: c, | ||||
|             result: res, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     fn other_player_strike_result(&self, c: Coordinates, res: FireResult) { | ||||
|         self.player.do_send(ServerMessage::OpponentStrikeResult { | ||||
|             pos: c, | ||||
|             result: res, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     fn lost_game(&self, your_map: EndGameMap, opponent_map: EndGameMap) { | ||||
|         self.player.do_send(ServerMessage::LostGame { | ||||
|             your_map, | ||||
|             opponent_map, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     fn won_game(&self, your_map: EndGameMap, opponent_map: EndGameMap) { | ||||
|         self.player.do_send(ServerMessage::WonGame { | ||||
|             your_map, | ||||
|             opponent_map, | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HumanPlayer { | ||||
|     pub fn handle_client_message(&self, msg: ClientMessage) { | ||||
|         match msg { | ||||
|             ClientMessage::StopGame => { | ||||
|                 // TODO : do something} | ||||
|                 // TODO : do something | ||||
|             } | ||||
|             ClientMessage::BoatsLayout { layout } => { | ||||
|                 // TODO : check boat layout validity | ||||
|                 self.game.do_send(SetBoatsLayout(self.uuid, layout)) | ||||
|             } | ||||
|             ClientMessage::Fire { location } => self.game.do_send(Fire(self.uuid, location)), | ||||
|   | ||||
| @@ -6,7 +6,9 @@ use actix_web_actors::ws; | ||||
| use actix_web_actors::ws::{CloseCode, CloseReason, Message, ProtocolError, WebsocketContext}; | ||||
| use uuid::Uuid; | ||||
|  | ||||
| use crate::data::{BoatsLayout, BotType, Coordinates, CurrentGameStatus, GameRules}; | ||||
| use crate::data::{ | ||||
|     BoatsLayout, BotType, Coordinates, CurrentGameStatus, EndGameMap, FireResult, GameRules, | ||||
| }; | ||||
| use crate::game::{AddPlayer, Game}; | ||||
| use crate::human_player::HumanPlayer; | ||||
| use crate::random_bot::RandomBot; | ||||
| @@ -33,12 +35,34 @@ pub enum ClientMessage { | ||||
| #[serde(tag = "type")] | ||||
| pub enum ServerMessage { | ||||
|     WaitingForAnotherPlayer, | ||||
|     QueryBoatsLayout { rules: GameRules }, | ||||
|     QueryBoatsLayout { | ||||
|         rules: GameRules, | ||||
|     }, | ||||
|     WaitingForOtherPlayerConfiguration, | ||||
|     OtherPlayerReady, | ||||
|     GameStarting, | ||||
|     OtherPlayerMustFire { status: CurrentGameStatus }, | ||||
|     RequestFire { status: CurrentGameStatus }, | ||||
|     OtherPlayerMustFire { | ||||
|         status: CurrentGameStatus, | ||||
|     }, | ||||
|     RequestFire { | ||||
|         status: CurrentGameStatus, | ||||
|     }, | ||||
|     StrikeResult { | ||||
|         pos: Coordinates, | ||||
|         result: FireResult, | ||||
|     }, | ||||
|     OpponentStrikeResult { | ||||
|         pos: Coordinates, | ||||
|         result: FireResult, | ||||
|     }, | ||||
|     LostGame { | ||||
|         your_map: EndGameMap, | ||||
|         opponent_map: EndGameMap, | ||||
|     }, | ||||
|     WonGame { | ||||
|         your_map: EndGameMap, | ||||
|         opponent_map: EndGameMap, | ||||
|     }, | ||||
| } | ||||
|  | ||||
| #[derive(Default)] | ||||
| @@ -95,7 +119,7 @@ impl Actor for HumanPlayerWS { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for HumanPlayerWS { | ||||
| impl StreamHandler<Result<ws::Message, ProtocolError>> for HumanPlayerWS { | ||||
|     fn handle(&mut self, msg: Result<Message, ProtocolError>, ctx: &mut Self::Context) { | ||||
|         match msg { | ||||
|             Ok(Message::Ping(msg)) => ctx.pong(&msg), | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| use actix::Addr; | ||||
| use uuid::Uuid; | ||||
|  | ||||
| use crate::data::{BoatsLayout, CurrentGameStatus, GameRules}; | ||||
| use crate::data::{BoatsLayout, Coordinates, CurrentGameStatus, EndGameMap, FireResult, GameRules}; | ||||
| use crate::game::{Fire, Game, Player, SetBoatsLayout}; | ||||
|  | ||||
| #[derive(Clone)] | ||||
| @@ -49,4 +49,12 @@ impl Player for RandomBot { | ||||
|     } | ||||
|  | ||||
|     fn other_player_must_fire(&self, _status: CurrentGameStatus) {} | ||||
|  | ||||
|     fn strike_result(&self, _c: Coordinates, _res: FireResult) {} | ||||
|  | ||||
|     fn other_player_strike_result(&self, _c: Coordinates, _res: FireResult) {} | ||||
|  | ||||
|     fn lost_game(&self, _your_map: EndGameMap, _opponent_map: EndGameMap) {} | ||||
|  | ||||
|     fn won_game(&self, _your_map: EndGameMap, _opponent_map: EndGameMap) {} | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,11 @@ | ||||
| use tokio::task; | ||||
|  | ||||
| use crate::args::Args; | ||||
| use crate::bot_client; | ||||
| use crate::data::GameRules; | ||||
| use crate::server::start_server; | ||||
| use crate::test::network_utils::wait_for_port; | ||||
| use crate::test::TestPort; | ||||
| use tokio::task; | ||||
|  | ||||
| #[tokio::test] | ||||
| async fn invalid_port() { | ||||
| @@ -46,10 +47,31 @@ async fn full_game() { | ||||
|     local_set | ||||
|         .run_until(async move { | ||||
|             let rules = GameRules::random_players_rules(); | ||||
|             task::spawn_local(start_server(Args::for_test(TestPort::ClientInvalidRules))); | ||||
|             wait_for_port(TestPort::ClientInvalidRules.port()).await; | ||||
|             task::spawn_local(start_server(Args::for_test(TestPort::FullGame))); | ||||
|             wait_for_port(TestPort::FullGame.port()).await; | ||||
|  | ||||
|             bot_client::run_client(&TestPort::ClientInvalidRules.as_url(), &rules) | ||||
|             bot_client::run_client(&TestPort::FullGame.as_url(), &rules) | ||||
|                 .await | ||||
|                 .unwrap(); | ||||
|         }) | ||||
|         .await; | ||||
| } | ||||
|  | ||||
| #[tokio::test] | ||||
| async fn full_game_no_touching_boats() { | ||||
|     let _ = env_logger::builder().is_test(true).try_init(); | ||||
|  | ||||
|     let local_set = task::LocalSet::new(); | ||||
|     local_set | ||||
|         .run_until(async move { | ||||
|             let mut rules = GameRules::random_players_rules(); | ||||
|             rules.boats_can_touch = false; | ||||
|             task::spawn_local(start_server(Args::for_test( | ||||
|                 TestPort::FullGameTouchingBoats, | ||||
|             ))); | ||||
|             wait_for_port(TestPort::FullGameTouchingBoats.port()).await; | ||||
|  | ||||
|             bot_client::run_client(&TestPort::FullGameTouchingBoats.as_url(), &rules) | ||||
|                 .await | ||||
|                 .unwrap(); | ||||
|         }) | ||||
|   | ||||
| @@ -4,6 +4,8 @@ use crate::args::Args; | ||||
| enum TestPort { | ||||
|     ClientInvalidPort = 20000, | ||||
|     ClientInvalidRules, | ||||
|     FullGame, | ||||
|     FullGameTouchingBoats, | ||||
| } | ||||
|  | ||||
| impl TestPort { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user