Can select bot type

This commit is contained in:
Pierre HUBERT 2022-10-02 18:00:45 +02:00
parent 46ab1fec9a
commit c9f643e224
5 changed files with 113 additions and 24 deletions

View File

@ -14,16 +14,11 @@ use tui::Terminal;
use cli_player::server::start_server_if_missing; use cli_player::server::start_server_if_missing;
use cli_player::ui_screens::*; use cli_player::ui_screens::*;
use sea_battle_backend::data::{GameRules, PlayConfiguration};
async fn run_app<B: Backend>(terminal: &mut Terminal<B>) -> Result<(), Box<dyn Error>> { async fn run_app<B: Backend>(terminal: &mut Terminal<B>) -> Result<(), Box<dyn Error>> {
// Temporary code // Temporary code
let res = configure_game_rules::configure_play_rules( let res = select_bot_type::select_bot_type(terminal)?;
GameRules::default(), Err(io::Error::new(
PlayConfiguration::default(),
terminal,
)?;
Err(std::io::Error::new(
ErrorKind::Other, ErrorKind::Other,
format!("configured rules: {:?}", res), format!("configured rules: {:?}", res),
))?; ))?;

View File

@ -12,20 +12,15 @@ use tui::style::{Color, Style};
use tui::widgets::*; use tui::widgets::*;
use tui::{Frame, Terminal}; use tui::{Frame, Terminal};
use sea_battle_backend::data::{GameRules, PlayConfiguration}; use sea_battle_backend::data::GameRules;
use crate::constants::TICK_RATE; use crate::constants::TICK_RATE;
use crate::ui_screens::utils::centered_rect_size; use crate::ui_screens::utils::centered_rect_size;
use crate::ui_screens::ScreenResult;
use crate::ui_widgets::button_widget::ButtonWidget; use crate::ui_widgets::button_widget::ButtonWidget;
use crate::ui_widgets::checkbox_widget::CheckboxWidget; use crate::ui_widgets::checkbox_widget::CheckboxWidget;
use crate::ui_widgets::text_editor_widget::TextEditorWidget; use crate::ui_widgets::text_editor_widget::TextEditorWidget;
#[derive(Debug)]
pub enum ConfigureGameRulesResult {
Ok(GameRules),
Canceled,
}
#[derive(num_derive::FromPrimitive, num_derive::ToPrimitive, Eq, PartialEq)] #[derive(num_derive::FromPrimitive, num_derive::ToPrimitive, Eq, PartialEq)]
enum EditingField { enum EditingField {
MapWidth = 0, MapWidth = 0,
@ -38,18 +33,15 @@ enum EditingField {
} }
struct GameRulesConfigurationScreen { struct GameRulesConfigurationScreen {
config: PlayConfiguration,
rules: GameRules, rules: GameRules,
curr_field: EditingField, curr_field: EditingField,
} }
pub fn configure_play_rules<B: Backend>( pub fn configure_play_rules<B: Backend>(
rules: GameRules, rules: GameRules,
config: PlayConfiguration,
terminal: &mut Terminal<B>, terminal: &mut Terminal<B>,
) -> io::Result<ConfigureGameRulesResult> { ) -> io::Result<ScreenResult<GameRules>> {
let mut model = GameRulesConfigurationScreen { let mut model = GameRulesConfigurationScreen {
config,
rules, rules,
curr_field: EditingField::OK, curr_field: EditingField::OK,
}; };
@ -68,7 +60,7 @@ pub fn configure_play_rules<B: Backend>(
if let Event::Key(key) = event::read()? { if let Event::Key(key) = event::read()? {
match key.code { match key.code {
// Quit app // Quit app
KeyCode::Char('q') => return Ok(ConfigureGameRulesResult::Canceled), KeyCode::Char('q') => return Ok(ScreenResult::Canceled),
// Navigate between fields // Navigate between fields
KeyCode::Up | KeyCode::Left => cursor_pos -= 1, KeyCode::Up | KeyCode::Left => cursor_pos -= 1,
@ -77,11 +69,11 @@ pub fn configure_play_rules<B: Backend>(
// Submit results // Submit results
KeyCode::Enter => { KeyCode::Enter => {
if model.curr_field == EditingField::Cancel { if model.curr_field == EditingField::Cancel {
return Ok(ConfigureGameRulesResult::Canceled); return Ok(ScreenResult::Canceled);
} }
if model.curr_field == EditingField::OK && model.rules.is_valid() { if model.curr_field == EditingField::OK && model.rules.is_valid() {
return Ok(ConfigureGameRulesResult::Ok(model.rules)); return Ok(ScreenResult::Ok(model.rules));
} }
} }

View File

@ -1,3 +1,10 @@
pub mod configure_game_rules; pub mod configure_game_rules;
pub mod select_bot_type;
pub mod select_play_mode; pub mod select_play_mode;
pub mod utils; pub mod utils;
#[derive(Debug)]
pub enum ScreenResult<E> {
Ok(E),
Canceled,
}

View File

@ -0,0 +1,95 @@
use std::io;
use std::time::{Duration, Instant};
use crossterm::event;
use crossterm::event::{Event, KeyCode};
use tui::backend::Backend;
use tui::style::{Color, Modifier, Style};
use tui::text::*;
use tui::widgets::*;
use tui::{Frame, Terminal};
use sea_battle_backend::data::{BotDescription, BotType, PlayConfiguration};
use crate::constants::TICK_RATE;
use crate::ui_screens::utils::centered_rect_size;
use crate::ui_screens::ScreenResult;
struct SelectPlayModeScreen {
state: ListState,
curr_selection: usize,
types: Vec<BotDescription>,
}
pub fn select_bot_type<B: Backend>(
terminal: &mut Terminal<B>,
) -> io::Result<ScreenResult<BotType>> {
let types = PlayConfiguration::default().bot_types;
let mut model = SelectPlayModeScreen {
state: Default::default(),
curr_selection: types.len() - 1,
types,
};
let mut last_tick = Instant::now();
loop {
model.state.select(Some(model.curr_selection));
terminal.draw(|f| ui(f, &mut model))?;
let timeout = TICK_RATE
.checked_sub(last_tick.elapsed())
.unwrap_or_else(|| Duration::from_secs(0));
if event::poll(timeout)? {
if let Event::Key(key) = event::read()? {
match key.code {
KeyCode::Char('q') => return Ok(ScreenResult::Canceled),
KeyCode::Enter => {
return Ok(ScreenResult::Ok(model.types[model.curr_selection].r#type));
}
KeyCode::Down => model.curr_selection += 1,
KeyCode::Up => model.curr_selection += model.types.len() - 1,
_ => {}
}
model.curr_selection %= model.types.len();
}
}
if last_tick.elapsed() >= TICK_RATE {
last_tick = Instant::now();
}
}
}
fn ui<B: Backend>(f: &mut Frame<B>, model: &mut SelectPlayModeScreen) {
let area = centered_rect_size(60, model.types.len() as u16 * 2 + 2, f.size());
// Create a List from all list items and highlight the currently selected one
let items = model
.types
.iter()
.map(|bot| {
ListItem::new(vec![
Spans::from(bot.name),
Spans::from(Span::styled(
bot.description,
Style::default().add_modifier(Modifier::ITALIC),
)),
])
})
.collect::<Vec<_>>();
let items = List::new(items)
.block(
Block::default()
.title("Select bot type")
.borders(Borders::ALL),
)
.highlight_style(
Style::default()
.fg(Color::Green)
.add_modifier(Modifier::BOLD),
)
.highlight_symbol(">> ");
f.render_stateful_widget(items, area, &mut model.state);
}

View File

@ -11,9 +11,9 @@ pub enum BotType {
#[derive(serde::Serialize)] #[derive(serde::Serialize)]
pub struct BotDescription { pub struct BotDescription {
r#type: BotType, pub r#type: BotType,
name: &'static str, pub name: &'static str,
description: &'static str, pub description: &'static str,
} }
#[derive(serde::Serialize)] #[derive(serde::Serialize)]