diff options
| -rw-r--r-- | src/consulter.rs | 12 | ||||
| -rw-r--r-- | src/gui.rs | 60 | ||||
| -rw-r--r-- | src/main.rs | 153 |
3 files changed, 137 insertions, 88 deletions
diff --git a/src/consulter.rs b/src/consulter.rs index a8e666f..7c7bca7 100644 --- a/src/consulter.rs +++ b/src/consulter.rs @@ -3,11 +3,12 @@ use std::io::{Error, ErrorKind, Result, Write}; use std::process::Command; use crate::game::*; +use crate::gui::*; use crate::parser::*; const FILE_NAME: &str = "/tmp/takwrap_ptn_consult.ptn"; -pub fn consult_next_action(game: &Game, proc: &str) -> Result<Action> { +pub fn consult_next_action(game: &Game, proc: &str) -> Result<GUIResult> { let file = File::create(FILE_NAME)?; write!(&file, "{}", game)?; @@ -16,9 +17,10 @@ pub fn consult_next_action(game: &Game, proc: &str) -> Result<Action> { match String::from_utf8(output.stdout) { Err(e) => Err(Error::new(ErrorKind::InvalidData, e)), - Ok(string) => match parse_action(game.query_stone_owner(), &string.trim()) { - Err(string) => Err(Error::new(ErrorKind::InvalidData, string)), - Ok(action) => Ok(action), - }, + Ok(raw) => Ok(GUIResult::Raw(raw)), + // Ok(string) => match parse_action(game.query_stone_owner(), &string.trim()) { + // Err(string) => Err(Error::new(ErrorKind::InvalidData, string)), + // Ok(action) => GUIResult::Selected(action), + // }, } } @@ -5,6 +5,22 @@ pub use pancurses::*; use crate::game::*; +#[derive(PartialEq)] +pub enum GUIResult { + Undo, + SwitchElement, + Raw(String), +} + +impl GUIResult { + pub fn is_raw(&self) -> bool { + match self { + GUIResult::Raw(_) => true, + _ => false, + } + } +} + pub struct GameLog { action_window: Window, pieces_window: Window, @@ -23,13 +39,17 @@ impl GameLog { white_colour: u8, black_colour: u8, ) -> GameLog { - GameLog { + let gl = GameLog { pieces_window: newwin(4, width, ypos, xpos), - action_window: newwin(height, width, ypos + 5, xpos), + action_window: newwin(height - 5, width, ypos + 5, xpos), has_colours: has_colours, white_colour: white_colour, black_colour: black_colour, - } + }; + + gl.action_window.setscrreg(0, height); + gl.action_window.scrollok(true); + gl } pub fn update(&self, game: &Game) { @@ -78,7 +98,7 @@ impl GameLog { self.pieces_window.attrset(Attribute::Normal); self.pieces_window.mv(3, 0); - self.pieces_window.addstr("Player: "); + self.pieces_window.addstr("Turn: "); match game.query_current_player() { Player::Black => { if self.has_colours { @@ -115,11 +135,6 @@ pub struct BoardGUI { black_colour: u8, } -pub enum BGAction { - Quit, - Selected(Action), -} - impl BoardGUI { fn draw_grid(&self) { let s: i32 = self.size as i32; @@ -224,7 +239,7 @@ impl BoardGUI { self.board_window.untouch(); } - pub fn read_action(&mut self) -> BGAction { + pub fn read_action(&mut self) -> GUIResult { loop { if let Some(key) = self.board_window.getch() { match key { @@ -252,7 +267,7 @@ impl BoardGUI { self.mv_to_cell(&self.cur_cell, 0); } } - Character('q') => return BGAction::Quit, + Character('q') => (), x => { self.board_window.addstr(format!("{:?}", x)); @@ -285,7 +300,12 @@ impl BoardGUI { for i in 0..s * s { let y: i32 = i / s; let x: i32 = i % s; - cell_windows.push(newwin(h - 1, w - 1, (s - y - 1) * h + 1, hoff + x * w + 1)); + cell_windows.push(newwin( + h - 1, + w - 1, + (s - y - 1) * h + 1 + ypos, + hoff + x * w + 1 + xpos, + )); } let bg = BoardGUI { @@ -357,7 +377,7 @@ impl ActionEntry { ae } - pub fn read_action(&self) -> String { + pub fn read_action(&self) -> GUIResult { self.window.mv(0, self.xstart); self.window.refresh(); @@ -365,8 +385,9 @@ impl ActionEntry { loop { if let Some(inp) = self.window.getch() { match inp { - Character('\n') => return result, - KeyEnter => return result, // TODO: is this ever used? + Character('\t') => return GUIResult::SwitchElement, + Character('\n') => break, + Character(c) => { // Annoyingly there are two backspace possibilities let ascii = c as usize; @@ -392,11 +413,15 @@ impl ActionEntry { self.window.mv(y, x - 1); } } + + KeyEnter => break, // TODO: is this ever used? _ => (), }; } self.window.refresh(); } + self.clear_entry_area(); + return GUIResult::Raw(result); } } @@ -415,12 +440,11 @@ impl MessageLog { has_colours: bool, red_colour: u8, ) -> MessageLog { - let ml = MessageLog { + MessageLog { window: newwin(height, width, ypos, xpos), has_colours: has_colours, red_colour: red_colour, - }; - ml + } } pub fn log_message(&self, str: String) { diff --git a/src/main.rs b/src/main.rs index e46a855..9ddaa50 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,9 +33,6 @@ fn init_gui(size: u8) -> GUI { noecho(); curs_set(2); - let width = screen.get_max_x(); - let height = screen.get_max_y(); - let has_colours = has_colors(); let (black_colour, white_colour, red_colour): (u8, u8, u8) = (1, 2, 3); @@ -47,17 +44,28 @@ fn init_gui(size: u8) -> GUI { init_pair(red_colour as i16, COLOR_RED, -1); } + screen.addstr(format!( + "takwrap {} | <TAB> to switch GUI element", + env!("CARGO_PKG_VERSION").to_string() + )); + + screen.mvchgat(0, 0, -1, A_REVERSE, -1); + let voff = 2; + + let width = screen.get_max_x(); + let height = screen.get_max_y() - voff; + screen.refresh(); let cell_height = 4; let cell_width = 8; let hoff = 3; - let board_bottom = (size * cell_height + 3) as i32; - let board_right = (size * cell_width + hoff + 3) as i32; + let board_bottom = voff + (size * cell_height + 3) as i32; + let board_right = (hoff + size * cell_width + 3) as i32; let board_gui = BoardGUI::new( - 0, + voff, 0, size, cell_height, @@ -69,10 +77,10 @@ fn init_gui(size: u8) -> GUI { ); let game_log = GameLog::new( - 0, + voff, board_right, height, - 20, + width - board_right, has_colours, white_colour, black_colour, @@ -101,23 +109,6 @@ fn init_gui(size: u8) -> GUI { } } -fn human_action(gui: &GUI, game: &Game) -> Result<Action, Error> { - loop { - let inp = gui.action_entry.read_action(); - match parse_action(game.query_stone_owner(), &inp) { - Err(err) => { - gui.message_log - .log_error(format!("Input \"{}\": {}", inp, err)); - gui.action_entry.clear_entry_area(); - } - Ok(action) => { - gui.action_entry.clear_entry_area(); - return Ok(action); - } - } - } -} - fn main() { let mut p1_name = match env::var("USER") { Err(_) => String::from("Player1"), @@ -215,68 +206,100 @@ fn main() { gui.game_log.update(&game); loop { - let inp = match game.query_current_player() { + let player = game.query_current_player(); + let engine_turn = call_proc + && ((p1_white && player == Player::Black) || ((!p1_white) && player == Player::White)); + + let result = match player { Player::Black => { if call_proc && p1_white { + gui.message_log + .log_message(String::from("Opponent is thinking...")); consult_next_action(&game, &engine) } else { - human_action(&gui, &game) + Ok(gui.action_entry.read_action()) } } Player::White => { if call_proc && (!p1_white) { + gui.message_log + .log_message(String::from("Opponent is thinking...")); consult_next_action(&game, &engine) } else { - human_action(&gui, &game) + Ok(gui.action_entry.read_action()) } } }; - match inp { - Err(err) => { - gui.message_log.log_error(format!("{}", err)); + match result { + Err(e) => { + gui.message_log.log_error(format!("Engine error: {}", e)); break; } - Ok(act) => { - let stone_owner = game.query_stone_owner(); - let player = game.query_current_player(); - let pn = game.query_current_player_name(); - - gui.message_log.log_message(format!( - "{} performs {}{}", - pn, - act, - if stone_owner != player { - format!(" with a {} stone.", stone_owner) - } else { - String::from(".") + Ok(gui_result) => match gui_result { + GUIResult::SwitchElement => { + if engine_turn { + gui.message_log.log_error(String::from( + "Internal error: engine passed switch-element message somehow.", + )); + break; } - )); - match game.perform_action(act) { - Err(e) => { - gui.message_log.log_error(e); - let current = game.query_current_player(); - if call_proc - && ((p1_white && current == Player::Black) - || ((!p1_white) && current == Player::White)) - { - gui.message_log.log_error(String::from( - "Error: external and internal game states disagree.", - )); - break; - } + } + GUIResult::Undo => { + if engine_turn { + gui.message_log.log_error(String::from( + "Internal error: engine passed undo message somehow.", + )); + break; } + } + GUIResult::Raw(raw) => { + let stone_owner = game.query_stone_owner(); + let act = parse_action(stone_owner, &raw); + match act { + Err(err) => { + gui.message_log.log_error(format!("Input {}: {}", raw, err)); + if engine_turn { + break; + } + } + Ok(act) => { + let pn = game.query_current_player_name(); + gui.message_log.log_message(format!( + "{} performs {}{}", + pn, + act, + if stone_owner != player { + format!(" with a {} stone.", stone_owner) + } else { + String::from(".") + } + )); + match game.perform_action(act) { + Err(e) => { + gui.message_log.log_error(e); + let current = game.query_current_player(); + if engine_turn { + gui.message_log.log_error(String::from( + "Error: external and internal game states disagree.", + )); + break; + } + } - Ok((pos_vec, win)) => { - gui.board_gui.update(&game, pos_vec); - gui.game_log.update(&game); - if let Some(w) = win { - gui.message_log.log_message(format!("{}", w)); - break; + Ok((pos_vec, win)) => { + gui.board_gui.update(&game, pos_vec); + gui.game_log.update(&game); + if let Some(w) = win { + gui.message_log.log_message(format!("{}", w)); + break; + } + } + } } } } - } + }, } } |
