diff options
| author | tslil clingman <> | 2020-01-14 20:44:29 -0500 |
|---|---|---|
| committer | tslil clingman <> | 2020-01-14 20:44:29 -0500 |
| commit | 7dcf57f6f2e987d44391ae68b8c48824ba7523a6 (patch) | |
| tree | a5966c819ba848c31e7b7ffde60780d9644b29a5 /src/board.rs | |
| parent | 0c3c243d60d49e1c18e5a18fc2300e7e47292938 (diff) | |
Second draft of checking code, beginning on perform actions
Diffstat (limited to 'src/board.rs')
| -rw-r--r-- | src/board.rs | 297 |
1 files changed, 174 insertions, 123 deletions
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<L> +where + L: Fn(String), +{ size: u8, black_flats: u8, white_flats: u8, black_capstones: u8, white_capstones: u8, board: Vec<Stack>, + 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<L: Fn(String)> Board<L> { + pub fn new(size: u8, log: L) -> Option<Board<L>> { + 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<Stack> = 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<u8>, - 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::<u32>() > 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::<u32>() > 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<u8>) {} + + 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 } } } |
