Skip to content

Commit

Permalink
Add final state to sequential outcome
Browse files Browse the repository at this point in the history
  • Loading branch information
walkie committed Sep 17, 2024
1 parent bd61faf commit b543e8d
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 12 deletions.
22 changes: 21 additions & 1 deletion t4t-games/src/tic_tac_toe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
//! # Example
//!
//! ```
//! use log::warn;
//! use t4t::*;
//! use t4t_games::tic_tac_toe::*;
//!
Expand All @@ -23,10 +24,14 @@
//! let mut cats_games = 0;
//!
//! // Play 5 games, letting Random go first.
//! for _ in 0..5 {
//! for i in 0..5 {
//! let outcome =
//! TicTacToe.play(&Matchup::from_players([random.clone(), minimax.clone()])).unwrap();
//!
//! // Print the final board state.
//! println!("\nGame {}:", i + 1);
//! outcome.final_state().print();
//!
//! // Update the scores.
//! let payoff = *outcome.payoff();
//! if payoff == Payoff::zeros() {
Expand Down Expand Up @@ -187,6 +192,21 @@ impl Board {
}
None
}

/// Print the current state of the board to stdout.
pub fn print(&self) {
let mark = |r: usize, c: usize| match self.squares[r][c] {
None => ' ',
Some(Mark::X) => 'X',
Some(Mark::O) => 'O',
};

println!("{}|{}|{}", mark(0, 0), mark(0, 1), mark(0, 2));
println!("-----");
println!("{}|{}|{}", mark(1, 0), mark(1, 1), mark(1, 2));
println!("-----");
println!("{}|{}|{}", mark(2, 0), mark(2, 1), mark(2, 2));
}
}

impl Default for Board {
Expand Down
10 changes: 7 additions & 3 deletions t4t/src/combinatorial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,14 @@ fn generate_tree<G: Combinatorial<P> + 'static, const P: usize>(
game: Arc<G>,
state: G::State,
transcript: Transcript<G::Move, P>,
) -> GameTree<G::State, G::Move, G::Utility, SequentialOutcome<G::Move, G::Utility, P>, P> {
) -> GameTree<G::State, G::Move, G::Utility, SequentialOutcome<G::State, G::Move, G::Utility, P>, P>
{
let player = game.whose_turn(&state);
match game.payoff(&state) {
Some(payoff) => GameTree::end(state, SequentialOutcome::new(transcript, payoff)),
Some(payoff) => GameTree::end(
state.clone(),
SequentialOutcome::new(state, transcript, payoff),
),

None => GameTree::player(state, player, move |state, the_move| {
let next_state = game.next_state(state, the_move)?;
Expand All @@ -61,7 +65,7 @@ fn generate_tree<G: Combinatorial<P> + 'static, const P: usize>(
}

impl<G: Combinatorial<P> + 'static, const P: usize> Playable<P> for G {
type Outcome = SequentialOutcome<Self::Move, Self::Utility, P>;
type Outcome = SequentialOutcome<Self::State, Self::Move, Self::Utility, P>;

fn into_game_tree(self) -> GameTree<Self::State, Self::Move, Self::Utility, Self::Outcome, P> {
let initial_state = self.initial_state();
Expand Down
6 changes: 4 additions & 2 deletions t4t/src/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::marker::PhantomData;

use crate::{
Move, Outcome, Past, Payoff, PlayerIndex, Plies, Profile, Record, SequentialOutcome,
SimultaneousOutcome, Summary, Transcript, Utility,
SimultaneousOutcome, State, Summary, Transcript, Utility,
};

/// For repeated games, a history of previously played games.
Expand Down Expand Up @@ -74,7 +74,9 @@ impl<M: Move, U: Utility, const P: usize> History<M, U, SimultaneousOutcome<M, U
}
}

impl<M: Move, U: Utility, const P: usize> History<M, U, SequentialOutcome<M, U, P>, P> {
impl<S: State, M: Move, U: Utility, const P: usize>
History<M, U, SequentialOutcome<S, M, U, P>, P>
{
/// Get an iterator over the transcripts of previously played games.
pub fn transcripts(&self) -> Past<&Transcript<M, P>> {
Past::from_iter(
Expand Down
26 changes: 20 additions & 6 deletions t4t/src/outcome.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::fmt::Debug;
use std::sync::Arc;

use crate::{Move, Payoff, PlayerIndex, PossibleProfiles, Profile, Record, Transcript, Utility};
use crate::{
Move, Payoff, PlayerIndex, PossibleProfiles, Profile, Record, State, Transcript, Utility,
};

/// A (potential) outcome of a game. A payoff combined with a record of the moves that produced it.
///
Expand Down Expand Up @@ -69,15 +71,25 @@ impl<M: Move, U: Utility, const P: usize> Outcome<M, U, P> for SimultaneousOutco
///
/// For extensive-form games, an outcome corresponds to a path through the game tree.
#[derive(Clone, Debug, PartialEq, Hash)]
pub struct SequentialOutcome<M: Move, U: Utility, const P: usize> {
pub struct SequentialOutcome<S: State, M: Move, U: Utility, const P: usize> {
final_state: S,
transcript: Transcript<M, P>,
payoff: Payoff<U, P>,
}

impl<M: Move, U: Utility, const P: usize> SequentialOutcome<M, U, P> {
impl<S: State, M: Move, U: Utility, const P: usize> SequentialOutcome<S, M, U, P> {
/// Construct a new sequential game outcome.
pub fn new(transcript: Transcript<M, P>, payoff: Payoff<U, P>) -> Self {
SequentialOutcome { transcript, payoff }
pub fn new(final_state: S, transcript: Transcript<M, P>, payoff: Payoff<U, P>) -> Self {
SequentialOutcome {
final_state,
transcript,
payoff,
}
}

/// The final state when the game ended.
pub fn final_state(&self) -> &S {
&self.final_state
}

/// The move transcript associated with this outcome.
Expand All @@ -86,7 +98,9 @@ impl<M: Move, U: Utility, const P: usize> SequentialOutcome<M, U, P> {
}
}

impl<M: Move, U: Utility, const P: usize> Outcome<M, U, P> for SequentialOutcome<M, U, P> {
impl<S: State, M: Move, U: Utility, const P: usize> Outcome<M, U, P>
for SequentialOutcome<S, M, U, P>
{
type Record = Transcript<M, P>;

fn record(&self) -> &Transcript<M, P> {
Expand Down

0 comments on commit b543e8d

Please sign in to comment.