Start to build game grid

This commit is contained in:
Pierre HUBERT 2022-10-07 10:20:21 +02:00
parent 17a4edf417
commit 7178b96077
6 changed files with 118 additions and 2 deletions

View File

@ -18,7 +18,8 @@ use sea_battle_backend::data::GameRules;
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(GameRules::default(), terminal)?; // select_bot_type::select_bot_type(terminal)?; // let res = configure_game_rules::configure_play_rules(GameRules::default(), terminal)?; // select_bot_type::select_bot_type(terminal)?;
let res = set_boats_layout::set_boat_layout(&GameRules::default(), terminal)?; // select_bot_type::select_bot_type(terminal)?;
Err(io::Error::new( Err(io::Error::new(
ErrorKind::Other, ErrorKind::Other,
format!("result: {:?}", res), format!("result: {:?}", res),

View File

@ -1,6 +1,7 @@
pub mod configure_game_rules; pub mod configure_game_rules;
pub mod select_bot_type; pub mod select_bot_type;
pub mod select_play_mode; pub mod select_play_mode;
pub mod set_boats_layout;
pub mod utils; pub mod utils;
#[derive(Debug)] #[derive(Debug)]

View File

@ -0,0 +1,75 @@
use std::io;
use std::time::{Duration, Instant};
use crossterm::event;
use crossterm::event::{Event, KeyCode};
use tui::backend::Backend;
use tui::style::*;
use tui::text::*;
use tui::widgets::*;
use tui::{Frame, Terminal};
use sea_battle_backend::data::*;
use crate::constants::*;
use crate::ui_screens::utils::centered_rect_size;
use crate::ui_screens::ScreenResult;
use crate::ui_widgets::game_map_widget::GameMapWidget;
struct SetBotsLayoutScreen {
curr_boat: usize,
layout: BoatsLayout,
}
pub fn set_boat_layout<B: Backend>(
rules: &GameRules,
terminal: &mut Terminal<B>,
) -> io::Result<ScreenResult<BoatsLayout>> {
let mut model = SetBotsLayoutScreen {
curr_boat: 0,
layout: BoatsLayout::gen_random_for_rules(rules)
.expect("Failed to generate initial boats layout"),
};
let mut last_tick = Instant::now();
loop {
terminal.draw(|f| ui(f, &mut model, rules))?;
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 => {
if model.layout.is_valid(rules) {
return Ok(ScreenResult::Ok(model.layout));
}
}
KeyCode::Down => model.curr_boat -= 1,
KeyCode::Up => model.curr_boat += model.layout.number_of_boats() - 1,
_ => {}
}
model.curr_boat %= model.layout.number_of_boats();
}
}
if last_tick.elapsed() >= TICK_RATE {
last_tick = Instant::now();
}
}
}
fn ui<B: Backend>(f: &mut Frame<B>, model: &mut SetBotsLayoutScreen, rules: &GameRules) {
let area = centered_rect_size(
rules.map_width as u16 + 10,
rules.map_height as u16 + 10,
f.size(),
);
let game_map_widget = GameMapWidget::new(rules);
f.render_widget(game_map_widget, area);
}

View File

@ -0,0 +1,34 @@
use sea_battle_backend::data::{GameRules, PlayConfiguration};
use tui::buffer::Buffer;
use tui::layout::Rect;
use tui::widgets::{BorderType, Widget};
pub struct GameMapWidget<'a> {
rules: &'a GameRules,
}
impl<'a> GameMapWidget<'a> {
pub fn new(rules: &'a GameRules) -> Self {
Self { rules }
}
}
impl<'a> Widget for GameMapWidget<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
let alphabet = PlayConfiguration::default().ordinate_alphabet;
let symbols = BorderType::line_symbols(BorderType::Plain);
// Paint game grid
for y in 0..(self.rules.map_height + 1) {
for x in 0..(self.rules.map_width + 1) {
let o_x = x as u16 * 2;
let o_y = y as u16 * 2;
buf.get_mut(o_x, o_y).set_symbol(symbols.cross);
buf.get_mut(o_x + 1, o_y).set_symbol(symbols.horizontal);
buf.get_mut(o_x, o_y + 1).set_symbol(symbols.vertical);
buf.get_mut(o_x + 1, o_y + 1).set_char('.');
}
}
}
}

View File

@ -1,3 +1,4 @@
pub mod button_widget; pub mod button_widget;
pub mod checkbox_widget; pub mod checkbox_widget;
pub mod game_map_widget;
pub mod text_editor_widget; pub mod text_editor_widget;

View File

@ -172,7 +172,7 @@ impl BoatPosition {
} }
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Default)] #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Default)]
pub struct BoatsLayout(Vec<BoatPosition>); pub struct BoatsLayout(pub Vec<BoatPosition>);
impl BoatsLayout { impl BoatsLayout {
/// Generate a new invalid (empty) boats layout /// Generate a new invalid (empty) boats layout
@ -331,6 +331,10 @@ impl BoatsLayout {
errors errors
} }
pub fn is_valid(&self, rules: &GameRules) -> bool {
self.errors(rules).is_empty()
}
pub fn find_boat_at_position(&self, pos: Coordinates) -> Option<&BoatPosition> { pub fn find_boat_at_position(&self, pos: Coordinates) -> Option<&BoatPosition> {
self.0.iter().find(|f| f.all_coordinates().contains(&pos)) self.0.iter().find(|f| f.all_coordinates().contains(&pos))
} }