diff options
| author | tslil clingman <> | 2020-01-20 23:09:52 -0800 |
|---|---|---|
| committer | tslil clingman <> | 2020-01-20 23:09:52 -0800 |
| commit | ec4ae2a749315720cc12f3d71f0860e2a32f96d2 (patch) | |
| tree | 36c884dbfc6f1e04e0a02d6a7be78a98d2930fd6 /src/game.rs | |
| parent | 42e504281ba8c36c0bf5c679dd767f04310655f3 (diff) | |
Move rules were incorrectly/incompletely implemented
Diffstat (limited to 'src/game.rs')
| -rw-r--r-- | src/game.rs | 165 |
1 files changed, 117 insertions, 48 deletions
diff --git a/src/game.rs b/src/game.rs index 1d61732..2922121 100644 --- a/src/game.rs +++ b/src/game.rs @@ -228,7 +228,7 @@ impl GameState { player: Player, pos: &Position, stone: Stone, - ) -> Result<GameState, String> { + ) -> Result<(Vec<Position>, GameState), String> { if let Err(e) = self.is_legal_place(player, pos, stone) { return Err(e); } @@ -256,7 +256,7 @@ impl GameState { } } } - Ok(copy) + Ok((vec![pos.clone()], copy)) } fn is_legal_move( @@ -268,37 +268,70 @@ impl GameState { ) -> Result<(), String> { if self.within_bounds(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 + - There are stones + - Drops have been specified + - Must actually move at least one stone + - Top stone belongs to player + - Zero or more dropped on starting square + - One or more on each subsequent square + - Total number of stones moved does not exceed the carry capacity + - Moved the entire stack (up to the carry capacity) + - Direction does not contain a capstone + - Wall may only appear on last spot if it's capstone alone that covers + - 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 { + let stack_len = stack.len(); + let drops_len = drops.len(); + + if stack_len == 0 { return Err(format!("{} has no stones to move.", pos)); - } else if drops.len() == 0 { + }; + + if drops_len == 0 { return Err(String::from( "A drop sequence for must be specified for a move.", )); - } else if stack[0].player != player { - return Err(format!( - "{} may not move the stack at {} as it belongs to {}.", - player, pos, stack[0].player + }; + + if drops_len == 1 && drops[0] == 1 { + return Err(String::from( + "A valid move must change the position of at least a single stone.", )); - } else if drops[0] > 1 { + } + + if stack[stack_len - 1].player != player { return Err(format!( - "A move may only drop 0 or 1 stones at it's origin square." + "{} may not move the stack at {} as it belongs to {}.", + player, + pos, + stack[stack_len - 1].player )); - // Do the sum in u32 just in case ? - } else if drops.iter().map(|&d| d as u32).sum::<u32>() > self.size as u32 { + }; + + for i in 0..drops_len { + if i > 0 && drops[i] == 0 { + return Err(String::from( + "A move may not drop 0 stones past the first square.", + )); + } + } + + let num_dropped = drops.iter().map(|&d| d as usize).sum::<usize>(); + let carry_capacity = self.size as usize; + if num_dropped > carry_capacity { return Err(format!( - "A move may not exceed the carry limit of {} stones.", + "A move may not exceed the carry capacity of {} stones.", self.size )); } + + if num_dropped != std::cmp::max(carry_capacity, stack_len) { + return Err(String::from( + "A move must effect the whole stack, up to the carry limit.", + )); + } + let mut steps: usize = drops.len() - 1; let cap: bool = stack[0].stone == Stone::Capstone; let mut pos_new = Position { x: pos.x, y: pos.y }; @@ -309,8 +342,8 @@ impl GameState { 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, + Direction::Left => pos_new.x == 0, + Direction::Right => pos_new.x + 1 >= self.size, } } { return Err(String::from("A move may not extend past the board.")); @@ -318,8 +351,8 @@ impl GameState { 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, + 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 { @@ -365,30 +398,53 @@ impl GameState { pos: &Position, direction: Direction, drops: &Vec<u8>, - ) -> Result<GameState, String> { + ) -> Result<(Vec<Position>, GameState), String> { if let Err(e) = self.is_legal_move(player, pos, direction, drops) { return Err(e); } let mut copy = self.copy(); - let mut pos_new = Position { x: pos.x, y: pos.y }; + let mut pos_vec: Vec<Position> = vec![pos.clone()]; + + let idx = copy.pos_to_idx(&pos); let stack: &Stack = &self.board[self.pos_to_idx(pos)]; - let mut k = 0; - for &d in drops { - let idx = copy.pos_to_idx(&pos_new); - for i in 0..d { - copy.board[idx].push(stack[(k + i) as usize]); + copy.board[idx].clear(); + + let num_drops = drops.len(); + let mut offset = 0; + for drop_idx in 0..num_drops { + let pos = pos_vec[pos_vec.len() - 1]; + let idx = copy.pos_to_idx(&pos); + let num = drops[drop_idx]; + + for i in 0..num { + copy.board[idx].push(stack[(offset + i) as usize]); + } + offset += num; + + if drop_idx + 1 < num_drops { + pos_vec.push(match direction { + Direction::Up => Position { + x: pos.x, + y: pos.y + 1, + }, + Direction::Down => Position { + x: pos.x, + y: pos.y - 1, + }, + Direction::Left => Position { + x: pos.x - 1, + y: pos.y, + }, + Direction::Right => Position { + x: pos.x + 1, + y: pos.y, + }, + }); } - k += d; - 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, - }; } - Ok(copy) + Ok((pos_vec, copy)) } } @@ -449,6 +505,14 @@ impl Game { self.current_player } + pub fn query_stone_owner(&self) -> Player { + match self.turn_order { + TurnOrder::BlackPlacesWhite => Player::White, + TurnOrder::WhitePlacesBlack => Player::Black, + TurnOrder::Normal => self.current_player, + } + } + // pub fn query_action(&self, turn: u16) -> Option<&Action> { // self.actions.get(turn as usize) // } @@ -459,15 +523,15 @@ impl Game { pub fn query_action_lines(&self) -> String { let mut result = String::new(); - let mut newline = false; + let mut newline = true; result += "0. "; for i in 0..self.actions.len() { - if newline { + if i > 1 && newline { // TODO: Is placing the opponent's first stone the zeroeth action? result += &format!("{}. ", i); } result += &format!("{} ", self.actions[i]); - if !newline { + if i > 0 && !newline { result.push('\n'); } newline = !newline; @@ -475,7 +539,7 @@ impl Game { result } - pub fn perform_action(&mut self, act: Action) -> Result<(), String> { + pub fn perform_action(&mut self, act: Action) -> Result<Vec<Position>, String> { let maybe_state = self.states.last(); if maybe_state.is_none() { return Err(String::from("Internal error: cannot find last game state")); @@ -528,19 +592,24 @@ impl Game { match new_state_either { Err(e) => return Err(e), - Ok(new_state) => { + Ok((pos_vec, new_state)) => { self.states.push(new_state); self.actions.push(act); - self.current_player = match self.current_player { - Player::Black => Player::White, - Player::White => Player::Black, + self.current_player = match self.turn_order { + TurnOrder::BlackPlacesWhite => Player::White, + TurnOrder::WhitePlacesBlack => Player::White, + TurnOrder::Normal => match self.current_player { + Player::Black => Player::White, + Player::White => Player::Black, + }, }; + self.turn_order = match self.turn_order { TurnOrder::BlackPlacesWhite => TurnOrder::WhitePlacesBlack, TurnOrder::WhitePlacesBlack => TurnOrder::Normal, TurnOrder::Normal => TurnOrder::Normal, }; - return Ok(()); + return Ok(pos_vec); } } } |
