use std::fmt; // #[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) } } #[derive(PartialEq)] pub enum Player { Black, White, } impl fmt::Display for Player { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{}", match self { Player::Black => "B", Player::White => "W", } ) } } #[derive(PartialEq)] pub enum Stone { Flat, Standing, Capstone, } impl fmt::Display for Stone { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{}", match self { Stone::Flat => "", Stone::Standing => "S", Stone::Capstone => "C", } ) } } // #[derive(Clone, Copy)] pub enum Direction { Up, Down, Left, Right, } impl fmt::Display for Direction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{}", match self { Direction::Up => "+", Direction::Down => "-", Direction::Left => "<", Direction::Right => ">", } ) } } pub enum Action { Place(Player, Position, Stone), Move(Player, Position, Direction, Vec), } 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::Move(_, pos, direction, drops) => write!( f, "{}{}{}", pos, direction, drops.into_iter().map(|q| q.to_string()).collect::() ), } } } type Stack = Vec<(Player, Stone)>; pub struct Board { size: u8, black_flats: u8, white_flats: u8, black_capstones: u8, white_capstones: u8, board: Vec, } // How to solve code duplication between is_legal_action and // perform_action? // TODO: Generate all legal actions for a given player pub type Logger = fn(String); impl Board { fn remaining_pieces(&self, player: &Player, stone: &Stone) -> u8 { match player { Player::Black => { if *stone == Stone::Capstone { self.black_capstones } else { self.black_flats } } Player::White => { if *stone == Stone::Capstone { self.white_capstones } else { self.white_flats } } } } fn within_bounds(&self, pos: &Position) -> bool { if (pos.x < self.size) && (pos.y < self.size) { true } else { false } } fn lookup_square(&self, pos: &Position) -> Option<&Stack> { let y: usize = pos.y as usize; let x: usize = pos.x as usize; self.board.get(x + y * (self.size as usize)) } fn is_legal_place(&self, player: &Player, pos: &Position, stone: &Stone, log: Logger) -> 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)); false } } } else { log(format!("Position {} is not within bounds.", pos)); false } } fn is_legal_move( &self, player: &Player, 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 } 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 => (), } } } None => { log(format!("Internal error, lookup for {} failed.", pos)); result = false; break; } } } steps -= 1; } result } } None => { log(format!("Internal error, lookup for {} failed.", pos)); false } } } else { 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, } } }