Skip to content

Commit

Permalink
Refactoring: only return positions from double_moves
Browse files Browse the repository at this point in the history
  • Loading branch information
carsten-wenderdel committed Aug 26, 2023
1 parent 6a28526 commit 7d60297
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 57 deletions.
16 changes: 7 additions & 9 deletions src/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,16 +149,14 @@ impl Position {
/// The return values have switched the sides of the players.
pub fn all_positions_after_moving(&self, dice: &Dice) -> Vec<Position> {
debug_assert!(self.o_off < NO_OF_CHECKERS && self.x_off < NO_OF_CHECKERS);
return match dice {
Dice::Double(die) => {
let moves = self.all_double_moves(*die);
moves.iter().map(|m| m.1.switch_sides()).collect()
}
Dice::Regular(dice) => {
let moves = self.all_positions_after_regular_move(dice);
moves.iter().map(|m| m.switch_sides()).collect()
}
let mut new_positions = match dice {
Dice::Double(die) => self.all_positions_after_double_move(*die),
Dice::Regular(dice) => self.all_positions_after_regular_move(dice),
};
for position in new_positions.iter_mut() {
*position = position.switch_sides();
}
new_positions
}

pub fn switch_sides(&self) -> Position {
Expand Down
81 changes: 33 additions & 48 deletions src/position/double_moves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,18 @@ use std::cmp::min;

impl Position {
/// Returns a vector of all possible moves when rolling a double.
/// The return value both contains the moves and the resulting positions.
/// The move is encoded in an array of 4 numbers, representing the pip from where to move.
/// If a checker cannot be moved, the corresponding number in the array is `O_BAR`.
pub(super) fn all_double_moves(&self, die: usize) -> Vec<([usize; 4], Position)> {
pub(super) fn all_positions_after_double_move(&self, die: usize) -> Vec<Position> {
if self.pips[X_BAR] > 0 && self.pips[X_BAR - die] < -1 {
// Has at least one checker on the bar but can't move it
return Vec::from([([O_BAR, O_BAR, O_BAR, O_BAR], self.clone())]);
return vec![self.clone()];
}

let (position, number_of_entered_checkers) = self.position_after_entering_checkers(die);
if number_of_entered_checkers == 4 {
return Vec::from([([X_BAR, X_BAR, X_BAR, X_BAR], position.clone())]);
return vec![position.clone()];
}

let mut moves = position.double_moves_after_entering(die, number_of_entered_checkers);
if number_of_entered_checkers != 0 {
for x in moves.iter_mut() {
x.0.rotate_right(number_of_entered_checkers as usize);
for i in 0..number_of_entered_checkers as usize {
x.0[i] = X_BAR;
}
}
}
let moves = position.double_moves_after_entering(die, number_of_entered_checkers);
debug_assert!(!moves.is_empty());
moves
}
Expand Down Expand Up @@ -56,37 +45,37 @@ impl Position {
&self,
die: usize,
number_of_entered_checkers: u32,
) -> Vec<([usize; 4], Position)> {
) -> Vec<Position> {
let nr_movable_checkers = self.number_of_movable_checkers(die, number_of_entered_checkers);
if nr_movable_checkers == 0 {
return Vec::from([([O_BAR, O_BAR, O_BAR, O_BAR], self.clone())]);
return vec![self.clone()];
}
let mut moves: Vec<([usize; 4], Position)> = Vec::new();
let mut moves: Vec<Position> = Vec::new();
for i1 in (1..X_BAR).rev() {
if self.can_move_in_board(i1, die) {
let pos = self.clone_and_move_single_checker(i1, die);
if nr_movable_checkers == 1 {
moves.push(([i1, O_BAR, O_BAR, O_BAR], pos));
moves.push(pos);
continue;
}
for i2 in (1..i1 + 1).rev() {
if pos.can_move_in_board(i2, die) {
let pos = pos.clone_and_move_single_checker(i2, die);
if nr_movable_checkers == 2 {
moves.push(([i1, i2, O_BAR, O_BAR], pos));
moves.push(pos);
continue;
}
for i3 in (1..i2 + 1).rev() {
if pos.can_move_in_board(i3, die) {
let pos = pos.clone_and_move_single_checker(i3, die);
if nr_movable_checkers == 3 {
moves.push(([i1, i2, i3, O_BAR], pos));
moves.push(pos);
continue;
}
for i4 in (1..i3 + 1).rev() {
if pos.can_move_in_board(i4, die) {
let pos = pos.clone_and_move_single_checker(i4, die);
moves.push(([i1, i2, i3, i4], pos));
moves.push(pos);
}
}
}
Expand Down Expand Up @@ -128,64 +117,60 @@ mod tests {
// Given
let position = pos!(x X_BAR:4; o 22:2);
// When
let moves = position.all_double_moves(3);
let resulting_positions = position.all_positions_after_double_move(3);
// Then
assert_eq!(moves.len(), 1);
assert_eq!(moves[0].0, [O_BAR, O_BAR, O_BAR, O_BAR]);
assert_eq!(moves[0].1, position);
assert_eq!(resulting_positions, vec![position]);
}

#[test]
fn enter_all_four_from_the_bar() {
// Given
let actual = pos!(x X_BAR:4; o 22:2, 20:2);
// When
let moves = actual.all_double_moves(4);
let resulting_positions = actual.all_positions_after_double_move(4);
// Then
let expected = pos!(x 21:4; o 22:2, 20:2);
assert_eq!(moves, Vec::from([([X_BAR, X_BAR, X_BAR, X_BAR], expected)]));
assert_eq!(resulting_positions, vec![expected]);
}

#[test]
fn enter_one_and_move_one_more_and_no_bearoff() {
// Given
let actual = pos!(x X_BAR:1, 15:1, 10:1, 4:1; o 22:2, 20:2, 17:3, 11:2, 6:1, 2:2);
// When
let moves = actual.all_double_moves(4);
let resulting_positions = actual.all_positions_after_double_move(4);
// Then
let expected = pos!(x 21:1, 15:1, 6:1, 4:1; o 22:2, 20:2, 17:3, 11:2, 2:2, O_BAR:1);
assert_eq!(moves, Vec::from([([X_BAR, 10, O_BAR, O_BAR], expected)]));
assert_eq!(resulting_positions, vec![expected]);
}

#[test]
fn enter_two_and_move_two_out_of_many() {
// Given
let position = pos!(x X_BAR:2, 4:1, 3:1; o 24:2);
// When
let moves = position.all_double_moves(3);
let resulting_positions = position.all_positions_after_double_move(3);
// Then
let expected1 = ([X_BAR, X_BAR, 22, 22], pos!(x 19:2, 4:1, 3:1; o 24:2));
let expected2 = ([X_BAR, X_BAR, 22, 19], pos!(x 22:1, 16:1, 4:1, 3:1; o 24:2));
let expected3 = ([X_BAR, X_BAR, 22, 4], pos!(x 22:1, 19:1, 3:1, 1:1; o 24:2));
assert_eq!(moves.len(), 3);
assert_eq!(moves, Vec::from([expected1, expected2, expected3]));
let expected1 = pos!(x 19:2, 4:1, 3:1; o 24:2);
let expected2 = pos!(x 22:1, 16:1, 4:1, 3:1; o 24:2);
let expected3 = pos!(x 22:1, 19:1, 3:1, 1:1; o 24:2);
assert_eq!(resulting_positions, vec![expected1, expected2, expected3]);
}

#[test]
fn bearoff_4_or_bearoff_less() {
// Given
let position = pos!(x 4:1, 3:1, 2:4; o 22:2);
// When
let moves = position.all_double_moves(2);
let resulting_positions = position.all_positions_after_double_move(2);
// Then
let expected1 = ([4, 3, 2, 2], pos!(x 2:3, 1:1; o 22:2));
let expected2 = ([4, 2, 2, 2], pos!(x 3:1, 2:2; o 22:2));
let expected3 = ([3, 2, 2, 2], pos!(x 4:1, 2:1, 1:1; o 22:2));
let expected4 = ([2, 2, 2, 2], pos!(x 4:1, 3:1; o 22:2));
assert_eq!(moves.len(), 4);
let expected1 = pos!(x 2:3, 1:1; o 22:2);
let expected2 = pos!(x 3:1, 2:2; o 22:2);
let expected3 = pos!(x 4:1, 2:1, 1:1; o 22:2);
let expected4 = pos!(x 4:1, 3:1; o 22:2);
assert_eq!(
moves,
Vec::from([expected1, expected2, expected3, expected4])
resulting_positions,
vec![expected1, expected2, expected3, expected4],
);
}

Expand All @@ -194,21 +179,21 @@ mod tests {
// Given
let actual = pos!(x 10:4; o 22:1, 4:2);
// When
let moves = actual.all_double_moves(3);
let resulting_positions = actual.all_positions_after_double_move(3);
// Then
let expected = pos!(x 7:4; o 22:1, 4:2);
assert_eq!(moves, Vec::from([([10, 10, 10, 10], expected)]));
assert_eq!(resulting_positions, vec![expected]);
}

#[test]
fn hits_opponent_when_entering_and_cannot_move_afterwards() {
// Given
let actual = pos!(x X_BAR:2; o 22:1, 19:2);
// When
let moves = actual.all_double_moves(3);
let resulting_positions = actual.all_positions_after_double_move(3);
// Then
let expected = pos!(x 22:2; o 19:2, O_BAR:1);
assert_eq!(moves, Vec::from([([X_BAR, X_BAR, O_BAR, O_BAR], expected)]));
assert_eq!(resulting_positions, vec![expected]);
}
}

Expand Down

0 comments on commit 7d60297

Please sign in to comment.