Skip to content

Commit

Permalink
Make game trees games directly
Browse files Browse the repository at this point in the history
  • Loading branch information
walkie committed Jun 23, 2024
1 parent d328e43 commit 594c6ea
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 71 deletions.
15 changes: 8 additions & 7 deletions t4t-games/src/dilemma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,12 @@ impl Dilemma {
Dilemma { game, utils }
}

/// Get the normal form representation of this game.
/// Convert this game into its normal-form representation.
pub fn into_normal(self) -> Normal<Move, i64, 2> {
self.game
}

/// Get the normal-form representation of this game.
pub fn as_normal(&self) -> &Normal<Move, i64, 2> {
&self.game
}
Expand Down Expand Up @@ -386,15 +391,11 @@ impl Game<2> for Dilemma {
type State = ();
type View = ();

fn game_tree(&self) -> GameTree<(), Move, SimultaneousOutcome<Move, i64, 2>, 2> {
self.as_normal().game_tree()
fn into_game_tree(self) -> GameTree<(), Move, i64, SimultaneousOutcome<Move, i64, 2>, 2> {
self.into_normal().into_game_tree()
}

fn state_view(&self, _state: &(), _player: PlayerIndex<2>) {}

fn is_valid_move(&self, _state: &(), _player: PlayerIndex<2>, _the_move: Move) -> bool {
true
}
}

// Strategies
Expand Down
22 changes: 11 additions & 11 deletions t4t/src/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,23 @@ pub trait Game<const P: usize>: Clone + Sized + Send + Sync {
/// state that are not visible to players while making strategic decisions.
type View: State;

/// The root of the game tree for this game.
/// Convert this game into the corresponding game tree.
///
/// The game tree is effectively a step-by-step executable description of how this game is
/// played.
fn game_tree(&self) -> GameTree<Self::State, Self::Move, Self::Outcome, P>;
fn into_game_tree(self) -> GameTree<Self::State, Self::Move, Self::Utility, Self::Outcome, P>;

/// Get the corresponding game tree for this game.
///
/// The game tree is effectively a step-by-step executable description of how this game is
/// played.
fn game_tree(&self) -> GameTree<Self::State, Self::Move, Self::Utility, Self::Outcome, P> {
self.clone().into_game_tree()
}

/// Produce a view of the game state for the given player.
fn state_view(&self, state: &Self::State, player: PlayerIndex<P>) -> Self::View;

/// Is this a valid move in the given context?
fn is_valid_move(
&self,
state: &Self::State,
player: PlayerIndex<P>,
the_move: Self::Move,
) -> bool;

/// The number of players this game is for.
fn num_players(&self) -> usize {
P
Expand Down Expand Up @@ -96,7 +96,7 @@ pub trait Game<const P: usize>: Clone + Sized + Send + Sync {
}
}

Action::End { outcome } => return Ok(outcome),
Action::End { outcome, .. } => return Ok(outcome),
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions t4t/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
//!
//! The library provides a variety of types for defining common kinds of games:
//!
//! - [`GameTree`]: A very generic game-tree representation. This is not very convenient to use
//! directly, but all games are eventually translated into this type in order to be executed.
//! - [`Normal`] -- A general representation of n-ary [normal-form games][normal-form-game].
//! An arbitrary number of players simultaneously play a single move selected from a finite set
//! of available moves.
//! - [`Simultaneous`] -- N-ary [simultaneous games][simultaneous-game].
//! Similar to [`Normal`], except the moves available to each player may be non-finite.
//! - `Extensive` (coming soon): A simple representation of [extensive-form games][extensive-form-game].
//! Games represented as complete game trees, where players take turns making moves,
//! - `Extensive` (coming soon): A simple representation of [extensive-form games][extensive-form-game],
//! that is, games represented as complete game trees, where players take turns making moves,
//! possibly with moves of chance interspersed.
//! - `StateBased` (coming soon): Games that revolve around manipulating a shared state.
//! - [`Repeated`]: Games where another game is played repeatedly a given number of times.
Expand Down
6 changes: 1 addition & 5 deletions t4t/src/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,7 @@ impl<M: Move, U: Utility, const P: usize> Game<P> for Normal<M, U, P> {
type State = ();
type View = ();

fn game_tree(&self) -> GameTree<(), M, SimultaneousOutcome<M, U, P>, P> {
fn into_game_tree(self) -> GameTree<(), M, U, SimultaneousOutcome<M, U, P>, P> {
let state = Arc::new(());
GameTree::all_players(state.clone(), move |_, profile| {
for ply in profile.plies() {
Expand All @@ -961,10 +961,6 @@ impl<M: Move, U: Utility, const P: usize> Game<P> for Normal<M, U, P> {
}

fn state_view(&self, _state: &(), _player: PlayerIndex<P>) {}

fn is_valid_move(&self, _state: &(), player: PlayerIndex<P>, the_move: M) -> bool {
self.is_valid_move_for_player(player, the_move)
}
}

impl<M: Move, U: Utility, const P: usize> FiniteGame<P> for Normal<M, U, P> {
Expand Down
40 changes: 20 additions & 20 deletions t4t/src/repeated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ impl<G: Game<P>, const P: usize> RepeatedState<G, P> {
}
}

fn lift_node<'g, G: Game<P> + 'g, const P: usize>(
stage_game: &'g G,
fn lift_node<G: Game<P> + 'static, const P: usize>(
stage_game: Arc<G>,
state: Arc<RepeatedState<G, P>>,
node: GameTree<'g, G::State, G::Move, G::Outcome, P>,
) -> GameTree<'g, RepeatedState<G, P>, G::Move, History<G, P>, P> {
node: GameTree<G::State, G::Move, G::Utility, G::Outcome, P>,
) -> GameTree<RepeatedState<G, P>, G::Move, G::Utility, History<G, P>, P> {
match node.action {
Action::Turns {
to_move: players,
Expand All @@ -92,7 +92,11 @@ fn lift_node<'g, G: Game<P> + 'g, const P: usize>(
let mut next_state = (*state).clone();
next_state.stage_state = stage_node.state.clone();

Ok(lift_node(stage_game, Arc::new(next_state), stage_node))
Ok(lift_node(
stage_game.clone(),
Arc::new(next_state),
stage_node,
))
}

Err(kind) => Err(kind),
Expand All @@ -110,14 +114,18 @@ fn lift_node<'g, G: Game<P> + 'g, const P: usize>(
let mut next_state = (*state).clone();
next_state.stage_state = stage_node.state.clone();

Ok(lift_node(stage_game, Arc::new(next_state), stage_node))
Ok(lift_node(
stage_game.clone(),
Arc::new(next_state),
stage_node,
))
}

Err(kind) => Err(kind),
},
),

Action::End { outcome } if state.remaining > 0 => {
Action::End { outcome, .. } if state.remaining > 0 => {
let stage_node = stage_game.game_tree();

let mut next_state = (*state).clone();
Expand All @@ -129,7 +137,7 @@ fn lift_node<'g, G: Game<P> + 'g, const P: usize>(
lift_node(stage_game, Arc::new(next_state), stage_node)
}

Action::End { outcome } => {
Action::End { outcome, .. } => {
let mut history = state.completed.clone(); // TODO avoid this clone
history.add(outcome);

Expand All @@ -145,14 +153,16 @@ impl<G: Game<P> + 'static, const P: usize> Game<P> for Repeated<G, P> {
type State = RepeatedState<G, P>;
type View = RepeatedState<G, P>; // TODO add RepeatedStateView or some other solution

fn game_tree(&self) -> GameTree<RepeatedState<G, P>, G::Move, History<G, P>, P> {
fn into_game_tree(
self,
) -> GameTree<RepeatedState<G, P>, G::Move, G::Utility, History<G, P>, P> {
let init_state = Arc::new(RepeatedState::new(
self.stage_game.clone(),
self.repetitions - 1,
));

lift_node(
self.stage_game.as_ref(),
self.stage_game.clone(),
init_state,
self.stage_game.game_tree(),
)
Expand All @@ -165,16 +175,6 @@ impl<G: Game<P> + 'static, const P: usize> Game<P> for Repeated<G, P> {
) -> RepeatedState<G, P> {
state.clone() // TODO
}

fn is_valid_move(
&self,
state: &Self::State,
player: PlayerIndex<P>,
the_move: Self::Move,
) -> bool {
self.stage_game
.is_valid_move(&state.stage_state, player, the_move)
}
}

impl<G: FiniteGame<P> + 'static, const P: usize> FiniteGame<P> for Repeated<G, P> {
Expand Down
6 changes: 1 addition & 5 deletions t4t/src/simultaneous.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ impl<M: Move, U: Utility, const P: usize> Game<P> for Simultaneous<M, U, P> {
type State = ();
type View = ();

fn game_tree(&self) -> GameTree<(), M, SimultaneousOutcome<M, U, P>, P> {
fn into_game_tree(self) -> GameTree<(), M, U, SimultaneousOutcome<M, U, P>, P> {
let state = Arc::new(());
GameTree::all_players(state.clone(), move |_, profile| {
for ply in profile.plies() {
Expand All @@ -142,10 +142,6 @@ impl<M: Move, U: Utility, const P: usize> Game<P> for Simultaneous<M, U, P> {
}

fn state_view(&self, _state: &(), _player: PlayerIndex<P>) {}

fn is_valid_move(&self, _state: &(), player: PlayerIndex<P>, the_move: M) -> bool {
self.is_valid_move_for_player(player, the_move)
}
}

#[cfg(test)]
Expand Down
Loading

0 comments on commit 594c6ea

Please sign in to comment.