diff --git a/rust/cli_player/src/ui_screens/set_boats_layout.rs b/rust/cli_player/src/ui_screens/set_boats_layout.rs index b3433d0..d182db4 100644 --- a/rust/cli_player/src/ui_screens/set_boats_layout.rs +++ b/rust/cli_player/src/ui_screens/set_boats_layout.rs @@ -1,8 +1,9 @@ +use std::collections::HashMap; use std::io; use std::time::{Duration, Instant}; use crossterm::event; -use crossterm::event::{Event, KeyCode}; +use crossterm::event::{Event, KeyCode, MouseButton, MouseEventKind}; use tui::backend::Backend; use tui::style::*; use tui::{Frame, Terminal}; @@ -19,6 +20,8 @@ struct SetBotsLayoutScreen { layout: BoatsLayout, } +type CoordinatesMapper = HashMap; + pub fn set_boat_layout( rules: &GameRules, terminal: &mut Terminal, @@ -29,16 +32,19 @@ pub fn set_boat_layout( .expect("Failed to generate initial boats layout"), }; + let mut coordinates_mapper = CoordinatesMapper::default(); + let mut last_tick = Instant::now(); loop { - terminal.draw(|f| ui(f, &mut model, rules))?; + terminal.draw(|f| coordinates_mapper = 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()? { + let event = event::read()?; + if let Event::Key(key) = &event { match key.code { KeyCode::Char('q') => return Ok(ScreenResult::Canceled), KeyCode::Enter => { @@ -51,6 +57,22 @@ pub fn set_boat_layout( } model.curr_boat %= model.layout.number_of_boats(); + } else if let Event::Mouse(mouse) = event { + if MouseEventKind::Up(MouseButton::Left) == mouse.kind { + let src_pos = Coordinates::new(mouse.column, mouse.row); + if let Some(pos) = coordinates_mapper.get(&src_pos) { + match model.layout.find_boat_at_position(*pos).cloned() { + // Change of selected boat + Some(b) if b != model.layout.0[model.curr_boat] => { + model.curr_boat = + model.layout.0.iter().position(|s| s == &b).unwrap(); + } + + // Move current boat + _ => model.layout.0[model.curr_boat].start = *pos, + } + } + } } } if last_tick.elapsed() >= TICK_RATE { @@ -59,7 +81,11 @@ pub fn set_boat_layout( } } -fn ui(f: &mut Frame, model: &mut SetBotsLayoutScreen, rules: &GameRules) { +fn ui( + f: &mut Frame, + model: &mut SetBotsLayoutScreen, + rules: &GameRules, +) -> CoordinatesMapper { let current_boat = ColoredCells { color: Color::Green, cells: model.layout.0[model.curr_boat].all_coordinates(), @@ -77,11 +103,20 @@ fn ui(f: &mut Frame, model: &mut SetBotsLayoutScreen, rules: &Gam cells: other_boats_cells, }; + let mut coordinates_mapper = HashMap::new(); + let game_map_widget = GameMapWidget::new(rules) .set_default_empty_char(' ') .add_colored_cells(current_boat) .add_colored_cells(other_boats) .set_title("Choose your boat layout") + .set_yield_func(|c, r| { + for i in 0..r.width { + for j in 0..r.height { + coordinates_mapper.insert(Coordinates::new(r.x + i, r.y + j), c); + } + } + }) .set_legend( "n next boat \n\ r rotate boat \n\n\ @@ -92,4 +127,6 @@ fn ui(f: &mut Frame, model: &mut SetBotsLayoutScreen, rules: &Gam let (w, h) = game_map_widget.estimated_size(); let area = centered_rect_size(w, h, &f.size()); f.render_widget(game_map_widget, area); + + coordinates_mapper } diff --git a/rust/cli_player/src/ui_widgets/game_map_widget.rs b/rust/cli_player/src/ui_widgets/game_map_widget.rs index de505c7..d10dab0 100644 --- a/rust/cli_player/src/ui_widgets/game_map_widget.rs +++ b/rust/cli_player/src/ui_widgets/game_map_widget.rs @@ -20,6 +20,7 @@ pub struct GameMapWidget<'a> { colored_cells: Vec, title: Option, legend: Option, + yield_coordinates: Option>, } impl<'a> GameMapWidget<'a> { @@ -30,6 +31,7 @@ impl<'a> GameMapWidget<'a> { colored_cells: vec![], title: None, legend: None, + yield_coordinates: None, } } @@ -53,6 +55,14 @@ impl<'a> GameMapWidget<'a> { self } + pub fn set_yield_func(mut self, func: F) -> Self + where + F: 'a + FnMut(Coordinates, Rect), + { + self.yield_coordinates = Some(Box::new(func)); + self + } + pub fn grid_size(&self) -> (u16, u16) { let w = self.rules.map_width as u16 * 2 + 1; let h = self.rules.map_height as u16 * 2 + 1; @@ -76,7 +86,7 @@ impl<'a> GameMapWidget<'a> { } impl<'a> Widget for GameMapWidget<'a> { - fn render(self, area: Rect, buf: &mut Buffer) { + fn render(mut self, area: Rect, buf: &mut Buffer) { let alphabet = PlayConfiguration::default().ordinate_alphabet; let symbols = BorderType::line_symbols(BorderType::Plain); @@ -92,12 +102,16 @@ impl<'a> Widget for GameMapWidget<'a> { // Paint game grid for y in 0..(self.rules.map_height + 1) { + // Header (ordinate) if y < self.rules.map_height { buf.get_mut(area.x, start_y + 2 + (y as u16 * 2)) .set_char(alphabet.chars().nth(y).unwrap()); } for x in 0..(self.rules.map_width + 1) { + let coordinates = Coordinates::new(x as i32, y as i32); + + // Header (abscissa) if x < self.rules.map_width { buf.set_string( area.x + 2 + (x as u16 * 2) - (x as u16) / 10, @@ -110,6 +124,11 @@ impl<'a> Widget for GameMapWidget<'a> { let o_x = 1 + area.x + (x as u16 * 2); let o_y = 1 + start_y + (y as u16 * 2); + let color = self + .colored_cells + .iter() + .find(|c| c.cells.contains(&coordinates)); + buf.get_mut(o_x, o_y).set_symbol(match (x, y) { (0, 0) => symbols.top_left, @@ -142,13 +161,13 @@ impl<'a> Widget for GameMapWidget<'a> { .get_mut(o_x + 1, o_y + 1) .set_char(self.default_empty_character); - if let Some(c) = self - .colored_cells - .iter() - .find(|c| c.cells.contains(&Coordinates::new(x as i32, y as i32))) - { + if let Some(c) = color { cell.set_bg(c.color); } + + if let Some(f) = self.yield_coordinates.as_mut() { + f(coordinates, Rect::new(o_x + 1, o_y + 1, 1, 1)); + } } } } diff --git a/rust/sea_battle_backend/src/data/boats_layout.rs b/rust/sea_battle_backend/src/data/boats_layout.rs index 150b1c7..b70d52f 100644 --- a/rust/sea_battle_backend/src/data/boats_layout.rs +++ b/rust/sea_battle_backend/src/data/boats_layout.rs @@ -18,12 +18,7 @@ pub enum BoatDirection { BottomRight, } -const _BOATS_DIRECTION: [BoatDirection; 4] = [ - BoatDirection::Left, - BoatDirection::Right, - BoatDirection::Up, - BoatDirection::Down, -]; +const _BOATS_DIRECTION: [BoatDirection; 2] = [BoatDirection::Right, BoatDirection::Down]; const _ALL_DIRECTIONS: [BoatDirection; 8] = [ BoatDirection::Left, @@ -54,8 +49,8 @@ impl BoatDirection { pub fn shift_coordinates(&self, coordinates: Coordinates, nth: usize) -> Coordinates { let shift = match self { - BoatDirection::Left => (1, 0), - BoatDirection::Right => (-1, 0), + BoatDirection::Left => (-1, 0), + BoatDirection::Right => (1, 0), BoatDirection::Up => (0, -1), BoatDirection::Down => (0, 1), BoatDirection::UpLeft => (-1, -1),