Add new test on invite mode
This commit is contained in:
parent
45b6a24eda
commit
dfdf6d9952
@ -1,25 +1,27 @@
|
|||||||
use actix::Addr;
|
use actix::Addr;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::data::{BoatsLayout, Coordinates, CurrentGameStatus, FireResult, GameRules};
|
use crate::data::{BoatsLayout, BotType, Coordinates, CurrentGameStatus, FireResult, GameRules};
|
||||||
use crate::game::{Fire, Game, Player, RespondRequestRematch, SetBoatsLayout};
|
use crate::game::{Fire, Game, Player, RespondRequestRematch, SetBoatsLayout};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct IntermediateBot {
|
pub struct BotPlayer {
|
||||||
game: Addr<Game>,
|
game: Addr<Game>,
|
||||||
|
kind: BotType,
|
||||||
uuid: Uuid,
|
uuid: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntermediateBot {
|
impl BotPlayer {
|
||||||
pub fn new(game: Addr<Game>) -> Self {
|
pub fn new(kind: BotType, game: Addr<Game>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
game,
|
game,
|
||||||
|
kind,
|
||||||
uuid: Uuid::new_v4(),
|
uuid: Uuid::new_v4(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Player for IntermediateBot {
|
impl Player for BotPlayer {
|
||||||
fn get_name(&self) -> &str {
|
fn get_name(&self) -> &str {
|
||||||
"Intermediate Bot"
|
"Intermediate Bot"
|
||||||
}
|
}
|
||||||
@ -54,11 +56,10 @@ impl Player for IntermediateBot {
|
|||||||
fn notify_game_starting(&self) {}
|
fn notify_game_starting(&self) {}
|
||||||
|
|
||||||
fn request_fire(&self, status: CurrentGameStatus) {
|
fn request_fire(&self, status: CurrentGameStatus) {
|
||||||
let coordinates = status
|
self.game.do_send(Fire(
|
||||||
.continue_attack_boat()
|
self.uuid,
|
||||||
.unwrap_or_else(|| status.find_valid_random_fire_location());
|
status.find_fire_coordinates_for_bot_type(self.kind),
|
||||||
|
));
|
||||||
self.game.do_send(Fire(self.uuid, coordinates));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opponent_must_fire(&self, _status: CurrentGameStatus) {}
|
fn opponent_must_fire(&self, _status: CurrentGameStatus) {}
|
@ -1,87 +0,0 @@
|
|||||||
use actix::Addr;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
use crate::data::{BoatsLayout, Coordinates, CurrentGameStatus, FireResult, GameRules};
|
|
||||||
use crate::game::{Fire, Game, Player, RespondRequestRematch, SetBoatsLayout};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct LinearBot {
|
|
||||||
game: Addr<Game>,
|
|
||||||
uuid: Uuid,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LinearBot {
|
|
||||||
pub fn new(game: Addr<Game>) -> Self {
|
|
||||||
Self {
|
|
||||||
game,
|
|
||||||
uuid: Uuid::new_v4(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Player for LinearBot {
|
|
||||||
fn get_name(&self) -> &str {
|
|
||||||
"Linear Bot"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_uid(&self) -> Uuid {
|
|
||||||
self.uuid
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_bot(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_other_player_name(&self, _name: &str) {}
|
|
||||||
|
|
||||||
fn query_boats_layout(&self, rules: &GameRules) {
|
|
||||||
match BoatsLayout::gen_random_for_rules(rules) {
|
|
||||||
Ok(layout) => self.game.do_send(SetBoatsLayout(self.uuid, layout)),
|
|
||||||
|
|
||||||
Err(e) => log::error!(
|
|
||||||
"Failed to use game rules to construct boats layout: {:?}",
|
|
||||||
e
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rejected_boats_layout(&self, _errors: Vec<&'static str>) {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn notify_other_player_ready(&self) {}
|
|
||||||
|
|
||||||
fn notify_game_starting(&self) {}
|
|
||||||
|
|
||||||
fn request_fire(&self, status: CurrentGameStatus) {
|
|
||||||
self.game
|
|
||||||
.do_send(Fire(self.uuid, status.find_first_valid_fire_location()));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn opponent_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, _status: CurrentGameStatus) {}
|
|
||||||
|
|
||||||
fn won_game(&self, _status: CurrentGameStatus) {}
|
|
||||||
|
|
||||||
fn opponent_requested_rematch(&self) {
|
|
||||||
self.game.do_send(RespondRequestRematch(self.uuid, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn opponent_rejected_rematch(&self) {}
|
|
||||||
|
|
||||||
fn opponent_accepted_rematch(&self) {}
|
|
||||||
|
|
||||||
fn opponent_left_game(&self) {
|
|
||||||
// Human are not reliable lol
|
|
||||||
}
|
|
||||||
|
|
||||||
fn opponent_replaced_by_bot(&self) {
|
|
||||||
// Not such a good idea. will panic, just in case
|
|
||||||
panic!("Bot shall not play against each other (it is completely useless)");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
pub mod intermediate_bot;
|
|
||||||
pub mod linear_bot;
|
|
||||||
pub mod random_bot;
|
|
||||||
pub mod smart_bot;
|
|
@ -1,87 +0,0 @@
|
|||||||
use actix::Addr;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
use crate::data::{BoatsLayout, Coordinates, CurrentGameStatus, FireResult, GameRules};
|
|
||||||
use crate::game::{Fire, Game, Player, RespondRequestRematch, SetBoatsLayout};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RandomBot {
|
|
||||||
game: Addr<Game>,
|
|
||||||
uuid: Uuid,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RandomBot {
|
|
||||||
pub fn new(game: Addr<Game>) -> Self {
|
|
||||||
Self {
|
|
||||||
game,
|
|
||||||
uuid: Uuid::new_v4(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Player for RandomBot {
|
|
||||||
fn get_name(&self) -> &str {
|
|
||||||
"Random Bot"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_uid(&self) -> Uuid {
|
|
||||||
self.uuid
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_bot(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_other_player_name(&self, _name: &str) {}
|
|
||||||
|
|
||||||
fn query_boats_layout(&self, rules: &GameRules) {
|
|
||||||
match BoatsLayout::gen_random_for_rules(rules) {
|
|
||||||
Ok(layout) => self.game.do_send(SetBoatsLayout(self.uuid, layout)),
|
|
||||||
|
|
||||||
Err(e) => log::error!(
|
|
||||||
"Failed to use game rules to construct boats layout: {:?}",
|
|
||||||
e
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rejected_boats_layout(&self, _errors: Vec<&'static str>) {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn notify_other_player_ready(&self) {}
|
|
||||||
|
|
||||||
fn notify_game_starting(&self) {}
|
|
||||||
|
|
||||||
fn request_fire(&self, status: CurrentGameStatus) {
|
|
||||||
self.game
|
|
||||||
.do_send(Fire(self.uuid, status.find_valid_random_fire_location()));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn opponent_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, _status: CurrentGameStatus) {}
|
|
||||||
|
|
||||||
fn won_game(&self, _status: CurrentGameStatus) {}
|
|
||||||
|
|
||||||
fn opponent_requested_rematch(&self) {
|
|
||||||
self.game.do_send(RespondRequestRematch(self.uuid, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn opponent_rejected_rematch(&self) {}
|
|
||||||
|
|
||||||
fn opponent_accepted_rematch(&self) {}
|
|
||||||
|
|
||||||
fn opponent_left_game(&self) {
|
|
||||||
// Human are not reliable lol
|
|
||||||
}
|
|
||||||
|
|
||||||
fn opponent_replaced_by_bot(&self) {
|
|
||||||
// Not such a good idea. will panic, just in case
|
|
||||||
panic!("Bot shall not play against each other (it is completely useless)");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
use actix::Addr;
|
|
||||||
use rand::RngCore;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
use crate::data::{BoatsLayout, Coordinates, CurrentGameStatus, FireResult, GameRules};
|
|
||||||
use crate::game::{Fire, Game, Player, RespondRequestRematch, SetBoatsLayout};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SmartBot {
|
|
||||||
game: Addr<Game>,
|
|
||||||
uuid: Uuid,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SmartBot {
|
|
||||||
pub fn new(game: Addr<Game>) -> Self {
|
|
||||||
Self {
|
|
||||||
game,
|
|
||||||
uuid: Uuid::new_v4(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Player for SmartBot {
|
|
||||||
fn get_name(&self) -> &str {
|
|
||||||
"Intermediate Bot"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_uid(&self) -> Uuid {
|
|
||||||
self.uuid
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_bot(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_other_player_name(&self, _name: &str) {}
|
|
||||||
|
|
||||||
fn query_boats_layout(&self, rules: &GameRules) {
|
|
||||||
match BoatsLayout::gen_random_for_rules(rules) {
|
|
||||||
Ok(layout) => self.game.do_send(SetBoatsLayout(self.uuid, layout)),
|
|
||||||
|
|
||||||
Err(e) => log::error!(
|
|
||||||
"Failed to use game rules to construct boats layout: {:?}",
|
|
||||||
e
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rejected_boats_layout(&self, _errors: Vec<&'static str>) {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn notify_other_player_ready(&self) {}
|
|
||||||
|
|
||||||
fn notify_game_starting(&self) {}
|
|
||||||
|
|
||||||
fn request_fire(&self, status: CurrentGameStatus) {
|
|
||||||
let coordinates = status.continue_attack_boat().unwrap_or_else(|| {
|
|
||||||
let coordinates = status.get_relevant_grid_locations();
|
|
||||||
if !coordinates.is_empty() {
|
|
||||||
let pos = rand::thread_rng().next_u32() as usize;
|
|
||||||
coordinates[pos % coordinates.len()]
|
|
||||||
} else {
|
|
||||||
status.find_valid_random_fire_location()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
self.game.do_send(Fire(self.uuid, coordinates));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn opponent_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, _status: CurrentGameStatus) {}
|
|
||||||
|
|
||||||
fn won_game(&self, _status: CurrentGameStatus) {}
|
|
||||||
|
|
||||||
fn opponent_requested_rematch(&self) {
|
|
||||||
self.game.do_send(RespondRequestRematch(self.uuid, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn opponent_rejected_rematch(&self) {}
|
|
||||||
|
|
||||||
fn opponent_accepted_rematch(&self) {}
|
|
||||||
|
|
||||||
fn opponent_left_game(&self) {
|
|
||||||
// Human are not reliable lol
|
|
||||||
}
|
|
||||||
|
|
||||||
fn opponent_replaced_by_bot(&self) {
|
|
||||||
// Not such a good idea. will panic, just in case
|
|
||||||
panic!("Bot shall not play against each other (it is completely useless)");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
|
|
||||||
use crate::data::{
|
use crate::data::{
|
||||||
BoatPosition, BoatsLayout, Coordinates, GameRules, MapCellContent, PrintableMap,
|
BoatPosition, BoatsLayout, BotType, Coordinates, GameRules, MapCellContent, PrintableMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Default)]
|
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Default)]
|
||||||
@ -274,6 +274,32 @@ impl CurrentGameStatus {
|
|||||||
pub fn print_opponent_map(&self) {
|
pub fn print_opponent_map(&self) {
|
||||||
print!("{}", self.get_opponent_map());
|
print!("{}", self.get_opponent_map());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_intermediate_bot_fire_location(&self) -> Coordinates {
|
||||||
|
self.continue_attack_boat()
|
||||||
|
.unwrap_or_else(|| self.find_valid_random_fire_location())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_smart_bot_fire_location(&self) -> Coordinates {
|
||||||
|
self.continue_attack_boat().unwrap_or_else(|| {
|
||||||
|
let coordinates = self.get_relevant_grid_locations();
|
||||||
|
if !coordinates.is_empty() {
|
||||||
|
let pos = rand::thread_rng().next_u32() as usize;
|
||||||
|
coordinates[pos % coordinates.len()]
|
||||||
|
} else {
|
||||||
|
self.find_valid_random_fire_location()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_fire_coordinates_for_bot_type(&self, t: BotType) -> Coordinates {
|
||||||
|
match t {
|
||||||
|
BotType::Random => self.find_valid_random_fire_location(),
|
||||||
|
BotType::Linear => self.find_first_valid_fire_location(),
|
||||||
|
BotType::Intermediate => self.find_intermediate_bot_fire_location(),
|
||||||
|
BotType::Smart => self.find_smart_bot_fire_location(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -4,7 +4,7 @@ use actix::prelude::*;
|
|||||||
use actix::{Actor, Context, Handler};
|
use actix::{Actor, Context, Handler};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::bots::random_bot::RandomBot;
|
use crate::bot_player::BotPlayer;
|
||||||
use crate::data::*;
|
use crate::data::*;
|
||||||
|
|
||||||
pub trait Player {
|
pub trait Player {
|
||||||
@ -362,7 +362,8 @@ impl Handler<PlayerLeftGame> for Game {
|
|||||||
ctx.stop();
|
ctx.stop();
|
||||||
} else {
|
} else {
|
||||||
// Replace the player with a bot
|
// Replace the player with a bot
|
||||||
self.players[offline_player] = Arc::new(RandomBot::new(ctx.address()));
|
self.players[offline_player] =
|
||||||
|
Arc::new(BotPlayer::new(self.rules.bot_type, ctx.address()));
|
||||||
self.players[opponent(offline_player)].opponent_replaced_by_bot();
|
self.players[opponent(offline_player)].opponent_replaced_by_bot();
|
||||||
|
|
||||||
if self.turn == offline_player {
|
if self.turn == offline_player {
|
||||||
|
@ -7,11 +7,8 @@ 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::intermediate_bot::IntermediateBot;
|
use crate::bot_player::BotPlayer;
|
||||||
use crate::bots::linear_bot::LinearBot;
|
use crate::data::{BoatsLayout, Coordinates, CurrentGameStatus, FireResult, GameRules};
|
||||||
use crate::bots::random_bot::RandomBot;
|
|
||||||
use crate::bots::smart_bot::SmartBot;
|
|
||||||
use crate::data::{BoatsLayout, BotType, Coordinates, CurrentGameStatus, FireResult, GameRules};
|
|
||||||
use crate::dispatcher_actor::{AcceptInvite, CreateInvite, DispatcherActor};
|
use crate::dispatcher_actor::{AcceptInvite, CreateInvite, DispatcherActor};
|
||||||
use crate::game::{AddPlayer, Game};
|
use crate::game::{AddPlayer, Game};
|
||||||
use crate::human_player::HumanPlayer;
|
use crate::human_player::HumanPlayer;
|
||||||
@ -156,20 +153,10 @@ impl Actor for HumanPlayerWS {
|
|||||||
log::debug!("Start play with a bot");
|
log::debug!("Start play with a bot");
|
||||||
let game = Game::new(rules.clone()).start();
|
let game = Game::new(rules.clone()).start();
|
||||||
|
|
||||||
match rules.bot_type {
|
game.do_send(AddPlayer(Arc::new(BotPlayer::new(
|
||||||
BotType::Random => {
|
rules.bot_type,
|
||||||
game.do_send(AddPlayer(Arc::new(RandomBot::new(game.clone()))));
|
game.clone(),
|
||||||
}
|
))));
|
||||||
BotType::Linear => {
|
|
||||||
game.do_send(AddPlayer(Arc::new(LinearBot::new(game.clone()))));
|
|
||||||
}
|
|
||||||
BotType::Intermediate => {
|
|
||||||
game.do_send(AddPlayer(Arc::new(IntermediateBot::new(game.clone()))));
|
|
||||||
}
|
|
||||||
BotType::Smart => {
|
|
||||||
game.do_send(AddPlayer(Arc::new(SmartBot::new(game.clone()))));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let player = Arc::new(HumanPlayer {
|
let player = Arc::new(HumanPlayer {
|
||||||
name: self.name.to_string(),
|
name: self.name.to_string(),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
extern crate core;
|
extern crate core;
|
||||||
|
|
||||||
pub mod args;
|
pub mod args;
|
||||||
pub mod bots;
|
pub mod bot_player;
|
||||||
pub mod consts;
|
pub mod consts;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod dispatcher_actor;
|
pub mod dispatcher_actor;
|
||||||
|
@ -4,7 +4,7 @@ use std::fmt::Display;
|
|||||||
use futures::{SinkExt, StreamExt};
|
use futures::{SinkExt, StreamExt};
|
||||||
use tokio_tungstenite::tungstenite::Message;
|
use tokio_tungstenite::tungstenite::Message;
|
||||||
|
|
||||||
use crate::data::{BoatsLayout, GameRules};
|
use crate::data::{BoatsLayout, BotType, GameRules};
|
||||||
use crate::human_player_ws::{ClientMessage, ServerMessage};
|
use crate::human_player_ws::{ClientMessage, ServerMessage};
|
||||||
use crate::server::AcceptInviteQuery;
|
use crate::server::AcceptInviteQuery;
|
||||||
|
|
||||||
@ -37,6 +37,7 @@ pub struct BotClient {
|
|||||||
layout: Option<BoatsLayout>,
|
layout: Option<BoatsLayout>,
|
||||||
number_plays: usize,
|
number_plays: usize,
|
||||||
server_msg_callback: Option<Box<dyn FnMut(&ServerMessage)>>,
|
server_msg_callback: Option<Box<dyn FnMut(&ServerMessage)>>,
|
||||||
|
play_as_bot_type: BotType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BotClient {
|
impl BotClient {
|
||||||
@ -51,6 +52,7 @@ impl BotClient {
|
|||||||
layout: None,
|
layout: None,
|
||||||
number_plays: 1,
|
number_plays: 1,
|
||||||
server_msg_callback: None,
|
server_msg_callback: None,
|
||||||
|
play_as_bot_type: BotType::Random,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,6 +84,11 @@ impl BotClient {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_play_as_bot_type(mut self, t: BotType) -> Self {
|
||||||
|
self.play_as_bot_type = t;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn run_client(&mut self) -> Result<ClientEndResult, Box<dyn Error>> {
|
pub async fn run_client(&mut self) -> Result<ClientEndResult, Box<dyn Error>> {
|
||||||
let mut remaining_games = self.number_plays;
|
let mut remaining_games = self.number_plays;
|
||||||
let mut number_victories = 0;
|
let mut number_victories = 0;
|
||||||
@ -196,7 +203,7 @@ impl BotClient {
|
|||||||
ServerMessage::RequestFire { status } => {
|
ServerMessage::RequestFire { status } => {
|
||||||
assert_eq!(status.opponent_map.boats.number_of_boats(), 0);
|
assert_eq!(status.opponent_map.boats.number_of_boats(), 0);
|
||||||
|
|
||||||
let location = status.find_valid_random_fire_location();
|
let location = status.find_fire_coordinates_for_bot_type(self.play_as_bot_type);
|
||||||
log::debug!("Will fire at {:?}", location);
|
log::debug!("Will fire at {:?}", location);
|
||||||
socket
|
socket
|
||||||
.send(Message::Text(serde_json::to_string(
|
.send(Message::Text(serde_json::to_string(
|
||||||
@ -253,7 +260,12 @@ impl BotClient {
|
|||||||
ServerMessage::SetOpponentName { name } => log::debug!("Opponent name: {}", name),
|
ServerMessage::SetOpponentName { name } => log::debug!("Opponent name: {}", name),
|
||||||
|
|
||||||
ServerMessage::OpponentRequestedRematch => {
|
ServerMessage::OpponentRequestedRematch => {
|
||||||
log::debug!("Opponent rejected rematch.");
|
log::debug!("Opponent requested rematch.");
|
||||||
|
socket
|
||||||
|
.send(Message::Text(serde_json::to_string(
|
||||||
|
&ClientMessage::AcceptRematch,
|
||||||
|
)?))
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
ServerMessage::OpponentAcceptedRematch => {
|
ServerMessage::OpponentAcceptedRematch => {
|
||||||
log::debug!("Opponent accepted rematch");
|
log::debug!("Opponent accepted rematch");
|
||||||
|
@ -5,6 +5,7 @@ use tokio::sync::mpsc::Sender;
|
|||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
|
||||||
use crate::args::Args;
|
use crate::args::Args;
|
||||||
|
use crate::data::BotType;
|
||||||
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, RunMode};
|
use crate::test::bot_client::{ClientEndResult, RunMode};
|
||||||
@ -40,9 +41,13 @@ async fn run_other_invite_side(
|
|||||||
sender: Sender<Result<ClientEndResult, Box<dyn Error>>>,
|
sender: Sender<Result<ClientEndResult, Box<dyn Error>>>,
|
||||||
port: TestPort,
|
port: TestPort,
|
||||||
code: String,
|
code: String,
|
||||||
|
play_as_bot_type: BotType,
|
||||||
|
number_plays: usize,
|
||||||
) {
|
) {
|
||||||
let res = bot_client::BotClient::new(port.as_url())
|
let res = bot_client::BotClient::new(port.as_url())
|
||||||
.with_run_mode(RunMode::AcceptInvite { code })
|
.with_run_mode(RunMode::AcceptInvite { code })
|
||||||
|
.with_play_as_bot_type(play_as_bot_type)
|
||||||
|
.with_number_plays(number_plays)
|
||||||
.run_client()
|
.run_client()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@ -69,6 +74,8 @@ async fn full_game() {
|
|||||||
sender.clone(),
|
sender.clone(),
|
||||||
TestPort::InviteModeFullGame,
|
TestPort::InviteModeFullGame,
|
||||||
code.clone(),
|
code.clone(),
|
||||||
|
BotType::Random,
|
||||||
|
1,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -82,3 +89,127 @@ async fn full_game() {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn first_player_win() {
|
||||||
|
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::InviteModeFirstPlayerWin,
|
||||||
|
)));
|
||||||
|
wait_for_port(TestPort::InviteModeFirstPlayerWin.port()).await;
|
||||||
|
|
||||||
|
let (sender, mut receiver) = mpsc::channel(1);
|
||||||
|
|
||||||
|
let res = bot_client::BotClient::new(TestPort::InviteModeFirstPlayerWin.as_url())
|
||||||
|
.with_run_mode(RunMode::CreateInvite)
|
||||||
|
.with_play_as_bot_type(BotType::Smart)
|
||||||
|
.with_number_plays(10)
|
||||||
|
.with_server_msg_callback(move |msg| {
|
||||||
|
if let ServerMessage::SetInviteCode { code } = msg {
|
||||||
|
task::spawn_local(run_other_invite_side(
|
||||||
|
sender.clone(),
|
||||||
|
TestPort::InviteModeFirstPlayerWin,
|
||||||
|
code.clone(),
|
||||||
|
BotType::Linear,
|
||||||
|
10,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.run_client()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let other_side_res = receiver.recv().await.unwrap().unwrap();
|
||||||
|
|
||||||
|
assert!(matches!(res, ClientEndResult::Finished { .. }));
|
||||||
|
|
||||||
|
assert!(matches!(other_side_res, ClientEndResult::Finished { .. }));
|
||||||
|
|
||||||
|
match (res, other_side_res) {
|
||||||
|
(
|
||||||
|
ClientEndResult::Finished {
|
||||||
|
number_defeats: d1,
|
||||||
|
number_victories: v1,
|
||||||
|
},
|
||||||
|
ClientEndResult::Finished {
|
||||||
|
number_defeats: d2,
|
||||||
|
number_victories: v2,
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
assert_eq!(d1, v2);
|
||||||
|
assert_eq!(v1, d2);
|
||||||
|
|
||||||
|
assert!(v1 > 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
(_, _) => unreachable!(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn second_player_win() {
|
||||||
|
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::InviteModeSecondPlayerWin,
|
||||||
|
)));
|
||||||
|
wait_for_port(TestPort::InviteModeSecondPlayerWin.port()).await;
|
||||||
|
|
||||||
|
let (sender, mut receiver) = mpsc::channel(1);
|
||||||
|
|
||||||
|
let res = bot_client::BotClient::new(TestPort::InviteModeSecondPlayerWin.as_url())
|
||||||
|
.with_run_mode(RunMode::CreateInvite)
|
||||||
|
.with_play_as_bot_type(BotType::Linear)
|
||||||
|
.with_number_plays(10)
|
||||||
|
.with_server_msg_callback(move |msg| {
|
||||||
|
if let ServerMessage::SetInviteCode { code } = msg {
|
||||||
|
task::spawn_local(run_other_invite_side(
|
||||||
|
sender.clone(),
|
||||||
|
TestPort::InviteModeSecondPlayerWin,
|
||||||
|
code.clone(),
|
||||||
|
BotType::Smart,
|
||||||
|
10,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.run_client()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let other_side_res = receiver.recv().await.unwrap().unwrap();
|
||||||
|
|
||||||
|
assert!(matches!(res, ClientEndResult::Finished { .. }));
|
||||||
|
|
||||||
|
assert!(matches!(other_side_res, ClientEndResult::Finished { .. }));
|
||||||
|
|
||||||
|
match (res, other_side_res) {
|
||||||
|
(
|
||||||
|
ClientEndResult::Finished {
|
||||||
|
number_defeats: d1,
|
||||||
|
number_victories: v1,
|
||||||
|
},
|
||||||
|
ClientEndResult::Finished {
|
||||||
|
number_defeats: d2,
|
||||||
|
number_victories: v2,
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
assert_eq!(d1, v2);
|
||||||
|
assert_eq!(v1, d2);
|
||||||
|
|
||||||
|
assert!(v2 > 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
(_, _) => unreachable!(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
@ -15,6 +15,8 @@ enum TestPort {
|
|||||||
IntermediateBotFullGame,
|
IntermediateBotFullGame,
|
||||||
InviteModeInvalidCode,
|
InviteModeInvalidCode,
|
||||||
InviteModeFullGame,
|
InviteModeFullGame,
|
||||||
|
InviteModeFirstPlayerWin,
|
||||||
|
InviteModeSecondPlayerWin,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestPort {
|
impl TestPort {
|
||||||
|
@ -2,14 +2,6 @@ use rand::distributions::Alphanumeric;
|
|||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
|
|
||||||
/// Generate a random string of a given size
|
/// Generate a random string of a given size
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use sea_battle_backend::utils::rand_str;
|
|
||||||
///
|
|
||||||
/// let size = 10;
|
|
||||||
/// let rand = rand_str(size);
|
|
||||||
/// assert_eq!(size, rand.len());
|
|
||||||
/// ```
|
|
||||||
pub fn rand_str(len: usize) -> String {
|
pub fn rand_str(len: usize) -> String {
|
||||||
thread_rng()
|
thread_rng()
|
||||||
.sample_iter(&Alphanumeric)
|
.sample_iter(&Alphanumeric)
|
||||||
@ -17,3 +9,15 @@ pub fn rand_str(len: usize) -> String {
|
|||||||
.take(len)
|
.take(len)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::utils::rand_str;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rand_str() {
|
||||||
|
let size = 10;
|
||||||
|
let rand = rand_str(size);
|
||||||
|
assert_eq!(size, rand.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user