diff --git a/rust/cli_player/src/ui_screens/configure_game_rules.rs b/rust/cli_player/src/ui_screens/configure_game_rules.rs index e6aaa25..e4416ac 100644 --- a/rust/cli_player/src/ui_screens/configure_game_rules.rs +++ b/rust/cli_player/src/ui_screens/configure_game_rules.rs @@ -13,7 +13,8 @@ use tui::{Frame, Terminal}; use sea_battle_backend::data::GameRules; -use crate::constants::TICK_RATE; +use crate::constants::{HIGHLIGHT_COLOR, TICK_RATE}; +use crate::ui_screens::select_bot_type::SelectBotTypeScreen; use crate::ui_screens::utils::centered_rect_size; use crate::ui_screens::ScreenResult; use crate::ui_widgets::button_widget::ButtonWidget; @@ -27,6 +28,7 @@ enum EditingField { BoatsList, BoatsCanTouch, PlayerContinueOnHit, + BotType, Cancel, OK, } @@ -70,6 +72,14 @@ impl GameRulesConfigurationScreen { // Submit results KeyCode::Enter => { + if self.curr_field == EditingField::BotType { + if let ScreenResult::Ok(t) = + SelectBotTypeScreen::default().show(terminal)? + { + self.rules.bot_type = t; + } + } + if self.curr_field == EditingField::Cancel { return Ok(ScreenResult::Canceled); } @@ -143,7 +153,7 @@ impl GameRulesConfigurationScreen { } fn ui(&mut self, f: &mut Frame) { - let area = centered_rect_size(50, 16, &f.size()); + let area = centered_rect_size(50, 20, &f.size()); let block = Block::default().title("Game rules").borders(Borders::ALL); f.render_widget(block, area); @@ -156,7 +166,8 @@ impl GameRulesConfigurationScreen { Constraint::Length(3), Constraint::Length(1), Constraint::Length(1), - Constraint::Length(1), + Constraint::Length(3), // Bot type + Constraint::Length(1), // Margin Constraint::Length(1), // Buttons Constraint::Length(1), // Error message (if any) ]) @@ -206,6 +217,22 @@ impl GameRulesConfigurationScreen { ); f.render_widget(editor, chunks[EditingField::PlayerContinueOnHit as usize]); + // Select bot type + let bot_type_text = format!("Bot type: {}", self.rules.bot_type.description().name); + let text = Paragraph::new(bot_type_text.as_str()).style( + match self.curr_field == EditingField::BotType { + false => Style::default(), + true => Style::default().fg(HIGHLIGHT_COLOR), + }, + ); + f.render_widget( + text, + chunks[EditingField::BotType as usize].inner(&Margin { + horizontal: 0, + vertical: 1, + }), + ); + // Buttons let buttons_chunk = Layout::default() .direction(Direction::Horizontal) diff --git a/rust/cli_player/src/ui_screens/select_bot_type.rs b/rust/cli_player/src/ui_screens/select_bot_type.rs index 02ccc75..cd28afd 100644 --- a/rust/cli_player/src/ui_screens/select_bot_type.rs +++ b/rust/cli_player/src/ui_screens/select_bot_type.rs @@ -27,7 +27,7 @@ impl Default for SelectBotTypeScreen { Self { state: Default::default(), curr_selection: types.len() - 1, - types, + types: types.to_vec(), } } } diff --git a/rust/sea_battle_backend/src/data/game_rules.rs b/rust/sea_battle_backend/src/data/game_rules.rs index 977d1b2..8efd961 100644 --- a/rust/sea_battle_backend/src/data/game_rules.rs +++ b/rust/sea_battle_backend/src/data/game_rules.rs @@ -36,7 +36,7 @@ impl GameRules { .join(","), boats_can_touch: MULTI_PLAYER_BOATS_CAN_TOUCH, player_continue_on_hit: MULTI_PLAYER_PLAYER_CAN_CONTINUE_AFTER_HIT, - bot_type: BotType::Random, + bot_type: BotType::Smart, } } diff --git a/rust/sea_battle_backend/src/data/play_config.rs b/rust/sea_battle_backend/src/data/play_config.rs index 8f41872..b2eafa0 100644 --- a/rust/sea_battle_backend/src/data/play_config.rs +++ b/rust/sea_battle_backend/src/data/play_config.rs @@ -9,13 +9,42 @@ pub enum BotType { Smart, } -#[derive(serde::Serialize)] +impl BotType { + pub fn description(&self) -> &'static BotDescription { + BOTS_TYPES.iter().find(|d| d.r#type == *self).unwrap() + } +} + +#[derive(serde::Serialize, Clone)] pub struct BotDescription { pub r#type: BotType, pub name: &'static str, pub description: &'static str, } +const BOTS_TYPES: [BotDescription; 4] = [ + BotDescription { + r#type: BotType::Linear, + name: "Linear", + description: "Linear strike. Shoot A1, A2, A3, ..., B1, B2, ...", + }, + BotDescription { + r#type: BotType::Random, + name: "Random", + description: "Random search. Random strike.", + }, + BotDescription { + r#type: BotType::Intermediate, + name: "Intermediate", + description: "Random search. Intelligent strike.", + }, + BotDescription { + r#type: BotType::Smart, + name: "Smart", + description: "Smart search. Smart strike.", + }, +]; + #[derive(serde::Serialize)] pub struct PlayConfiguration { pub min_boat_len: usize, @@ -26,7 +55,7 @@ pub struct PlayConfiguration { pub max_map_height: usize, pub min_boats_number: usize, pub max_boats_number: usize, - pub bot_types: Vec, + pub bot_types: &'static [BotDescription], pub ordinate_alphabet: &'static str, } @@ -41,28 +70,7 @@ impl Default for PlayConfiguration { max_map_height: MAX_MAP_HEIGHT, min_boats_number: MIN_BOATS_NUMBER, max_boats_number: MAX_BOATS_NUMBER, - bot_types: vec![ - BotDescription { - r#type: BotType::Linear, - name: "Linear", - description: "Linear strike. Shoot A1, A2, A3, ..., B1, B2, ...", - }, - BotDescription { - r#type: BotType::Random, - name: "Ranom", - description: "Random search. Random strike.", - }, - BotDescription { - r#type: BotType::Intermediate, - name: "Intermediate", - description: "Random search. Intelligent strike.", - }, - BotDescription { - r#type: BotType::Smart, - name: "Smart", - description: "Smart search. Smart strike.", - }, - ], + bot_types: &BOTS_TYPES, ordinate_alphabet: ALPHABET, } }