Skip to content

Commit

Permalink
Refactor move_possibilities away
Browse files Browse the repository at this point in the history
Less code and also less runtime.
  • Loading branch information
carsten-wenderdel committed Nov 3, 2024
1 parent ddddd43 commit 643633f
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 82 deletions.
28 changes: 10 additions & 18 deletions crates/engine/src/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ impl Position {
for position in new_positions.iter_mut() {
*position = position.sides_switched();
}
debug_assert!(!new_positions.is_empty());
new_positions
}

Expand Down Expand Up @@ -295,15 +296,6 @@ impl Position {
new
}

/// Only call this if no checkers are on `X_BAR`
fn can_move_in_board(&self, from: usize, die: usize) -> bool {
debug_assert!(
self.pips[X_BAR] == 0,
"Don't call this function if x has checkers on the bar"
);
self.can_move_internally(from, die)
}

#[inline]
fn can_move_internally(&self, from: usize, die: usize) -> bool {
if self.pips[from] < 1 {
Expand Down Expand Up @@ -787,54 +779,54 @@ mod private_tests {
#[test]
fn cannot_move_no_checker() {
let given = pos!(x 4:10; o);
assert!(!given.can_move_in_board(5, 2));
assert!(!given.can_move(5, 2));
}

#[test]
fn cannot_move_opposing_checker() {
let given = Position::from_hash_maps(&HashMap::new(), &HashMap::from([(4, 10)]));
assert!(!given.can_move_in_board(4, 2));
assert!(!given.can_move(4, 2));
}

#[test]
fn cannot_move_would_land_on_two_opposing_checkers() {
let given = pos!(x 4:10; o 2:2);
assert!(!given.can_move_in_board(4, 2));
assert!(!given.can_move(4, 2));
}

#[test]
fn can_move_will_land_on_one_opposing_checker() {
let given = pos!(x 4:10; o 2:1);
assert!(given.can_move_in_board(4, 2));
assert!(given.can_move(4, 2));
}

#[test]
fn can_move_will_land_on_checkers() {
let given = pos!(x 4:10; o 2:1);
assert!(given.can_move_in_board(4, 2));
assert!(given.can_move(4, 2));
}

#[test]
fn cannot_move_bear_off_illegal_because_other_checkers() {
let given = pos!(x 10:2, 4:10; o);
assert!(!given.can_move_in_board(4, 4));
assert!(!given.can_move(4, 4));
}

#[test]
fn can_move_will_bear_off_exactly() {
let given = pos!(x 4:10; o);
assert!(given.can_move_in_board(4, 4));
assert!(given.can_move(4, 4));
}

#[test]
fn cannot_move_bear_off_skipping_illegal_because_other_checkers() {
let given = pos!(x 10:2, 4:10; o);
assert!(!given.can_move_in_board(4, 6));
assert!(!given.can_move(4, 6));
}

#[test]
fn can_move_will_bear_off_skipping() {
let given = pos!(x 4:10; o);
assert!(given.can_move_in_board(4, 6));
assert!(given.can_move(4, 6));
}
}
79 changes: 15 additions & 64 deletions crates/engine/src/position/mixed_moves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,27 +70,34 @@ impl Position {
fn moves_with_0_checkers_on_bar(&self, dice: &MixedDice) -> Vec<Position> {
debug_assert!(self.pips[X_BAR] == 0);

match self.move_possibilities(dice) {
MovePossibilities::None => vec![*self],
MovePossibilities::One { die } => self.one_checker_moves(die),
MovePossibilities::Two => self.two_checker_moves(dice),
// Let's try to find moves where both dice are used.
let mut moves = self.two_checker_moves(dice);
if moves.is_empty() {
// No moves found with both dice used, so let's try the bigger die only.
self.one_checker_moves(dice.big, &mut moves);
if moves.is_empty() {
// No moves found with the bigger die used, so let's try the smaller one.
self.one_checker_moves(dice.small, &mut moves);
if moves.is_empty() {
// The player can't move any checker, so we return the identical position.
moves.push(*self);
}
}
}
moves
}

/// All positions after moving a single checker once. If no move is possible it returns `None`.
/// So if the return value is not `None`, the Vector is not empty.
fn one_checker_moves(&self, die: usize) -> Vec<Position> {
fn one_checker_moves(&self, die: usize, moves: &mut Vec<Position>) {
debug_assert!(self.pips[X_BAR] == 0);

let mut moves: Vec<Position> = Vec::with_capacity(MOVES_CAPACITY);
(self.smallest_pip_to_check(die)..X_BAR).for_each(|i| {
if self.can_move_when_bearoff_is_legal(i, die) {
let position = self.clone_and_move_single_checker(i, die);
moves.push(position);
}
});
debug_assert!(!moves.is_empty());
moves
}

// All moves with no checkers on the bar where two checkers can be moved.
Expand Down Expand Up @@ -141,7 +148,6 @@ impl Position {
}
});

debug_assert!(!moves.is_empty());
moves
}

Expand All @@ -159,55 +165,6 @@ impl Position {
vec![position]
}

/// Will return 2 if both dice can be used.
/// Will return 0 if no checker can be moved.
fn move_possibilities(&self, dice: &MixedDice) -> MovePossibilities {
debug_assert!(self.pips[X_BAR] == 0);

let mut can_move_big = false;
let mut can_move_small = false;

// Move dice.big first
for i in self.smallest_pip_to_check(dice.big)..X_BAR {
if self.can_move_when_bearoff_is_legal(i, dice.big) {
can_move_big = true;
let position = self.clone_and_move_single_checker(i, dice.big);
for j in position.smallest_pip_to_check(dice.small)..X_BAR {
if position.can_move_when_bearoff_is_legal(j, dice.small) {
return MovePossibilities::Two;
}
}
}
}

// Move dice.small first, assuming dice.big cannot be moved first
for i in self.smallest_pip_to_check(dice.small)..X_BAR {
if self.can_move_when_bearoff_is_legal(i, dice.small) {
can_move_small = true;
let position = self.clone_and_move_single_checker(i, dice.small);
// If die1 and die2 could be used with different checkers without bearing off, then we would not get here.
// So, we only need to check if die1 can be moved with the same checker as die2.
if i > dice.small && position.can_move_in_board(i - dice.small, dice.big) {
return MovePossibilities::Two;
}
// Now checking bearing off
for j in position.smallest_pip_to_check(dice.big)..7 {
if position.can_move_when_bearoff_is_legal(j, dice.big) {
return MovePossibilities::Two;
}
}
}
}

if can_move_big {
MovePossibilities::One { die: dice.big }
} else if can_move_small {
MovePossibilities::One { die: dice.small }
} else {
MovePossibilities::None
}
}

fn can_enter(&self, die: usize) -> bool {
debug_assert!(
self.pips[X_BAR] > 0,
Expand Down Expand Up @@ -243,12 +200,6 @@ impl Position {
}
}

enum MovePossibilities {
None,
One { die: usize },
Two,
}

#[cfg(test)]
mod tests {
use crate::dice::MixedDice;
Expand Down

0 comments on commit 643633f

Please sign in to comment.