Add strike timeout setting
This commit is contained in:
parent
8c1a3f2c5f
commit
ba1ed84b33
@ -5,6 +5,9 @@ use std::time::{Duration, Instant};
|
|||||||
|
|
||||||
use crossterm::event;
|
use crossterm::event;
|
||||||
use crossterm::event::{Event, KeyCode};
|
use crossterm::event::{Event, KeyCode};
|
||||||
|
use sea_battle_backend::consts::{
|
||||||
|
MAX_BOATS_NUMBER, MAX_MAP_HEIGHT, MAX_MAP_WIDTH, MAX_STRIKE_TIMEOUT,
|
||||||
|
};
|
||||||
use tui::backend::Backend;
|
use tui::backend::Backend;
|
||||||
use tui::layout::{Constraint, Direction, Layout, Margin};
|
use tui::layout::{Constraint, Direction, Layout, Margin};
|
||||||
use tui::style::{Color, Style};
|
use tui::style::{Color, Style};
|
||||||
@ -26,6 +29,7 @@ enum EditingField {
|
|||||||
MapWidth = 0,
|
MapWidth = 0,
|
||||||
MapHeight,
|
MapHeight,
|
||||||
BoatsList,
|
BoatsList,
|
||||||
|
StrikeTimeout,
|
||||||
BoatsCanTouch,
|
BoatsCanTouch,
|
||||||
PlayerContinueOnHit,
|
PlayerContinueOnHit,
|
||||||
BotType,
|
BotType,
|
||||||
@ -114,26 +118,48 @@ impl GameRulesConfigurationScreen {
|
|||||||
{
|
{
|
||||||
self.rules.remove_last_boat();
|
self.rules.remove_last_boat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.curr_field == EditingField::StrikeTimeout {
|
||||||
|
match self.rules.strike_timeout.unwrap_or(0) / 10 {
|
||||||
|
0 => self.rules.strike_timeout = None,
|
||||||
|
v => self.rules.strike_timeout = Some(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyCode::Char(c) if ('0'..='9').contains(&c) => {
|
KeyCode::Char(c) if ('0'..='9').contains(&c) => {
|
||||||
let val = c.to_string().parse::<usize>().unwrap_or_default();
|
let val = c.to_string().parse::<usize>().unwrap_or_default();
|
||||||
|
|
||||||
if self.curr_field == EditingField::MapWidth {
|
if self.curr_field == EditingField::MapWidth {
|
||||||
|
if self.rules.map_width <= MAX_MAP_WIDTH {
|
||||||
self.rules.map_width *= 10;
|
self.rules.map_width *= 10;
|
||||||
self.rules.map_width += val;
|
self.rules.map_width += val;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.curr_field == EditingField::MapHeight {
|
if self.curr_field == EditingField::MapHeight {
|
||||||
|
if self.rules.map_height <= MAX_MAP_HEIGHT {
|
||||||
self.rules.map_height *= 10;
|
self.rules.map_height *= 10;
|
||||||
self.rules.map_height += val;
|
self.rules.map_height += val;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.curr_field == EditingField::BoatsList {
|
if self.curr_field == EditingField::BoatsList {
|
||||||
|
if self.rules.boats_list().len() < MAX_BOATS_NUMBER {
|
||||||
self.rules.add_boat(val);
|
self.rules.add_boat(val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.curr_field == EditingField::StrikeTimeout {
|
||||||
|
let mut timeout = self.rules.strike_timeout.unwrap_or(0);
|
||||||
|
if timeout <= MAX_STRIKE_TIMEOUT {
|
||||||
|
timeout *= 10;
|
||||||
|
timeout += val;
|
||||||
|
self.rules.strike_timeout = Some(timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,7 +179,7 @@ impl GameRulesConfigurationScreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ui<B: Backend>(&mut self, f: &mut Frame<B>) {
|
fn ui<B: Backend>(&mut self, f: &mut Frame<B>) {
|
||||||
let area = centered_rect_size(50, 20, &f.size());
|
let area = centered_rect_size(50, 23, &f.size());
|
||||||
|
|
||||||
let block = Block::default().title("Game rules").borders(Borders::ALL);
|
let block = Block::default().title("Game rules").borders(Borders::ALL);
|
||||||
f.render_widget(block, area);
|
f.render_widget(block, area);
|
||||||
@ -161,11 +187,12 @@ impl GameRulesConfigurationScreen {
|
|||||||
let chunks = Layout::default()
|
let chunks = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([
|
.constraints([
|
||||||
Constraint::Length(3),
|
Constraint::Length(3), // Map width
|
||||||
Constraint::Length(3),
|
Constraint::Length(3), // Map height
|
||||||
Constraint::Length(3),
|
Constraint::Length(3), // Boats list
|
||||||
Constraint::Length(1),
|
Constraint::Length(3), // Strike timeout
|
||||||
Constraint::Length(1),
|
Constraint::Length(1), // Boats can touch
|
||||||
|
Constraint::Length(1), // Player continue on hit
|
||||||
Constraint::Length(3), // Bot type
|
Constraint::Length(3), // Bot type
|
||||||
Constraint::Length(1), // Margin
|
Constraint::Length(1), // Margin
|
||||||
Constraint::Length(1), // Buttons
|
Constraint::Length(1), // Buttons
|
||||||
@ -203,6 +230,13 @@ impl GameRulesConfigurationScreen {
|
|||||||
);
|
);
|
||||||
f.render_widget(editor, chunks[EditingField::BoatsList as usize]);
|
f.render_widget(editor, chunks[EditingField::BoatsList as usize]);
|
||||||
|
|
||||||
|
let editor = TextEditorWidget::new(
|
||||||
|
"Strike timeout (0 to disable)",
|
||||||
|
&self.rules.strike_timeout.unwrap_or(0).to_string(),
|
||||||
|
self.curr_field == EditingField::StrikeTimeout,
|
||||||
|
);
|
||||||
|
f.render_widget(editor, chunks[EditingField::StrikeTimeout as usize]);
|
||||||
|
|
||||||
let editor = CheckboxWidget::new(
|
let editor = CheckboxWidget::new(
|
||||||
"Boats can touch",
|
"Boats can touch",
|
||||||
self.rules.boats_can_touch,
|
self.rules.boats_can_touch,
|
||||||
|
@ -24,3 +24,6 @@ pub const INVITE_CODE_LENGTH: usize = 5;
|
|||||||
|
|
||||||
pub const MIN_PLAYER_NAME_LENGTH: usize = 1;
|
pub const MIN_PLAYER_NAME_LENGTH: usize = 1;
|
||||||
pub const MAX_PLAYER_NAME_LENGTH: usize = 10;
|
pub const MAX_PLAYER_NAME_LENGTH: usize = 10;
|
||||||
|
|
||||||
|
pub const MIN_STRIKE_TIMEOUT: usize = 5;
|
||||||
|
pub const MAX_STRIKE_TIMEOUT: usize = 90;
|
||||||
|
@ -489,6 +489,7 @@ mod test {
|
|||||||
boats_str: "1,1".to_string(),
|
boats_str: "1,1".to_string(),
|
||||||
boats_can_touch: false,
|
boats_can_touch: false,
|
||||||
player_continue_on_hit: false,
|
player_continue_on_hit: false,
|
||||||
|
strike_timeout: None,
|
||||||
bot_type: BotType::Random,
|
bot_type: BotType::Random,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::consts::*;
|
use crate::consts::*;
|
||||||
use crate::data::{BotType, PlayConfiguration};
|
use crate::data::{BotType, PlayConfiguration};
|
||||||
use serde_with::{serde_as, DisplayFromStr};
|
use serde_with::{serde_as, DisplayFromStr, NoneAsEmptyString};
|
||||||
|
|
||||||
#[serde_as]
|
#[serde_as]
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq)]
|
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq)]
|
||||||
@ -15,6 +15,8 @@ pub struct GameRules {
|
|||||||
pub boats_can_touch: bool,
|
pub boats_can_touch: bool,
|
||||||
#[serde_as(as = "DisplayFromStr")]
|
#[serde_as(as = "DisplayFromStr")]
|
||||||
pub player_continue_on_hit: bool,
|
pub player_continue_on_hit: bool,
|
||||||
|
#[serde_as(as = "NoneAsEmptyString")]
|
||||||
|
pub strike_timeout: Option<usize>,
|
||||||
pub bot_type: BotType,
|
pub bot_type: BotType,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +38,7 @@ impl GameRules {
|
|||||||
.join(","),
|
.join(","),
|
||||||
boats_can_touch: MULTI_PLAYER_BOATS_CAN_TOUCH,
|
boats_can_touch: MULTI_PLAYER_BOATS_CAN_TOUCH,
|
||||||
player_continue_on_hit: MULTI_PLAYER_PLAYER_CAN_CONTINUE_AFTER_HIT,
|
player_continue_on_hit: MULTI_PLAYER_PLAYER_CAN_CONTINUE_AFTER_HIT,
|
||||||
|
strike_timeout: Some(30),
|
||||||
bot_type: BotType::Smart,
|
bot_type: BotType::Smart,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,6 +124,16 @@ impl GameRules {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(timeout) = self.strike_timeout {
|
||||||
|
if timeout < config.min_strike_timeout {
|
||||||
|
errors.push("Strike timeout is too short!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if timeout > config.max_strike_timeout {
|
||||||
|
errors.push("Strike timeout is too long!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
errors
|
errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +59,8 @@ pub struct PlayConfiguration {
|
|||||||
pub ordinate_alphabet: &'static str,
|
pub ordinate_alphabet: &'static str,
|
||||||
pub min_player_name_len: usize,
|
pub min_player_name_len: usize,
|
||||||
pub max_player_name_len: usize,
|
pub max_player_name_len: usize,
|
||||||
|
pub min_strike_timeout: usize,
|
||||||
|
pub max_strike_timeout: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PlayConfiguration {
|
impl Default for PlayConfiguration {
|
||||||
@ -76,6 +78,8 @@ impl Default for PlayConfiguration {
|
|||||||
ordinate_alphabet: ALPHABET,
|
ordinate_alphabet: ALPHABET,
|
||||||
min_player_name_len: MIN_PLAYER_NAME_LENGTH,
|
min_player_name_len: MIN_PLAYER_NAME_LENGTH,
|
||||||
max_player_name_len: MAX_PLAYER_NAME_LENGTH,
|
max_player_name_len: MAX_PLAYER_NAME_LENGTH,
|
||||||
|
min_strike_timeout: MIN_STRIKE_TIMEOUT,
|
||||||
|
max_strike_timeout: MAX_STRIKE_TIMEOUT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,6 +163,7 @@ pub async fn start_server(args: Args) -> std::io::Result<()> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use crate::data::GameRules;
|
||||||
use crate::server::BotPlayQuery;
|
use crate::server::BotPlayQuery;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -177,4 +178,20 @@ mod test {
|
|||||||
|
|
||||||
assert_eq!(query, des)
|
assert_eq!(query, des)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_bot_request_serialize_deserialize_no_timeout() {
|
||||||
|
let query = BotPlayQuery {
|
||||||
|
rules: GameRules {
|
||||||
|
strike_timeout: None,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
player_name: "Player".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let string = serde_urlencoded::to_string(&query).unwrap();
|
||||||
|
let des = serde_urlencoded::from_str(&string).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(query, des)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user