diff --git a/src/board.rs b/src/board.rs index df7435a..53f057e 100644 --- a/src/board.rs +++ b/src/board.rs @@ -97,7 +97,7 @@ impl Board { for (i, p) in ps[0..18].iter().enumerate() { if i % 2 == 0 { //is a port - if i % 3 != 2 { + if i % 3 != 1 { orientation.rotate(); } hexes.push(Hex { diff --git a/src/display.rs b/src/display.rs new file mode 100644 index 0000000..4fe7f17 --- /dev/null +++ b/src/display.rs @@ -0,0 +1,234 @@ +use crate::logic::{Board, Position}; +use ratatui::buffer::Buffer; +use ratatui::prelude::*; + +pub struct BoardView { + buffer: Box, +} + +impl BoardView { + pub fn new(board: Box) -> BoardView { + let buffer = Box::new(board.draw()); + BoardView { buffer } + } +} + +impl ratatui::widgets::Widget for BoardView { + fn render(self, area: ratatui::layout::Rect, buf: &mut Buffer) { + buf.merge(&self.buffer); + } +} + +impl Board { + fn draw(&self) -> Buffer { + let mut b = Buffer::empty(Rect { + x: 0, + y: 0, + width: 49, + height: 28, + }); + for i in 0..self.hexes.len() { + for j in 0..self.hexes[i].len() { + if i + j < 3 || i + j > 9 { + continue; + } + b.merge(&self.draw_hex(&Position(i as i32, j as i32))); + } + } + b + } + + pub fn draw_hex(&self, p: &Position) -> Buffer { + let mut b = Buffer::empty(Rect { + x: 0, + y: 0, + width: 7, + height: 5, + }); + let get_edge = |x, y| { + let p = p.edge(&Position(x, y)); + self.edges[p.0 as usize][p.1 as usize] + }; + let get_vertex = |x, y| { + let p = p.vertex(&Position(x, y)); + self.vertices[p.0 as usize][p.1 as usize] + }; + let hex = self.hexes[p.0 as usize][p.1 as usize]; + let edge_NW = get_edge(0, -1); + let edge_SW = get_edge(1, -1); + let edge_S = get_edge(1, 0); + let vertex_SW = get_vertex(1, -1); + let vertex_SE = get_vertex(1, 0); + b.get_mut(0, 0).set_symbol(" "); + draw_edge(&mut b, edge_NW, "⧸", &[(1, 0), (0, 1)]); + draw_edge(&mut b, edge_SW, "⧹", &[(0, 2), (1, 3)]); + draw_edge( + &mut b, + edge_S, + "_", + &[(2, 3), (3, 3), (4, 3), (5, 3), (6, 3)], + ); + draw_vertex(&mut b, vertex_SW, (2, 3)); + draw_vertex(&mut b, vertex_SE, (6, 3)); + match hex { + None => (), + Some(h) => { + if self.robber.0 == p.0 && self.robber.1 == p.1 { + b.get_mut(4, 3).set_symbol(""); + } + match h.hextype { + crate::logic::HexType::Desert => (), + crate::logic::HexType::Port => { + if h.resource.is_some() { + draw_resource(&mut b, h.resource.unwrap(), (4, 1)); + } else { + b.get_mut(4, 1).set_symbol("?"); + } + draw_ports(&mut b, h.orientation.unwrap()); + } + crate::logic::HexType::Ocean => (), + crate::logic::HexType::Resource => { + draw_resource(&mut b, h.resource.unwrap(), (4, 0)); + match h.number { + None => (), + Some(n) => { + draw_number(&mut b, n as u8); + } + }; + } + } + } + }; + b.resize(Rect { + x: (p.1 * 7) as u16, + y: (p.0 * 4 + p.1 * 2) as u16, + width: 7, + height: 5, + }); + b + } +} + +fn draw_edge(b: &mut Buffer, edge: Option, symbol: &str, ps: &[(u16, u16)]) { + match edge { + None => (), + Some(edge) => { + for &(x, y) in ps { + b.get_mut(x, y) + .set_symbol(symbol) + .set_style(Style::default().bold()); + } + match edge.road { + None => (), + Some(road) => { + for &(x, y) in ps { + b.get_mut(x, y).set_fg(color_convert(road.0)); + } + } + }; + } + }; +} + +fn draw_vertex(b: &mut Buffer, vertex: Option, p: (u16, u16)) { + match vertex { + None => (), + Some(vertex) => { + if vertex.settlement.is_some() { + b.get_mut(p.0, p.1) + .set_symbol("") + .set_fg(color_convert(vertex.settlement.unwrap().0)); + } else if vertex.city.is_some() { + b.get_mut(p.0, p.1) + .set_symbol("󰴕") + .set_fg(color_convert(vertex.city.unwrap().0)); + } + } + }; +} + +fn draw_resource(b: &mut Buffer, resource: crate::logic::Resource, p: (u16, u16)) { + let (bg, fg, symbol) = match resource { + crate::logic::Resource::Brick => (Color::Red, Color::Black, "󱉏"), + crate::logic::Resource::Ore => (Color::Blue, Color::Black, ""), + crate::logic::Resource::Sheep => (Color::Gray, Color::Black, "󰅟"), + crate::logic::Resource::Wheat => (Color::Yellow, Color::Black, ""), + crate::logic::Resource::Wood => (Color::Green, Color::Black, ""), + }; + b.get_mut(p.0, p.1).set_bg(bg).set_fg(fg).set_symbol(symbol); + // .set_style(Style::default().bold()); + b.get_mut(p.0 - 1, p.1).set_bg(bg); + b.get_mut(p.0 + 1, p.1).set_bg(bg); +} + +fn draw_ports(b: &mut Buffer, orientation: Position) { + match orientation { + Position(1, 0) => { + b.get_mut(3, 2).set_symbol(""); + b.get_mut(5, 2).set_symbol(""); + } + Position(0, 1) => { + b.get_mut(5, 2).set_symbol(""); + b.get_mut(6, 1).set_symbol(""); + } + Position(-1, 1) => { + b.get_mut(6, 1).set_symbol(""); + b.get_mut(5, 0).set_symbol(""); + } + Position(-1, 0) => { + b.get_mut(5, 0).set_symbol(""); + b.get_mut(3, 0).set_symbol(""); + } + Position(0, -1) => { + b.get_mut(3, 0).set_symbol(""); + b.get_mut(2, 1).set_symbol(""); + } + Position(1, -1) => { + b.get_mut(2, 1).set_symbol(""); + b.get_mut(3, 2).set_symbol(""); + } + _ => (), + }; +} + +fn draw_number(b: &mut Buffer, number: u8) { + let style = if number == 6 || number == 8 { + Style::default().fg(Color::Red) + } else { + Style::default() + }; + b.set_string(3, 1, &format!("{number:>2}"), style); + + match number { + 2 | 12 => { + b.get_mut(4, 2).set_symbol("⠈").set_style(style); + } + 3 | 11 => { + b.get_mut(4, 2).set_symbol("⠉").set_style(style); + } + 4 | 10 => { + b.get_mut(3, 2).set_symbol("⠈").set_style(style); + b.get_mut(4, 2).set_symbol("⠉").set_style(style); + } + 5 | 9 => { + b.get_mut(3, 2).set_symbol("⠈").set_style(style); + b.get_mut(4, 2).set_symbol("⠉").set_style(style); + b.get_mut(5, 2).set_symbol("⠁").set_style(style); + } + 6 | 8 => { + b.get_mut(3, 2).set_symbol("⠉").set_style(style); + b.get_mut(4, 2).set_symbol("⠉").set_style(style); + b.get_mut(5, 2).set_symbol("⠁").set_style(style); + } + _ => (), + } +} + +fn color_convert(c: crate::logic::Color) -> Color { + match c { + crate::logic::Color::Red => Color::LightRed, + crate::logic::Color::Green => Color::LightGreen, + crate::logic::Color::Yellow => Color::LightYellow, + crate::logic::Color::Blue => Color::LightBlue, + } +} diff --git a/src/main.rs b/src/main.rs index 4e5d5d4..55334bb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,43 @@ mod board; +mod display; mod logic; +use crossterm::{ + event::{self, KeyCode, KeyEventKind}, + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, + ExecutableCommand, +}; +use logic::Board; +use ratatui::prelude::{CrosstermBackend, Terminal}; +use std::io::{stdout, Result}; /** * Three Ore Two Wheat */ -fn main() { - println!("Hello, world!"); +fn main() -> Result<()> { + stdout().execute(EnterAlternateScreen)?; + enable_raw_mode()?; + let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?; + terminal.clear()?; + + let board = Box::new(Board::new("example_seed")); + let hex = display::BoardView::new(board); + + terminal.draw(|frame| { + frame.render_widget(hex, frame.size()); + })?; + + loop { + if event::poll(std::time::Duration::from_millis(16))? { + if let event::Event::Key(key) = event::read()? { + if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') { + break; + } + } + } + } + + stdout().execute(LeaveAlternateScreen)?; + disable_raw_mode()?; + Ok(()) }