Add strike timeout setting
This commit is contained in:
		@@ -5,6 +5,9 @@ use std::time::{Duration, Instant};
 | 
			
		||||
 | 
			
		||||
use crossterm::event;
 | 
			
		||||
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::layout::{Constraint, Direction, Layout, Margin};
 | 
			
		||||
use tui::style::{Color, Style};
 | 
			
		||||
@@ -26,6 +29,7 @@ enum EditingField {
 | 
			
		||||
    MapWidth = 0,
 | 
			
		||||
    MapHeight,
 | 
			
		||||
    BoatsList,
 | 
			
		||||
    StrikeTimeout,
 | 
			
		||||
    BoatsCanTouch,
 | 
			
		||||
    PlayerContinueOnHit,
 | 
			
		||||
    BotType,
 | 
			
		||||
@@ -114,26 +118,48 @@ impl GameRulesConfigurationScreen {
 | 
			
		||||
                            {
 | 
			
		||||
                                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) => {
 | 
			
		||||
                            let val = c.to_string().parse::<usize>().unwrap_or_default();
 | 
			
		||||
 | 
			
		||||
                            if self.curr_field == EditingField::MapWidth {
 | 
			
		||||
                                if self.rules.map_width <= MAX_MAP_WIDTH {
 | 
			
		||||
                                    self.rules.map_width *= 10;
 | 
			
		||||
                                    self.rules.map_width += val;
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            if self.curr_field == EditingField::MapHeight {
 | 
			
		||||
                                if self.rules.map_height <= MAX_MAP_HEIGHT {
 | 
			
		||||
                                    self.rules.map_height *= 10;
 | 
			
		||||
                                    self.rules.map_height += val;
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            if self.curr_field == EditingField::BoatsList {
 | 
			
		||||
                                if self.rules.boats_list().len() < MAX_BOATS_NUMBER {
 | 
			
		||||
                                    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>) {
 | 
			
		||||
        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);
 | 
			
		||||
        f.render_widget(block, area);
 | 
			
		||||
@@ -161,11 +187,12 @@ impl GameRulesConfigurationScreen {
 | 
			
		||||
        let chunks = Layout::default()
 | 
			
		||||
            .direction(Direction::Vertical)
 | 
			
		||||
            .constraints([
 | 
			
		||||
                Constraint::Length(3),
 | 
			
		||||
                Constraint::Length(3),
 | 
			
		||||
                Constraint::Length(3),
 | 
			
		||||
                Constraint::Length(1),
 | 
			
		||||
                Constraint::Length(1),
 | 
			
		||||
                Constraint::Length(3), // Map width
 | 
			
		||||
                Constraint::Length(3), // Map height
 | 
			
		||||
                Constraint::Length(3), // Boats list
 | 
			
		||||
                Constraint::Length(3), // Strike timeout
 | 
			
		||||
                Constraint::Length(1), // Boats can touch
 | 
			
		||||
                Constraint::Length(1), // Player continue on hit
 | 
			
		||||
                Constraint::Length(3), // Bot type
 | 
			
		||||
                Constraint::Length(1), // Margin
 | 
			
		||||
                Constraint::Length(1), // Buttons
 | 
			
		||||
@@ -203,6 +230,13 @@ impl GameRulesConfigurationScreen {
 | 
			
		||||
        );
 | 
			
		||||
        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(
 | 
			
		||||
            "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 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_can_touch: false,
 | 
			
		||||
            player_continue_on_hit: false,
 | 
			
		||||
            strike_timeout: None,
 | 
			
		||||
            bot_type: BotType::Random,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
use crate::consts::*;
 | 
			
		||||
use crate::data::{BotType, PlayConfiguration};
 | 
			
		||||
use serde_with::{serde_as, DisplayFromStr};
 | 
			
		||||
use serde_with::{serde_as, DisplayFromStr, NoneAsEmptyString};
 | 
			
		||||
 | 
			
		||||
#[serde_as]
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq)]
 | 
			
		||||
@@ -15,6 +15,8 @@ pub struct GameRules {
 | 
			
		||||
    pub boats_can_touch: bool,
 | 
			
		||||
    #[serde_as(as = "DisplayFromStr")]
 | 
			
		||||
    pub player_continue_on_hit: bool,
 | 
			
		||||
    #[serde_as(as = "NoneAsEmptyString")]
 | 
			
		||||
    pub strike_timeout: Option<usize>,
 | 
			
		||||
    pub bot_type: BotType,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -36,6 +38,7 @@ impl GameRules {
 | 
			
		||||
                .join(","),
 | 
			
		||||
            boats_can_touch: MULTI_PLAYER_BOATS_CAN_TOUCH,
 | 
			
		||||
            player_continue_on_hit: MULTI_PLAYER_PLAYER_CAN_CONTINUE_AFTER_HIT,
 | 
			
		||||
            strike_timeout: Some(30),
 | 
			
		||||
            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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -59,6 +59,8 @@ pub struct PlayConfiguration {
 | 
			
		||||
    pub ordinate_alphabet: &'static str,
 | 
			
		||||
    pub min_player_name_len: usize,
 | 
			
		||||
    pub max_player_name_len: usize,
 | 
			
		||||
    pub min_strike_timeout: usize,
 | 
			
		||||
    pub max_strike_timeout: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for PlayConfiguration {
 | 
			
		||||
@@ -76,6 +78,8 @@ impl Default for PlayConfiguration {
 | 
			
		||||
            ordinate_alphabet: ALPHABET,
 | 
			
		||||
            min_player_name_len: MIN_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)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use crate::data::GameRules;
 | 
			
		||||
    use crate::server::BotPlayQuery;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
@@ -177,4 +178,20 @@ mod test {
 | 
			
		||||
 | 
			
		||||
        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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user