diff options
| author | tslil clingman <> | 2020-01-19 23:26:29 -0800 |
|---|---|---|
| committer | tslil clingman <> | 2020-01-19 23:26:29 -0800 |
| commit | 8110985e6636e511f0a2e85a6f30aa8ff109320d (patch) | |
| tree | 7e6c1b38d04db474aac5cdac2593878f167cac8a /src | |
| parent | 4d18fad08c185e07d030f6574963ec6de8441ee5 (diff) | |
Parsing `by hand', gui in separate module
Diffstat (limited to 'src')
| -rw-r--r-- | src/game.rs | 7 | ||||
| -rw-r--r-- | src/gui.rs | 341 | ||||
| -rw-r--r-- | src/main.rs | 350 | ||||
| -rw-r--r-- | src/parser.rs | 134 |
4 files changed, 489 insertions, 343 deletions
diff --git a/src/game.rs b/src/game.rs index 931a15f..057a307 100644 --- a/src/game.rs +++ b/src/game.rs @@ -82,10 +82,11 @@ pub enum Action { impl fmt::Display for Action { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Action::Place(player, pos, stone) => write!(f, "{}{}{}", pos, player, stone), + Action::Place(_, pos, stone) => write!(f, "{}{}", stone, pos), Action::Move(_, pos, direction, drops) => write!( f, - "{}{}{}", + "{}{}{}{}", + drops.iter().map(|&d| d as u32).sum::<u32>(), pos, direction, drops.into_iter().map(|q| q.to_string()).collect::<String>() @@ -244,6 +245,8 @@ enum TurnOrder { Normal, } +// TODO: Redo this logging story with Result<...,String> instead + pub struct Game<L> where L: Fn(String), diff --git a/src/gui.rs b/src/gui.rs new file mode 100644 index 0000000..abeb902 --- /dev/null +++ b/src/gui.rs @@ -0,0 +1,341 @@ +extern crate pancurses; + +pub use pancurses::Input::*; +pub use pancurses::*; + +use crate::game::*; + +pub struct GameLog { + action_window: Window, + pieces_window: Window, +} + +impl GameLog { + pub fn new(ypos: i32, xpos: i32, height: i32, width: i32) -> GameLog { + GameLog { + pieces_window: newwin(2, width, ypos, xpos), + action_window: newwin(height, width, ypos + 3, xpos), + } + } + + pub fn update<L: Fn(String)>(&self, game: &Game<L>) { + self.pieces_window.clear(); + self.pieces_window.addstr(format!( + "W: {:2} B: {:2}\nWC: {} BC: {}", + game.query_pieces(Player::White, Stone::Flat), + game.query_pieces(Player::Black, Stone::Flat), + game.query_pieces(Player::White, Stone::Capstone), + game.query_pieces(Player::Black, Stone::Capstone), + )); + self.pieces_window.refresh(); + + self.action_window.clear(); + self.action_window.addstr(game.query_action_lines()); + self.action_window.refresh(); + } +} + +pub struct BoardGUI { + board_window: Window, + cell_windows: Vec<Window>, + size: u8, + cell_height: u8, + cell_width: u8, + hoff: i32, + cur_cell: Position, + has_colours: bool, + white_colour: u8, + black_colour: u8, +} + +#[derive(PartialEq)] +pub enum BGAction { + Quit, + None, +} + +impl BoardGUI { + fn draw_grid(&self) { + let s: i32 = self.size as i32; + let h: i32 = self.cell_height as i32; + let w: i32 = self.cell_width as i32; + for y in 0..h * s + 1 { + for x in 0..w * s + 1 { + if y % h == 0 { + if x % w == 0 { + self.board_window.mvaddch(y, x + self.hoff, '+'); + } else { + self.board_window.mvaddch(y, x + self.hoff, '-'); + } + } else if x % w == 0 { + self.board_window.mvaddch(y, x + self.hoff, '|'); + } + } + } + for y in 0..s { + self.board_window + .mvaddstr(y * h + h / 2, 0, format!("{}.", s - y)); + } + for x in 0..s { + self.board_window.mvaddstr( + s * h + 1, + w * x + w / 2 + self.hoff, + format!("{:x}.", x + 10), + ); + } + self.board_window.refresh(); + } + + fn pos_to_idx(&self, pos: &Position) -> usize { + let y = pos.y as usize; + let x = pos.x as usize; + x + y * (self.size as usize) + } + + fn mv_to_cell(&self, pos: &Position, line: i32) { + if let Some(win) = self.cell_windows.get(self.pos_to_idx(pos)) { + win.mv(line, 0); + win.refresh(); + } + } + + fn draw_stone(&self, win: &Window, top: bool, player: &Player, stone: &Stone) { + if self.has_colours { + match player { + Player::Black => win.attrset(ColorPair(self.black_colour)), + Player::White => win.attrset(ColorPair(self.white_colour)), + }; + if top { + win.addch(match stone { + Stone::Flat => '#', + Stone::Standing => '/', + Stone::Capstone => '*', + }); + } else { + win.addstr(format!("{}", player)); + } + } else { + win.addstr(format!("{}", player)); + if top { + win.addstr(format!("{}", stone)); + } + } + } + + fn draw_stack(&self, pos: &Position, stack: &Stack) { + if let Some(win) = self.cell_windows.get(self.pos_to_idx(pos)) { + win.mv(0, 0); + let l = stack.len() as i32; + if l > 0 { + for i in 0..l { + let piece = &stack[i as usize]; + self.draw_stone(win, i == l - 1, &piece.player, &piece.stone); + } + } + win.refresh(); + } + } + + pub fn update<L: Fn(String)>(&self, game: &Game<L>) { + // Is this really the `best' way? + assert!(game.get_size() == self.size); + let mut pos = Position { x: 0, y: 0 }; + for y in 0..self.size { + pos.y = y; + for x in 0..self.size { + pos.x = x; + if let Some(stack) = game.query_square(&pos) { + self.draw_stack(&pos, stack); + } + } + } + } + + pub fn read_action(&mut self) -> BGAction { + loop { + if let Some(key) = self.board_window.getch() { + match key { + KeyDown => { + if 0 < self.cur_cell.y { + self.cur_cell.y -= 1; + self.mv_to_cell(&self.cur_cell, 0); + } + } + KeyUp => { + if self.cur_cell.y + 1 < self.size { + self.cur_cell.y += 1; + self.mv_to_cell(&self.cur_cell, 0); + } + } + KeyLeft => { + if 0 < self.cur_cell.x { + self.cur_cell.x -= 1; + self.mv_to_cell(&self.cur_cell, 0); + } + } + KeyRight => { + if self.cur_cell.x + 1 < self.size { + self.cur_cell.x += 1; + self.mv_to_cell(&self.cur_cell, 0); + } + } + Character('q') => return BGAction::Quit, + + x => { + self.board_window.addstr(format!("{:?}", x)); + self.board_window.refresh(); + } + } + } + } + } + + pub fn new( + ypos: i32, + xpos: i32, + size: u8, + cell_height: u8, + cell_width: u8, + hoff: u8, + has_colours: bool, + white_colour: u8, + black_colour: u8, + ) -> BoardGUI { + let mut cell_windows: Vec<Window> = Vec::new(); + + let hoff = hoff as i32; + let s: i32 = size as i32; + let h: i32 = cell_height as i32; + let w: i32 = cell_width as i32; + 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)); + } + + let bg = BoardGUI { + board_window: newwin(h * s + 3, w * s + 3 + hoff, ypos, xpos), + cell_windows: cell_windows, + size: size, + cell_height: cell_height, + cell_width: cell_width, + hoff: hoff, + cur_cell: Position { x: 0, y: 0 }, + has_colours: has_colours, + black_colour: black_colour, + white_colour: white_colour, + }; + + bg.board_window.keypad(true); + bg.board_window.nodelay(false); + + bg.draw_grid(); + + // for i in 0..s * s { + // let y: i32 = i / s; + // let x: i32 = i % s; + // let y = (s - y - 1) * h + 1; + // let x = hoff + x * w + 1; + // bg.cell_windows[i as usize].addstr(format!("{}:({},{})", i, y, x)); + // bg.cell_windows[i as usize].refresh(); + // } + + bg.mv_to_cell(&bg.cur_cell, 0); + bg.board_window.refresh(); + bg + } +} + +pub struct ActionEntry { + window: Window, + max_len: usize, + xstart: i32, +} + +impl ActionEntry { + pub fn new(ypos: i32, xpos: i32, max_len: usize) -> ActionEntry { + let txt = "Action entry: "; + let xstart = txt.len() as i32; + let width = max_len as i32 + xstart + 1; + + let ae = ActionEntry { + window: newwin(1, width, ypos, xpos), + max_len: max_len, + xstart: xstart, + }; + + ae.window.nodelay(false); + ae.window.keypad(true); + + ae.window.addstr(txt); + + for _i in 0..max_len { + ae.window.addch('_'); + } + + ae.window.mv(0, xstart); + ae.window.refresh(); + ae + } + + pub fn read_action(&self) -> String { + self.window.mv(0, self.xstart); + self.window.refresh(); + + let mut result = String::new(); + loop { + if let Some(inp) = self.window.getch() { + match inp { + Character('\n') => return result, + KeyEnter => return result, // TODO: is this ever used? + Character(c) => { + if result.len() < self.max_len { + result.push(c); + self.window.addch(c); + } + } + KeyBackspace => { + if !result.is_empty() { + result.pop(); + let (y, x) = (self.window.get_cur_y(), self.window.get_cur_x()); + self.window.mv(y, x - 1); + self.window.addch('_'); + self.window.mv(y, x - 1); + } + } + _ => (), + }; + } + self.window.refresh(); + } + } +} + +pub struct MessageLog { + window: Window, +} + +impl MessageLog { + pub fn new( + ypos: i32, + xpos: i32, + height: i32, + width: i32, + has_colours: bool, + red_colour: u8, + ) -> MessageLog { + let ml = MessageLog { + window: newwin(height, width, ypos, xpos), + }; + if has_colours { + ml.window.attrset(ColorPair(red_colour)); + } + ml + } + + pub fn log_error(&self, str: String) { + self.window.clear(); + self.window.addstr(str); + self.window.refresh(); + } +} diff --git a/src/main.rs b/src/main.rs index 043702a..c7281cf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,345 +1,10 @@ -extern crate pancurses; - -use pancurses::Input::*; -use pancurses::*; - mod game; -use crate::game::*; - -pub struct GameLog { - action_window: Window, - pieces_window: Window, -} - -impl GameLog { - pub fn new(ypos: i32, xpos: i32, height: i32, width: i32) -> GameLog { - GameLog { - pieces_window: newwin(2, width, ypos, xpos), - action_window: newwin(height, width, ypos + 3, xpos), - } - } - - pub fn update<L: Fn(String)>(&self, game: &Game<L>) { - self.pieces_window.clear(); - self.pieces_window.addstr(format!( - "W: {:2} B: {:2}\nWC: {} BC: {}", - game.query_pieces(Player::White, Stone::Flat), - game.query_pieces(Player::Black, Stone::Flat), - game.query_pieces(Player::White, Stone::Capstone), - game.query_pieces(Player::Black, Stone::Capstone), - )); - self.pieces_window.refresh(); - - self.action_window.clear(); - self.action_window.addstr(game.query_action_lines()); - self.action_window.refresh(); - } -} - -pub struct BoardGUI { - board_window: Window, - cell_windows: Vec<Window>, - size: u8, - cell_height: u8, - cell_width: u8, - hoff: i32, - cur_cell: Position, - has_colours: bool, - white_colour: u8, - black_colour: u8, -} - -#[derive(PartialEq)] -pub enum BGAction { - Quit, - None, -} - -impl BoardGUI { - fn draw_grid(&self) { - let s: i32 = self.size as i32; - let h: i32 = self.cell_height as i32; - let w: i32 = self.cell_width as i32; - for y in 0..h * s + 1 { - for x in 0..w * s + 1 { - if y % h == 0 { - if x % w == 0 { - self.board_window.mvaddch(y, x + self.hoff, '+'); - } else { - self.board_window.mvaddch(y, x + self.hoff, '-'); - } - } else if x % w == 0 { - self.board_window.mvaddch(y, x + self.hoff, '|'); - } - } - } - for y in 0..s { - self.board_window - .mvaddstr(y * h + h / 2, 0, format!("{}.", s - y)); - } - for x in 0..s { - self.board_window.mvaddstr( - s * h + 1, - w * x + w / 2 + self.hoff, - format!("{:x}.", x + 10), - ); - } - self.board_window.refresh(); - } - - fn pos_to_idx(&self, pos: &Position) -> usize { - let y = pos.y as usize; - let x = pos.x as usize; - x + y * (self.size as usize) - } - - fn mv_to_cell(&self, pos: &Position, line: i32) { - if let Some(win) = self.cell_windows.get(self.pos_to_idx(pos)) { - win.mv(line, 0); - win.refresh(); - } - } - - fn draw_stone(&self, win: &Window, top: bool, player: &Player, stone: &Stone) { - if self.has_colours { - match player { - Player::Black => win.attrset(ColorPair(self.black_colour)), - Player::White => win.attrset(ColorPair(self.white_colour)), - }; - if top { - win.addch(match stone { - Stone::Flat => '#', - Stone::Standing => '/', - Stone::Capstone => '*', - }); - } else { - win.addstr(format!("{}", player)); - } - } else { - win.addstr(format!("{}", player)); - if top { - win.addstr(format!("{}", stone)); - } - } - } - - fn draw_stack(&self, pos: &Position, stack: &Stack) { - if let Some(win) = self.cell_windows.get(self.pos_to_idx(pos)) { - win.mv(0, 0); - let l = stack.len() as i32; - if l > 0 { - for i in 0..l { - let piece = &stack[i as usize]; - self.draw_stone(win, i == l - 1, &piece.player, &piece.stone); - } - } - win.refresh(); - } - } - - pub fn update<L: Fn(String)>(&self, game: &Game<L>) { - // Is this really the `best' way? - assert!(game.get_size() == self.size); - let mut pos = Position { x: 0, y: 0 }; - for y in 0..self.size { - pos.y = y; - for x in 0..self.size { - pos.x = x; - if let Some(stack) = game.query_square(&pos) { - self.draw_stack(&pos, stack); - } - } - } - } - - fn read_action(&mut self) -> BGAction { - loop { - if let Some(key) = self.board_window.getch() { - match key { - KeyDown => { - if 0 < self.cur_cell.y { - self.cur_cell.y -= 1; - self.mv_to_cell(&self.cur_cell, 0); - } - } - KeyUp => { - if self.cur_cell.y + 1 < self.size { - self.cur_cell.y += 1; - self.mv_to_cell(&self.cur_cell, 0); - } - } - KeyLeft => { - if 0 < self.cur_cell.x { - self.cur_cell.x -= 1; - self.mv_to_cell(&self.cur_cell, 0); - } - } - KeyRight => { - if self.cur_cell.x + 1 < self.size { - self.cur_cell.x += 1; - self.mv_to_cell(&self.cur_cell, 0); - } - } - Character('q') => return BGAction::Quit, - - x => { - self.board_window.addstr(format!("{:?}", x)); - self.board_window.refresh(); - } - } - } - } - } +mod gui; +mod parser; - pub fn new( - ypos: i32, - xpos: i32, - size: u8, - cell_height: u8, - cell_width: u8, - hoff: u8, - has_colours: bool, - white_colour: u8, - black_colour: u8, - ) -> BoardGUI { - let mut cell_windows: Vec<Window> = Vec::new(); - - let hoff = hoff as i32; - let s: i32 = size as i32; - let h: i32 = cell_height as i32; - let w: i32 = cell_width as i32; - 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)); - } - - let bg = BoardGUI { - board_window: newwin(h * s + 3, w * s + 3 + hoff, ypos, xpos), - cell_windows: cell_windows, - size: size, - cell_height: cell_height, - cell_width: cell_width, - hoff: hoff, - cur_cell: Position { x: 0, y: 0 }, - has_colours: has_colours, - black_colour: black_colour, - white_colour: white_colour, - }; - - bg.board_window.keypad(true); - bg.board_window.nodelay(false); - - bg.draw_grid(); - - // for i in 0..s * s { - // let y: i32 = i / s; - // let x: i32 = i % s; - // let y = (s - y - 1) * h + 1; - // let x = hoff + x * w + 1; - // bg.cell_windows[i as usize].addstr(format!("{}:({},{})", i, y, x)); - // bg.cell_windows[i as usize].refresh(); - // } - - bg.mv_to_cell(&bg.cur_cell, 0); - bg.board_window.refresh(); - bg - } -} - -pub struct ActionEntry { - window: Window, - max_len: usize, - xstart: i32, -} - -impl ActionEntry { - pub fn new(ypos: i32, xpos: i32, max_len: usize) -> ActionEntry { - let txt = "Action entry: "; - let xstart = txt.len() as i32; - let width = max_len as i32 + xstart + 1; - - let ae = ActionEntry { - window: newwin(1, width, ypos, xpos), - max_len: max_len, - xstart: xstart, - }; - - ae.window.nodelay(false); - ae.window.keypad(true); - - ae.window.addstr(txt); - - for _i in 0..max_len { - ae.window.addch('_'); - } - - ae.window.mv(0, xstart); - ae.window.refresh(); - ae - } - - fn read_action(&self) -> String { - self.window.mv(0, self.xstart); - self.window.refresh(); - - let mut result = String::new(); - loop { - if let Some(inp) = self.window.getch() { - match inp { - Character('\n') => return result, - KeyEnter => return result, // TODO: is this ever used? - Character(c) => { - if result.len() < self.max_len { - result.push(c); - self.window.addch(c); - } - } - KeyBackspace => { - if !result.is_empty() { - result.pop(); - let (y, x) = (self.window.get_cur_y(), self.window.get_cur_x()); - self.window.mv(y, x - 1); - self.window.addch('_'); - self.window.mv(y, x - 1); - } - } - _ => (), - }; - } - self.window.refresh(); - } - } -} - -pub struct MessageLog { - window: Window, -} - -impl MessageLog { - pub fn new( - ypos: i32, - xpos: i32, - height: i32, - width: i32, - has_colours: bool, - red_colour: u8, - ) -> MessageLog { - let ml = MessageLog { - window: newwin(height, width, ypos, xpos), - }; - if has_colours { - ml.window.attrset(ColorPair(red_colour)); - } - ml - } - - pub fn log_error(&self, str: String) { - self.window.clear(); - self.window.addstr(str); - self.window.refresh(); - } -} +use crate::game::*; +use crate::gui::*; +use crate::parser::*; fn main() { let screen = initscr(); @@ -376,7 +41,10 @@ fn main() { board_gui.update(&game); game_log.update(&game); - log(action_entry.read_action()); + match parse_action(game.query_current_player(), &action_entry.read_action()) { + Ok(act) => log(format!("{}", act)), + Err(err) => log(err), + } // game_state.perform_action(&action); // game_state.is_legal_action(&Action::Move(Player::White, pos, Direction::Up, vec![0, 1])); diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..49ded9b --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,134 @@ +// #[macro_use] +// extern crate nom; + +// use nom::*; + +use crate::game::*; + +fn parse_move(player: Player, mv: &str) -> Result<Action, String> { + let mut chars = mv.chars(); + + let c = chars.next(); + if c.is_none() { + return Err(String::from("Empty string cannot be parsed.")); + } + + let dc = c.unwrap(); + if (dc < '1') || ('9' < dc) { + return Err(String::from( + "Moves must begin with a digit in 1-8 indicating the number of drops.", + )); + } + let sum_drops = dc.to_digit(10).unwrap(); + + let c = chars.next(); + if c.is_none() { + return Err(String::from("Moves must specify a stack position.")); + } + + let x = c.unwrap(); + if (x < 'a') || (x > 'h') { + return Err(String::from( + "Moves must specify a position on the game board.", + )); + } + let x: u8 = x as u8 - 'a' as u8; + + let c = chars.next(); + if c.is_none() { + return Err(String::from("Moves must specify a stack position.")); + } + + let y = c.unwrap(); + if (y < '1') || (y > '8') { + return Err(String::from( + "Moves must specify a position on the game board.", + )); + } + let y: u8 = (y.to_digit(10).unwrap() - 1) as u8; + + let c = chars.next(); + if c.is_none() { + return Err(String::from("Moves must specify a move direction.")); + } + + let c = c.unwrap(); + let mut dir = Direction::Up; + match c { + '+' => dir = Direction::Up, + '-' => dir = Direction::Down, + '<' => dir = Direction::Left, + '>' => dir = Direction::Right, + _ => return Err(String::from("The valid directions are +,-,<, and >.")), + } + + let mut drops: Vec<u8> = Vec::new(); + for d in chars { + if let Some(u) = d.to_digit(10) { + if u > 9 { + return Err(String::from( + "No more than 8 pieces may be dropped on a given square.", + )); + } else { + drops.push(u as u8) + } + } else { + return Err(String::from( + "A move must specify a number of pieces dropped for each square.", + )); + } + } + + let sum = drops.iter().map(|&d| d as u32).sum::<u32>(); + if sum != sum_drops { + return Err(format!( + "The move called for {} stones, but {} were dropped.", + sum_drops, sum + )); + } + + Ok(Action::Move(player, Position { x: x, y: y }, dir, drops)) +} + +fn parse_place(player: Player, pl: &str) -> Result<Action, String> { + Ok(Action::Place(player, Position { x: 0, y: 0 }, Stone::Flat)) +} + +pub fn parse_action(player: Player, act: &str) -> Result<Action, String> { + if act + .chars() + .any(|c| c == '+' || c == '-' || c == '>' || c == '<') + { + parse_move(player, act) + } else { + parse_place(player, act) + } +} + +// fn parse_move(mv: &str) -> Result<Action, Err<&str>> { +// one_of!("abcdefgh"); +// Ok(Action::Place( +// Player::Black, +// Position { x: 0, y: 0 }, +// Stone::Flat, +// )) +// } + +// fn parse_place(pl: &str) -> Result<Action, Err<&str>> { +// Ok(Action::Place( +// Player::Black, +// Position { x: 0, y: 0 }, +// Stone::Flat, +// )) +// } + +// pub fn parse_action(act: &str) -> Result<Action, Err<&str>> { +// if act +// .chars() +// .any(|c| c == '+' || c == '-' || c == '>' || c == '<') +// { +// parse_move(act) +// } else { +// parse_place(act) +// } +// } |
