From 7dcf57f6f2e987d44391ae68b8c48824ba7523a6 Mon Sep 17 00:00:00 2001 From: tslil clingman <> Date: Tue, 14 Jan 2020 20:44:29 -0500 Subject: Second draft of checking code, beginning on perform actions --- src/board.rs | 297 ++++++++++++++++++++++++++++++++++------------------------- src/main.rs | 31 ++++++- 2 files changed, 204 insertions(+), 124 deletions(-) (limited to 'src') diff --git a/src/board.rs b/src/board.rs index 484c31a..4e3b0d5 100644 --- a/src/board.rs +++ b/src/board.rs @@ -1,13 +1,13 @@ use std::fmt; -// #[derive(Clone, Copy)] +#[derive(Clone, Copy)] pub struct Position { pub x: u8, pub y: u8, } impl fmt::Display for Position { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:x}{}", 10 + self.x, self.y) + write!(f, "{:x}{}", 10 + self.x, self.y + 1) } } @@ -96,13 +96,17 @@ impl fmt::Display for Action { type Stack = Vec<(Player, Stone)>; -pub struct Board { +pub struct Board +where + L: Fn(String), +{ size: u8, black_flats: u8, white_flats: u8, black_capstones: u8, white_capstones: u8, board: Vec, + log: L, } // How to solve code duplication between is_legal_action and @@ -110,9 +114,40 @@ pub struct Board { // TODO: Generate all legal actions for a given player -pub type Logger = fn(String); +// pub type Logger = fn(String); + +impl Board { + pub fn new(size: u8, log: L) -> Option> { + if size >= 3 && size <= 8 { + let (flats, caps) = match size { + 3 => (10, 0), + 4 => (15, 0), + 5 => (21, 1), + 6 => (30, 1), + 7 => (40, 2), + _ => return None, + }; + Some(Board { + size: size, + black_flats: flats, + white_flats: flats, + black_capstones: caps, + white_capstones: caps, + board: { + let mut v: Vec = Vec::new(); + for _i in 0..size * size { + let t: Stack = Vec::new(); + v.push(t); + } + v + }, + log: log, + }) + } else { + None + } + } -impl Board { fn remaining_pieces(&self, player: &Player, stone: &Stone) -> u8 { match player { Player::Black => { @@ -140,40 +175,40 @@ impl Board { } } - fn lookup_square(&self, pos: &Position) -> Option<&Stack> { + fn pos_to_idx(&self, pos: &Position) -> usize { let y: usize = pos.y as usize; let x: usize = pos.x as usize; - self.board.get(x + y * (self.size as usize)) + x + y * (self.size as usize) } - fn is_legal_place(&self, player: &Player, pos: &Position, stone: &Stone, log: Logger) -> bool { + fn is_legal_place(&self, player: &Player, pos: &Position, stone: &Stone) -> bool { /* In order to legally place a piece: 1. The desired square must be empty 2. The player must have sufficient pieces */ if self.within_bounds(pos) { - match self.lookup_square(pos) { - Some(stack) => { - if stack.len() > 0 { - log(format!("{} is already occupied.", pos)); - false - } else if self.remaining_pieces(player, stone) == 0 { - log(format!( - "{} has no more remaining {} pieces.", - player, stone - )); - false - } else { - true - } - } - None => { - log(format!("Internal error, lookup for {} failed.", pos)); + if let Some(stack) = &self.board.get(self.pos_to_idx(pos)) { + if stack.len() > 0 { + (self.log)(format!("{} is already occupied.", pos)); false + } else if self.remaining_pieces(&player, &stone) == 0 { + (self.log)(format!( + "{} has no more remaining {} pieces.", + player, stone + )); + false + } else { + true } + } else { + (self.log)(format!( + "Internal error: lookup for {} failed in is_legal_place.", + pos + )); + false } } else { - log(format!("Position {} is not within bounds.", pos)); + (self.log)(format!("Position {} is not within bounds.", pos)); false } } @@ -184,119 +219,135 @@ impl Board { pos: &Position, direction: &Direction, drops: &Vec, - log: Logger, ) -> bool { if self.within_bounds(pos) { - match self.lookup_square(pos) { - /* Rules for moving a stack: - 0. There are stones - 1. Top stone belongs to player - 2. Zero or One stones dropped on starting square - 3. Total number of stones moved does not exceed the carry capacity - 4. Direction does not contain a capstone - 5. Wall may only appear on last spot if it's capstone alone that covers - 6. All stones are used up before then end of the board is met - */ - Some(stack) => { - if stack.len() == 0 { - log(format!("{} has no stones to move.", pos)); - false - } else if drops.len() == 0 { - log(format!("A drop sequence for must be specified for a move.")); - false - } else if stack[0].0 != *player { - log(format!( - "{} may not move the stack at {} as it belongs to {}.", - player, pos, stack[0].0 - )); - false - } else if drops[0] > 1 { - log(format!( - "A move may only drop 0 or 1 stones at it's origin square." - )); - false - // Do the sum in u32 just in case ? - } else if drops.iter().map(|&d| d as u32).sum::() > self.size as u32 { - log(format!( - "A move may not exceed the carry limit of {} stones.", - self.size - )); - false + /* Rules for moving a stack: + 0. There are stones + 1. Top stone belongs to player + 2. Zero or One stones dropped on starting square + 3. Total number of stones moved does not exceed the carry capacity + 4. Direction does not contain a capstone + 5. Wall may only appear on last spot if it's capstone alone that covers + 6. All stones are used up before then end of the board is met + */ + if let Some(stack) = &self.board.get(self.pos_to_idx(pos)) { + if stack.len() == 0 { + (self.log)(format!("{} has no stones to move.", pos)); + return false; + } else if drops.len() == 0 { + (self.log)(format!("A drop sequence for must be specified for a move.")); + return false; + } else if stack[0].0 != *player { + (self.log)(format!( + "{} may not move the stack at {} as it belongs to {}.", + player, pos, stack[0].0 + )); + return false; + } else if drops[0] > 1 { + (self.log)(format!( + "A move may only drop 0 or 1 stones at it's origin square." + )); + return false; + // Do the sum in u32 just in case ? + } else if drops.iter().map(|&d| d as u32).sum::() > self.size as u32 { + (self.log)(format!( + "A move may not exceed the carry limit of {} stones.", + self.size + )); + return false; + } + let mut steps: usize = drops.len() - 1; + let cap: bool = stack[0].1 == Stone::Capstone; + let mut pos_new = Position { x: pos.x, y: pos.y }; + + while steps > 0 { + steps -= 1; + if { + match direction { + Direction::Up => pos_new.y + 1 >= self.size, + Direction::Down => pos_new.y == 0, + Direction::Left => pos_new.x + 1 >= self.size, + Direction::Right => pos_new.x == 0, + } + } { + (self.log)(format!("A move may not extend past the board.")); + return false; } else { - let mut steps: usize = drops.len() - 1; - let cap: bool = stack[0].1 == Stone::Capstone; - let mut pos_new = Position { x: pos.x, y: pos.y }; - let mut result = true; - while steps > 0 { - if { - match direction { - Direction::Up => pos_new.y + 1 >= self.size, - Direction::Down => pos_new.y == 0, - Direction::Left => pos_new.x + 1 >= self.size, - Direction::Right => pos_new.x == 0, - } - } { - log(format!("A move may not extend past the board.")); - result = false; - break; - } else { - match direction { - Direction::Up => pos_new.y += 1, - Direction::Down => pos_new.y -= 1, - Direction::Left => pos_new.x += 1, - Direction::Right => pos_new.x -= 1, - } - match self.lookup_square(&pos_new) { - Some(stack) => { - if stack.len() > 0 { - match stack[0].1 { - Stone::Capstone => { - log(format!( - "A move may not cover a capstone." - )); - result = false; - break; - } - Stone::Standing => { - if (steps > 1) || (!cap) { - log(format!( - "A move may not cover a standing stone." - )); - result = false; - break; - } - } - Stone::Flat => (), - } - } + match direction { + Direction::Up => pos_new.y += 1, + Direction::Down => pos_new.y -= 1, + Direction::Left => pos_new.x += 1, + Direction::Right => pos_new.x -= 1, + } + if let Some(stack) = &self.board.get(self.pos_to_idx(&pos_new)) { + if stack.len() > 0 { + match stack[0].1 { + Stone::Capstone => { + (self.log)(format!("A move may not cover a capstone.")); + return false; } - None => { - log(format!("Internal error, lookup for {} failed.", pos)); - result = false; - break; + Stone::Standing => { + if (steps > 1) || (!cap) { + (self.log)(format!( + "A move may not cover a standing stone." + )); + return false; + } } + Stone::Flat => (), } } - steps -= 1; + } else { + (self.log)(format!( + "Internal error: lookup for {} failed in is_legal_move (1).", + pos_new + )); + return false; } - result } } - None => { - log(format!("Internal error, lookup for {} failed.", pos)); - false - } + return true; + } else { + (self.log)(format!( + "Internal error: lookup for {} failed in is_legal_move (2).", + pos + )); + return false; } } else { - log(format!("Position {} is not within bounds.", pos)); + (self.log)(format!("Position {} is not within bounds.", pos)); false } } - fn is_legal_action(&self, mov: &Action, log: Logger) -> bool { - match mov { - Action::Place(player, pos, stone) => self.is_legal_place(player, pos, stone, log), - Action::Move(player, pos, direction, drops) => false, + pub fn is_legal_action(&self, act: &Action) -> bool { + match act { + Action::Place(player, pos, stone) => self.is_legal_place(player, pos, stone), + Action::Move(player, pos, direction, drops) => { + self.is_legal_move(player, pos, direction, drops) + } + } + } + + // Unconditional and so could lead to invalid state, but private + fn place_stone(&mut self, player: Player, pos: Position, stone: Stone) { + let idx = self.pos_to_idx(&pos); + self.board[idx].push((player, stone)); + } + + fn move_stack(&mut self, player: Player, pos: Position, direction: Direction, drops: Vec) {} + + pub fn perform_action(&mut self, act: Action) -> bool { + if self.is_legal_action(&act) { + match act { + Action::Place(player, pos, stone) => self.place_stone(player, pos, stone), + Action::Move(player, pos, direction, drops) => { + self.move_stack(player, pos, direction, drops) + } + } + true + } else { + false } } } diff --git a/src/main.rs b/src/main.rs index 5794458..4528e96 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,7 @@ impl BoardGui { let y: i32 = pos.y as i32; let x: i32 = pos.x as i32; if (y < self.size) && (x < self.size) { - let y = y * self.cell_height + 1; + let y = (self.size - y) * self.cell_height + 1; let x = self.hoff + x * self.cell_width + 1; self.win.mv(y, x); self.win.refresh(); @@ -123,6 +123,24 @@ impl MoveEntry { // self.win.mvchgat(0,12,10,A_NORMAL,COLOR_PAIR(1)); self.win.refresh(); } + + fn read_action(&self) -> &str { + "a1" + } +} + +struct MessageLog { + win: Window, +} + +impl MessageLog { + fn init(&self) {} + + fn log_error(&self, str: String) { + // self.win.clear(); + self.win.addstr(str); + self.win.refresh(); + } } fn main() { @@ -143,6 +161,7 @@ fn main() { let bgw = newwin(25, 40, 0, 0); let mew = newwin(1, 30, 25, 0); + let mlw = newwin(3, 70, 26, 0); let mut bg = BoardGui { win: bgw, @@ -155,8 +174,18 @@ fn main() { let me = MoveEntry { win: mew }; + let ml = MessageLog { win: mlw }; + bg.init(); me.init(); + ml.init(); + + let board_option = Board::new(5, |s| MessageLog::log_error(&ml, s)); + if let Some(mut board) = board_option { + let pos = Position { x: 0, y: 0 }; + board.perform_action(Action::Place(Player::White, pos, Stone::Flat)); + board.is_legal_action(&Action::Move(Player::White, pos, Direction::Up, vec![0, 1])); + } loop { if let Some(inp) = screen.getch() { -- cgit v1.3.1