Skip to content

Commit

Permalink
feat: better accurate scoring
Browse files Browse the repository at this point in the history
  • Loading branch information
LeoDog896 committed Sep 24, 2024
1 parent aa58a33 commit 02d90f9
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 9 deletions.
11 changes: 9 additions & 2 deletions crates/game-solver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,19 @@ fn negamax<T: Game<Player = impl TwoPlayer> + Eq + Hash>(
match game.state() {
GameState::Playable => (),
GameState::Tie => return Ok(0),
GameState::Win(_) => return Ok(0),
GameState::Win(winning_player) => {
// The next player is the winning player - the score should be positive.
if game.player() == winning_player {
return Ok(upper_bound(game) - game.move_count() as isize)
} else {
return Ok(-(upper_bound(game) - game.move_count() as isize))
}
},
};

// check if this is a winning configuration
if let Ok(Some(board)) = game.find_immediately_resolvable_game() {
return Ok(upper_bound(&board) - board.move_count() as isize);
return Ok(upper_bound(&board) - board.move_count() as isize + 1);
}

// fetch values from the transposition table
Expand Down
3 changes: 2 additions & 1 deletion crates/game-solver/src/player.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// Represents a player.
pub trait Player: Sized {
pub trait Player: Sized + Eq {
/// The max player count.
#[must_use]
fn count() -> usize;
Expand Down Expand Up @@ -100,6 +100,7 @@ impl Player for ImpartialPlayer {
impl TwoPlayer for ImpartialPlayer {}

/// Represents a player in an N-player game.
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct NPlayerConst<const N: usize>(usize);

impl<const N: usize> NPlayerConst<N> {
Expand Down
11 changes: 6 additions & 5 deletions crates/games/src/nim/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::util::{cli::move_failable, move_natural::NaturalMove};
pub struct Nim {
heaps: Vec<usize>,
move_count: usize,
max_score: usize,
max_moves: usize,
}

type NimMove = NaturalMove<2>;
Expand All @@ -31,7 +31,7 @@ impl Nim {
heaps: heaps.clone(),
move_count: 0,
// sum of all the heaps is the upper bound for the amount of moves
max_score: heaps.iter().sum::<usize>(),
max_moves: heaps.iter().sum::<usize>(),
}
}
}
Expand All @@ -52,14 +52,15 @@ impl Game for Nim {
/// where Move is a tuple of the heap index and the number of objects to remove
type Move = NimMove;
type Iter<'a> = std::vec::IntoIter<Self::Move>;
/// Define Nimbers as a zero-sum game

/// Define Nim as a zero-sum impartial game
type Player = ImpartialPlayer;
type MoveError = NimMoveError;

const STATE_TYPE: Option<StateType> = Some(StateType::Normal);

fn max_moves(&self) -> Option<usize> {
Some(self.max_score)
Some(self.max_moves)
}

fn move_count(&self) -> usize {
Expand Down Expand Up @@ -128,7 +129,7 @@ impl Display for Nim {
#[derive(Args, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct NimArgs {
/// The configuration of the game. For example, 3,5,7
/// creates a Nimbers game that has three heaps, where each
/// creates a Nim game that has three heaps, where each
/// heap has 3, 5, and 7 objects respectively
configuration: String,
/// Nim moves, ordered as x1-y1 x2-y2 ...
Expand Down
8 changes: 7 additions & 1 deletion crates/games/src/util/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ pub fn play<
println!();
}
GameState::Tie => println!("No moves left! Game tied!"),
GameState::Win(player) => println!("Player {player:?} won!"),
GameState::Win(player) => {
if TypeId::of::<T::Player>() != TypeId::of::<ImpartialPlayer>() {
println!("The {player:?} player won!");
} else {
println!("Player {player:?} won!");
}
}
}
}

Expand Down

0 comments on commit 02d90f9

Please sign in to comment.