Add tests for no replay after hit rule
This commit is contained in:
parent
7cfd7a4899
commit
6832aebedb
@ -1,7 +1,7 @@
|
|||||||
use actix::Addr;
|
use actix::Addr;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::data::{BoatsLayout, Coordinates, CurrentGameStatus, EndGameMap, FireResult, GameRules};
|
use crate::data::{BoatsLayout, Coordinates, CurrentGameStatus, FireResult, GameRules};
|
||||||
use crate::game::{Fire, Game, Player, RespondRequestRematch, SetBoatsLayout};
|
use crate::game::{Fire, Game, Player, RespondRequestRematch, SetBoatsLayout};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -64,9 +64,9 @@ impl Player for LinearBot {
|
|||||||
|
|
||||||
fn other_player_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 lost_game(&self, _status: CurrentGameStatus) {}
|
||||||
|
|
||||||
fn won_game(&self, _your_map: EndGameMap, _opponent_map: EndGameMap) {}
|
fn won_game(&self, _status: CurrentGameStatus) {}
|
||||||
|
|
||||||
fn opponent_requested_rematch(&self) {
|
fn opponent_requested_rematch(&self) {
|
||||||
self.game.do_send(RespondRequestRematch(self.uuid, true));
|
self.game.do_send(RespondRequestRematch(self.uuid, true));
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use actix::Addr;
|
use actix::Addr;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::data::{BoatsLayout, Coordinates, CurrentGameStatus, EndGameMap, FireResult, GameRules};
|
use crate::data::{BoatsLayout, Coordinates, CurrentGameStatus, FireResult, GameRules};
|
||||||
use crate::game::{Fire, Game, Player, RespondRequestRematch, SetBoatsLayout};
|
use crate::game::{Fire, Game, Player, RespondRequestRematch, SetBoatsLayout};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -64,9 +64,9 @@ impl Player for RandomBot {
|
|||||||
|
|
||||||
fn other_player_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 lost_game(&self, _status: CurrentGameStatus) {}
|
||||||
|
|
||||||
fn won_game(&self, _your_map: EndGameMap, _opponent_map: EndGameMap) {}
|
fn won_game(&self, _status: CurrentGameStatus) {}
|
||||||
|
|
||||||
fn opponent_requested_rematch(&self) {
|
fn opponent_requested_rematch(&self) {
|
||||||
self.game.do_send(RespondRequestRematch(self.uuid, true));
|
self.game.do_send(RespondRequestRematch(self.uuid, true));
|
||||||
|
@ -213,6 +213,42 @@ impl BoatsLayout {
|
|||||||
Ok(boats)
|
Ok(boats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate boats layout that put boats at the beginning of the map
|
||||||
|
pub fn layout_for_boats_at_beginning_of_map(rules: &GameRules) -> std::io::Result<Self> {
|
||||||
|
let mut boats = Self(Vec::with_capacity(rules.boats_list().len()));
|
||||||
|
let mut list = rules.boats_list();
|
||||||
|
list.sort();
|
||||||
|
list.reverse();
|
||||||
|
|
||||||
|
for y in 0..rules.map_height {
|
||||||
|
for x in 0..rules.map_width {
|
||||||
|
if list.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let position = BoatPosition {
|
||||||
|
start: Coordinates::new(x as i32, y as i32),
|
||||||
|
len: list[0],
|
||||||
|
direction: BoatDirection::Right,
|
||||||
|
};
|
||||||
|
|
||||||
|
if boats.is_acceptable_boat_position(&position, rules) {
|
||||||
|
list.remove(0);
|
||||||
|
boats.0.push(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !list.is_empty() {
|
||||||
|
return Err(std::io::Error::new(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"Un-usable game rules!",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(boats)
|
||||||
|
}
|
||||||
|
|
||||||
fn is_acceptable_boat_position(&self, pos: &BoatPosition, rules: &GameRules) -> bool {
|
fn is_acceptable_boat_position(&self, pos: &BoatPosition, rules: &GameRules) -> bool {
|
||||||
// Check if boat coordinates are valid
|
// Check if boat coordinates are valid
|
||||||
if pos.all_coordinates().iter().any(|c| !c.is_valid(rules)) {
|
if pos.all_coordinates().iter().any(|c| !c.is_valid(rules)) {
|
||||||
|
@ -16,6 +16,10 @@ impl CurrentGameMapStatus {
|
|||||||
pub fn did_fire_at_location(&self, c: Coordinates) -> bool {
|
pub fn did_fire_at_location(&self, c: Coordinates) -> bool {
|
||||||
self.successful_strikes.contains(&c) || self.failed_strikes.contains(&c)
|
self.successful_strikes.contains(&c) || self.failed_strikes.contains(&c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn number_of_fires(&self) -> usize {
|
||||||
|
self.successful_strikes.len() + self.failed_strikes.len()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PrintableCurrentGameMapStatus(GameRules, CurrentGameMapStatus);
|
struct PrintableCurrentGameMapStatus(GameRules, CurrentGameMapStatus);
|
||||||
@ -207,12 +211,20 @@ impl CurrentGameStatus {
|
|||||||
boats_size.first().cloned()
|
boats_size.first().cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_your_map(&self) -> String {
|
||||||
|
PrintableCurrentGameMapStatus(self.rules.clone(), self.your_map.clone()).get_map()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_opponent_map(&self) -> String {
|
||||||
|
PrintableCurrentGameMapStatus(self.rules.clone(), self.opponent_map.clone()).get_map()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn print_your_map(&self) {
|
pub fn print_your_map(&self) {
|
||||||
PrintableCurrentGameMapStatus(self.rules.clone(), self.your_map.clone()).print_map()
|
print!("{}", self.get_your_map());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_opponent_map(&self) {
|
pub fn print_opponent_map(&self) {
|
||||||
PrintableCurrentGameMapStatus(self.rules.clone(), self.opponent_map.clone()).print_map()
|
print!("{}", self.get_opponent_map());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
use std::fmt::Write;
|
|
||||||
|
|
||||||
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 {
|
|
||||||
write!(&mut s, "{} ", col.letter()).unwrap();
|
|
||||||
}
|
|
||||||
s.push('\n');
|
|
||||||
}
|
|
||||||
s
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,5 @@
|
|||||||
use crate::data::boats_layout::{BoatsLayout, Coordinates};
|
use crate::data::boats_layout::{BoatsLayout, Coordinates};
|
||||||
use crate::data::{
|
use crate::data::*;
|
||||||
BoatPosition, CurrentGameMapStatus, EndGameMap, GameRules, MapCellContent, PrintableMap,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum FireResult {
|
pub enum FireResult {
|
||||||
@ -110,19 +108,6 @@ impl GameMap {
|
|||||||
sunk_boats: self.sunk_boats.clone(),
|
sunk_boats: self.sunk_boats.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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<_>>(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrintableMap for GameMap {
|
impl PrintableMap for GameMap {
|
||||||
|
@ -38,6 +38,11 @@ impl GameRules {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_player_continue_on_hit(mut self, c: bool) -> Self {
|
||||||
|
self.player_continue_on_hit = c;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the list of boats for this configuration
|
/// Set the list of boats for this configuration
|
||||||
pub fn set_boats_list(&mut self, boats: &[usize]) {
|
pub fn set_boats_list(&mut self, boats: &[usize]) {
|
||||||
self.boats_str = boats
|
self.boats_str = boats
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
pub use boats_layout::*;
|
pub use boats_layout::*;
|
||||||
pub use current_game_status::*;
|
pub use current_game_status::*;
|
||||||
pub use end_game_map::*;
|
|
||||||
pub use game_map::*;
|
pub use game_map::*;
|
||||||
pub use game_rules::*;
|
pub use game_rules::*;
|
||||||
pub use play_config::*;
|
pub use play_config::*;
|
||||||
@ -8,7 +7,6 @@ pub use printable_map::*;
|
|||||||
|
|
||||||
mod boats_layout;
|
mod boats_layout;
|
||||||
mod current_game_status;
|
mod current_game_status;
|
||||||
mod end_game_map;
|
|
||||||
mod game_map;
|
mod game_map;
|
||||||
mod game_rules;
|
mod game_rules;
|
||||||
mod play_config;
|
mod play_config;
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
use std::fmt::Write;
|
||||||
|
use std::string::String;
|
||||||
|
|
||||||
use crate::data::Coordinates;
|
use crate::data::Coordinates;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
@ -27,6 +30,12 @@ pub trait PrintableMap {
|
|||||||
fn map_cell_content(&self, c: Coordinates) -> MapCellContent;
|
fn map_cell_content(&self, c: Coordinates) -> MapCellContent;
|
||||||
|
|
||||||
fn print_map(&self) {
|
fn print_map(&self) {
|
||||||
|
print!("{}", self.get_map());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_map(&self) -> String {
|
||||||
|
let mut out = String::with_capacity(100);
|
||||||
|
|
||||||
let mut y = 0;
|
let mut y = 0;
|
||||||
let mut x;
|
let mut x;
|
||||||
loop {
|
loop {
|
||||||
@ -38,11 +47,11 @@ pub trait PrintableMap {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
print!("{} ", content.letter());
|
write!(out, "{} ", content.letter()).unwrap();
|
||||||
x += 1;
|
x += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
println!();
|
out.push('\n');
|
||||||
|
|
||||||
// x == 0 <=> we reached the end of the map
|
// x == 0 <=> we reached the end of the map
|
||||||
if x == 0 {
|
if x == 0 {
|
||||||
@ -51,5 +60,7 @@ pub trait PrintableMap {
|
|||||||
|
|
||||||
y += 1;
|
y += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,9 +32,9 @@ pub trait Player {
|
|||||||
|
|
||||||
fn other_player_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 lost_game(&self, status: CurrentGameStatus);
|
||||||
|
|
||||||
fn won_game(&self, your_map: EndGameMap, opponent_map: EndGameMap);
|
fn won_game(&self, status: CurrentGameStatus);
|
||||||
|
|
||||||
fn opponent_requested_rematch(&self);
|
fn opponent_requested_rematch(&self);
|
||||||
|
|
||||||
@ -163,11 +163,9 @@ impl Game {
|
|||||||
{
|
{
|
||||||
self.status = GameStatus::Finished;
|
self.status = GameStatus::Finished;
|
||||||
|
|
||||||
let winner_map = self.player_map(self.turn).final_map();
|
self.players[self.turn].won_game(self.get_game_status_for_player(self.turn));
|
||||||
let looser_map = self.player_map(opponent(self.turn)).final_map();
|
self.players[opponent(self.turn)]
|
||||||
|
.lost_game(self.get_game_status_for_player(opponent(self.turn)));
|
||||||
self.players[self.turn].won_game(winner_map.clone(), looser_map.clone());
|
|
||||||
self.players[opponent(self.turn)].lost_game(looser_map, winner_map);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -215,7 +213,9 @@ impl Game {
|
|||||||
CurrentGameStatus {
|
CurrentGameStatus {
|
||||||
rules: self.rules.clone(),
|
rules: self.rules.clone(),
|
||||||
your_map: self.player_map(id).current_map_status(false),
|
your_map: self.player_map(id).current_map_status(false),
|
||||||
opponent_map: self.player_map(opponent(id)).current_map_status(true),
|
opponent_map: self
|
||||||
|
.player_map(opponent(id))
|
||||||
|
.current_map_status(self.status != GameStatus::Finished),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use actix::Addr;
|
use actix::Addr;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::data::{Coordinates, CurrentGameStatus, EndGameMap, FireResult, GameRules};
|
use crate::data::*;
|
||||||
use crate::game::*;
|
use crate::game::*;
|
||||||
use crate::human_player_ws::{ClientMessage, HumanPlayerWS, ServerMessage};
|
use crate::human_player_ws::{ClientMessage, HumanPlayerWS, ServerMessage};
|
||||||
|
|
||||||
@ -74,18 +74,12 @@ impl Player for HumanPlayer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lost_game(&self, your_map: EndGameMap, opponent_map: EndGameMap) {
|
fn lost_game(&self, status: CurrentGameStatus) {
|
||||||
self.player.do_send(ServerMessage::LostGame {
|
self.player.do_send(ServerMessage::LostGame { status });
|
||||||
your_map,
|
|
||||||
opponent_map,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn won_game(&self, your_map: EndGameMap, opponent_map: EndGameMap) {
|
fn won_game(&self, status: CurrentGameStatus) {
|
||||||
self.player.do_send(ServerMessage::WonGame {
|
self.player.do_send(ServerMessage::WonGame { status });
|
||||||
your_map,
|
|
||||||
opponent_map,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opponent_requested_rematch(&self) {
|
fn opponent_requested_rematch(&self) {
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use crate::bots::linear_bot::LinearBot;
|
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use actix::{Actor, Handler, StreamHandler};
|
use actix::{Actor, Handler, StreamHandler};
|
||||||
use actix_web_actors::ws;
|
use actix_web_actors::ws;
|
||||||
use actix_web_actors::ws::{CloseCode, CloseReason, Message, ProtocolError, WebsocketContext};
|
use actix_web_actors::ws::{CloseCode, CloseReason, Message, ProtocolError, WebsocketContext};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::bots::linear_bot::LinearBot;
|
||||||
use crate::bots::random_bot::RandomBot;
|
use crate::bots::random_bot::RandomBot;
|
||||||
use crate::data::{
|
use crate::data::{BoatsLayout, BotType, Coordinates, CurrentGameStatus, FireResult, GameRules};
|
||||||
BoatsLayout, BotType, Coordinates, CurrentGameStatus, EndGameMap, FireResult, GameRules,
|
|
||||||
};
|
|
||||||
use crate::game::{AddPlayer, Game};
|
use crate::game::{AddPlayer, Game};
|
||||||
use crate::human_player::HumanPlayer;
|
use crate::human_player::HumanPlayer;
|
||||||
|
|
||||||
@ -73,12 +71,10 @@ pub enum ServerMessage {
|
|||||||
result: FireResult,
|
result: FireResult,
|
||||||
},
|
},
|
||||||
LostGame {
|
LostGame {
|
||||||
your_map: EndGameMap,
|
status: CurrentGameStatus,
|
||||||
opponent_map: EndGameMap,
|
|
||||||
},
|
},
|
||||||
WonGame {
|
WonGame {
|
||||||
your_map: EndGameMap,
|
status: CurrentGameStatus,
|
||||||
opponent_map: EndGameMap,
|
|
||||||
},
|
},
|
||||||
OpponentRequestedRematch,
|
OpponentRequestedRematch,
|
||||||
OpponentAcceptedRematch,
|
OpponentAcceptedRematch,
|
||||||
|
@ -160,13 +160,10 @@ impl BotClient {
|
|||||||
pos.human_print(),
|
pos.human_print(),
|
||||||
result
|
result
|
||||||
),
|
),
|
||||||
ServerMessage::LostGame {
|
ServerMessage::LostGame { status } => {
|
||||||
your_map,
|
|
||||||
opponent_map,
|
|
||||||
} => {
|
|
||||||
log::debug!("We lost game :(");
|
log::debug!("We lost game :(");
|
||||||
log::debug!("Other game:\n{}\n", opponent_map.get_map());
|
log::debug!("Opponent map:\n{}", status.get_opponent_map());
|
||||||
log::debug!("Our game:\n{}\n", your_map.get_map());
|
log::debug!("Our map:\n{}\n", status.get_your_map());
|
||||||
|
|
||||||
if remaining_games > 0 {
|
if remaining_games > 0 {
|
||||||
socket
|
socket
|
||||||
@ -178,13 +175,10 @@ impl BotClient {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ServerMessage::WonGame {
|
ServerMessage::WonGame { status } => {
|
||||||
your_map,
|
|
||||||
opponent_map,
|
|
||||||
} => {
|
|
||||||
log::debug!("We won the game !!!!");
|
log::debug!("We won the game !!!!");
|
||||||
log::debug!("Other game:\n{}\n", opponent_map.get_map());
|
log::debug!("Opponent map:\n{}\n", status.get_opponent_map());
|
||||||
log::debug!("Our game:\n{}\n", your_map.get_map());
|
log::debug!("Our map:\n{}\n", status.get_your_map());
|
||||||
|
|
||||||
if remaining_games > 0 {
|
if remaining_games > 0 {
|
||||||
socket
|
socket
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
|
||||||
use crate::args::Args;
|
use crate::args::Args;
|
||||||
use crate::data::{BotType, Coordinates, GameRules};
|
use crate::data::{BoatsLayout, BotType, Coordinates, GameRules};
|
||||||
use crate::human_player_ws::ServerMessage;
|
use crate::human_player_ws::ServerMessage;
|
||||||
use crate::server::start_server;
|
use crate::server::start_server;
|
||||||
use crate::test::bot_client::ClientEndResult;
|
use crate::test::bot_client::ClientEndResult;
|
||||||
use crate::test::network_utils::wait_for_port;
|
use crate::test::network_utils::wait_for_port;
|
||||||
|
use crate::test::play_utils::check_no_replay_on_hit;
|
||||||
use crate::test::{bot_client, TestPort};
|
use crate::test::{bot_client, TestPort};
|
||||||
|
|
||||||
fn check_strikes_are_linear(msg: &ServerMessage) {
|
fn check_strikes_are_linear(msg: &ServerMessage) {
|
||||||
@ -49,3 +50,95 @@ async fn full_game() {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn full_game_no_replay_on_hit() {
|
||||||
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
|
|
||||||
|
let local_set = task::LocalSet::new();
|
||||||
|
local_set
|
||||||
|
.run_until(async move {
|
||||||
|
task::spawn_local(start_server(Args::for_test(
|
||||||
|
TestPort::LinearBotNoReplayOnHit,
|
||||||
|
)));
|
||||||
|
wait_for_port(TestPort::LinearBotNoReplayOnHit.port()).await;
|
||||||
|
|
||||||
|
let res = bot_client::BotClient::new(TestPort::LinearBotNoReplayOnHit.as_url())
|
||||||
|
.with_rules(
|
||||||
|
GameRules::random_players_rules()
|
||||||
|
.with_player_continue_on_hit(false)
|
||||||
|
.with_bot_type(BotType::Linear),
|
||||||
|
)
|
||||||
|
.with_server_msg_callback(check_no_replay_on_hit)
|
||||||
|
.run_client()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(res, ClientEndResult::Finished);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn full_game_no_replay_on_hit_two() {
|
||||||
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
|
|
||||||
|
let local_set = task::LocalSet::new();
|
||||||
|
local_set
|
||||||
|
.run_until(async move {
|
||||||
|
task::spawn_local(start_server(Args::for_test(
|
||||||
|
TestPort::LinearBotNoReplayOnHit,
|
||||||
|
)));
|
||||||
|
wait_for_port(TestPort::LinearBotNoReplayOnHit.port()).await;
|
||||||
|
|
||||||
|
let rules = GameRules::random_players_rules()
|
||||||
|
.with_player_continue_on_hit(false)
|
||||||
|
.with_bot_type(BotType::Linear);
|
||||||
|
let layout = BoatsLayout::layout_for_boats_at_beginning_of_map(&rules).unwrap();
|
||||||
|
let res = bot_client::BotClient::new(TestPort::LinearBotNoReplayOnHit.as_url())
|
||||||
|
.with_rules(rules.clone())
|
||||||
|
.with_layout(layout)
|
||||||
|
.with_server_msg_callback(check_no_replay_on_hit)
|
||||||
|
.run_client()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(res, ClientEndResult::Finished);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn full_game_with_replay_on_hit() {
|
||||||
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
|
|
||||||
|
let local_set = task::LocalSet::new();
|
||||||
|
local_set
|
||||||
|
.run_until(async move {
|
||||||
|
task::spawn_local(start_server(Args::for_test(
|
||||||
|
TestPort::LinearBotNoReplayOnHit,
|
||||||
|
)));
|
||||||
|
wait_for_port(TestPort::LinearBotNoReplayOnHit.port()).await;
|
||||||
|
|
||||||
|
let rules = GameRules::random_players_rules()
|
||||||
|
.with_player_continue_on_hit(true)
|
||||||
|
.with_bot_type(BotType::Linear);
|
||||||
|
let layout = BoatsLayout::layout_for_boats_at_beginning_of_map(&rules).unwrap();
|
||||||
|
let res = bot_client::BotClient::new(TestPort::LinearBotNoReplayOnHit.as_url())
|
||||||
|
.with_rules(rules.clone())
|
||||||
|
.with_layout(layout)
|
||||||
|
.with_server_msg_callback(|msg| {
|
||||||
|
if let ServerMessage::LostGame { status } | ServerMessage::WonGame { status } =
|
||||||
|
msg
|
||||||
|
{
|
||||||
|
assert!(
|
||||||
|
status.opponent_map.number_of_fires()
|
||||||
|
< status.your_map.number_of_fires()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.run_client()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(res, ClientEndResult::Finished);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
@ -6,13 +6,14 @@ use crate::server::start_server;
|
|||||||
use crate::test::bot_client;
|
use crate::test::bot_client;
|
||||||
use crate::test::bot_client::ClientEndResult;
|
use crate::test::bot_client::ClientEndResult;
|
||||||
use crate::test::network_utils::wait_for_port;
|
use crate::test::network_utils::wait_for_port;
|
||||||
|
use crate::test::play_utils::check_no_replay_on_hit;
|
||||||
use crate::test::TestPort;
|
use crate::test::TestPort;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn invalid_port() {
|
async fn invalid_port() {
|
||||||
let _ = env_logger::builder().is_test(true).try_init();
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
|
|
||||||
bot_client::BotClient::new(TestPort::ClientInvalidPort.as_url())
|
bot_client::BotClient::new(TestPort::RandomBotClientInvalidPort.as_url())
|
||||||
.run_client()
|
.run_client()
|
||||||
.await
|
.await
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
@ -28,10 +29,12 @@ async fn invalid_rules() {
|
|||||||
let mut rules = GameRules::random_players_rules();
|
let mut rules = GameRules::random_players_rules();
|
||||||
rules.map_width = 0;
|
rules.map_width = 0;
|
||||||
|
|
||||||
task::spawn_local(start_server(Args::for_test(TestPort::ClientInvalidRules)));
|
task::spawn_local(start_server(Args::for_test(
|
||||||
wait_for_port(TestPort::ClientInvalidRules.port()).await;
|
TestPort::RandomBotClientInvalidRules,
|
||||||
|
)));
|
||||||
|
wait_for_port(TestPort::RandomBotClientInvalidRules.port()).await;
|
||||||
|
|
||||||
bot_client::BotClient::new(TestPort::ClientInvalidRules.as_url())
|
bot_client::BotClient::new(TestPort::RandomBotClientInvalidRules.as_url())
|
||||||
.with_rules(rules.clone())
|
.with_rules(rules.clone())
|
||||||
.with_layout(
|
.with_layout(
|
||||||
BoatsLayout::gen_random_for_rules(&GameRules::random_players_rules()).unwrap(),
|
BoatsLayout::gen_random_for_rules(&GameRules::random_players_rules()).unwrap(),
|
||||||
@ -50,10 +53,10 @@ async fn full_game() {
|
|||||||
let local_set = task::LocalSet::new();
|
let local_set = task::LocalSet::new();
|
||||||
local_set
|
local_set
|
||||||
.run_until(async move {
|
.run_until(async move {
|
||||||
task::spawn_local(start_server(Args::for_test(TestPort::FullGame)));
|
task::spawn_local(start_server(Args::for_test(TestPort::RandomBotFullGame)));
|
||||||
wait_for_port(TestPort::FullGame.port()).await;
|
wait_for_port(TestPort::RandomBotFullGame.port()).await;
|
||||||
|
|
||||||
let res = bot_client::BotClient::new(TestPort::FullGame.as_url())
|
let res = bot_client::BotClient::new(TestPort::RandomBotFullGame.as_url())
|
||||||
.run_client()
|
.run_client()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -72,15 +75,16 @@ async fn full_game_no_touching_boats() {
|
|||||||
let mut rules = GameRules::random_players_rules();
|
let mut rules = GameRules::random_players_rules();
|
||||||
rules.boats_can_touch = false;
|
rules.boats_can_touch = false;
|
||||||
task::spawn_local(start_server(Args::for_test(
|
task::spawn_local(start_server(Args::for_test(
|
||||||
TestPort::FullGameNoTouchingBoats,
|
TestPort::RandomBotFullGameNoTouchingBoats,
|
||||||
)));
|
)));
|
||||||
wait_for_port(TestPort::FullGameNoTouchingBoats.port()).await;
|
wait_for_port(TestPort::RandomBotFullGameNoTouchingBoats.port()).await;
|
||||||
|
|
||||||
let res = bot_client::BotClient::new(TestPort::FullGameNoTouchingBoats.as_url())
|
let res =
|
||||||
.with_rules(rules)
|
bot_client::BotClient::new(TestPort::RandomBotFullGameNoTouchingBoats.as_url())
|
||||||
.run_client()
|
.with_rules(rules)
|
||||||
.await
|
.run_client()
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(res, ClientEndResult::Finished);
|
assert_eq!(res, ClientEndResult::Finished);
|
||||||
})
|
})
|
||||||
@ -97,21 +101,22 @@ async fn invalid_boats_layout_number_of_boats() {
|
|||||||
let mut rules = GameRules::random_players_rules();
|
let mut rules = GameRules::random_players_rules();
|
||||||
rules.boats_can_touch = false;
|
rules.boats_can_touch = false;
|
||||||
task::spawn_local(start_server(Args::for_test(
|
task::spawn_local(start_server(Args::for_test(
|
||||||
TestPort::InvalidBoatsLayoutNumberOfBoats,
|
TestPort::RandomBotInvalidBoatsLayoutNumberOfBoats,
|
||||||
)));
|
)));
|
||||||
wait_for_port(TestPort::InvalidBoatsLayoutNumberOfBoats.port()).await;
|
wait_for_port(TestPort::RandomBotInvalidBoatsLayoutNumberOfBoats.port()).await;
|
||||||
|
|
||||||
let mut rules_modified = rules.clone();
|
let mut rules_modified = rules.clone();
|
||||||
rules_modified.pop_boat();
|
rules_modified.pop_boat();
|
||||||
let layout = BoatsLayout::gen_random_for_rules(&rules_modified).unwrap();
|
let layout = BoatsLayout::gen_random_for_rules(&rules_modified).unwrap();
|
||||||
|
|
||||||
let res =
|
let res = bot_client::BotClient::new(
|
||||||
bot_client::BotClient::new(&TestPort::InvalidBoatsLayoutNumberOfBoats.as_url())
|
&TestPort::RandomBotInvalidBoatsLayoutNumberOfBoats.as_url(),
|
||||||
.with_rules(rules)
|
)
|
||||||
.with_layout(layout)
|
.with_rules(rules)
|
||||||
.run_client()
|
.with_layout(layout)
|
||||||
.await
|
.run_client()
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(res, ClientEndResult::InvalidBoatsLayout);
|
assert_eq!(res, ClientEndResult::InvalidBoatsLayout);
|
||||||
})
|
})
|
||||||
@ -128,21 +133,23 @@ async fn invalid_boats_layout_len_of_a_boat() {
|
|||||||
let mut rules = GameRules::random_players_rules();
|
let mut rules = GameRules::random_players_rules();
|
||||||
rules.boats_can_touch = false;
|
rules.boats_can_touch = false;
|
||||||
task::spawn_local(start_server(Args::for_test(
|
task::spawn_local(start_server(Args::for_test(
|
||||||
TestPort::InvalidBoatsLayoutLenOfABoat,
|
TestPort::RandomBotInvalidBoatsLayoutLenOfABoat,
|
||||||
)));
|
)));
|
||||||
wait_for_port(TestPort::InvalidBoatsLayoutLenOfABoat.port()).await;
|
wait_for_port(TestPort::RandomBotInvalidBoatsLayoutLenOfABoat.port()).await;
|
||||||
|
|
||||||
let mut rules_modified = rules.clone();
|
let mut rules_modified = rules.clone();
|
||||||
let previous = rules_modified.pop_boat();
|
let previous = rules_modified.pop_boat();
|
||||||
rules_modified.add_boat(previous - 1);
|
rules_modified.add_boat(previous - 1);
|
||||||
let layout = BoatsLayout::gen_random_for_rules(&rules_modified).unwrap();
|
let layout = BoatsLayout::gen_random_for_rules(&rules_modified).unwrap();
|
||||||
|
|
||||||
let res = bot_client::BotClient::new(&TestPort::InvalidBoatsLayoutLenOfABoat.as_url())
|
let res = bot_client::BotClient::new(
|
||||||
.with_rules(rules)
|
&TestPort::RandomBotInvalidBoatsLayoutLenOfABoat.as_url(),
|
||||||
.with_layout(layout)
|
)
|
||||||
.run_client()
|
.with_rules(rules)
|
||||||
.await
|
.with_layout(layout)
|
||||||
.unwrap();
|
.run_client()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(res, ClientEndResult::InvalidBoatsLayout);
|
assert_eq!(res, ClientEndResult::InvalidBoatsLayout);
|
||||||
})
|
})
|
||||||
@ -157,12 +164,36 @@ async fn full_game_multiple_rematches() {
|
|||||||
local_set
|
local_set
|
||||||
.run_until(async move {
|
.run_until(async move {
|
||||||
task::spawn_local(start_server(Args::for_test(
|
task::spawn_local(start_server(Args::for_test(
|
||||||
TestPort::FullGameMultipleRematch,
|
TestPort::RandomBotFullGameMultipleRematch,
|
||||||
)));
|
)));
|
||||||
wait_for_port(TestPort::FullGameMultipleRematch.port()).await;
|
wait_for_port(TestPort::RandomBotFullGameMultipleRematch.port()).await;
|
||||||
|
|
||||||
let res = bot_client::BotClient::new(TestPort::FullGameMultipleRematch.as_url())
|
let res =
|
||||||
.with_number_plays(5)
|
bot_client::BotClient::new(TestPort::RandomBotFullGameMultipleRematch.as_url())
|
||||||
|
.with_number_plays(5)
|
||||||
|
.run_client()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(res, ClientEndResult::Finished);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn full_game_no_replay_on_hit() {
|
||||||
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
|
|
||||||
|
let local_set = task::LocalSet::new();
|
||||||
|
local_set
|
||||||
|
.run_until(async move {
|
||||||
|
task::spawn_local(start_server(Args::for_test(
|
||||||
|
TestPort::RandomBotNoReplayOnHit,
|
||||||
|
)));
|
||||||
|
wait_for_port(TestPort::RandomBotNoReplayOnHit.port()).await;
|
||||||
|
|
||||||
|
let res = bot_client::BotClient::new(TestPort::RandomBotNoReplayOnHit.as_url())
|
||||||
|
.with_rules(GameRules::random_players_rules().with_player_continue_on_hit(false))
|
||||||
|
.with_server_msg_callback(check_no_replay_on_hit)
|
||||||
.run_client()
|
.run_client()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -2,14 +2,16 @@ use crate::args::Args;
|
|||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
enum TestPort {
|
enum TestPort {
|
||||||
ClientInvalidPort = 20000,
|
RandomBotClientInvalidPort = 20000,
|
||||||
ClientInvalidRules,
|
RandomBotClientInvalidRules,
|
||||||
FullGame,
|
RandomBotFullGame,
|
||||||
FullGameNoTouchingBoats,
|
RandomBotFullGameNoTouchingBoats,
|
||||||
InvalidBoatsLayoutNumberOfBoats,
|
RandomBotInvalidBoatsLayoutNumberOfBoats,
|
||||||
InvalidBoatsLayoutLenOfABoat,
|
RandomBotInvalidBoatsLayoutLenOfABoat,
|
||||||
FullGameMultipleRematch,
|
RandomBotFullGameMultipleRematch,
|
||||||
|
RandomBotNoReplayOnHit,
|
||||||
LinearBotFullGame,
|
LinearBotFullGame,
|
||||||
|
LinearBotNoReplayOnHit,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestPort {
|
impl TestPort {
|
||||||
@ -36,3 +38,4 @@ pub mod bot_client;
|
|||||||
mod bot_client_bot_linear_play;
|
mod bot_client_bot_linear_play;
|
||||||
mod bot_client_bot_random_play;
|
mod bot_client_bot_random_play;
|
||||||
mod network_utils;
|
mod network_utils;
|
||||||
|
mod play_utils;
|
||||||
|
12
sea_battle_backend/src/test/play_utils.rs
Normal file
12
sea_battle_backend/src/test/play_utils.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use crate::human_player_ws::ServerMessage;
|
||||||
|
|
||||||
|
/// Make sure player can not replay after successful hit
|
||||||
|
pub fn check_no_replay_on_hit(msg: &ServerMessage) {
|
||||||
|
if let ServerMessage::OpponentMustFire { status } | ServerMessage::RequestFire { status } = msg
|
||||||
|
{
|
||||||
|
let diff =
|
||||||
|
status.opponent_map.number_of_fires() as i32 - status.your_map.number_of_fires() as i32;
|
||||||
|
|
||||||
|
assert!(diff <= 1);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user