1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
use crate::{Move, PerPlayer, PlayerIndex, Plies, Ply, Profile, Record, Summary};
/// A transcript of the moves played (so far) in a sequential game.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Transcript<M: Move, const P: usize> {
/// The sequence of played moves.
plies: im::Vector<Ply<M, P>>,
/// The number of moves played by each player.
summary: Summary<P>,
}
impl<M: Move, const P: usize> Default for Transcript<M, P> {
fn default() -> Self {
Transcript {
plies: im::Vector::new(),
summary: Summary::empty(),
}
}
}
impl<M: Move, const P: usize> Record<M, P> for Transcript<M, P> {
fn plies(&self) -> Plies<M, P> {
Plies::from_iter(self.plies.len(), self.plies.clone().into_iter())
}
fn summary(&self) -> Summary<P> {
self.summary
}
fn to_transcript(&self) -> Transcript<M, P> {
self.clone()
}
}
impl<M: Move, const P: usize> Transcript<M, P> {
/// Construct a new, empty transcript.
pub fn new() -> Self {
Transcript::default()
}
/// Construct a transcript from a ply iterator.
pub fn from_plies(plies: Plies<M, P>) -> Self {
let mut transcript = Transcript::new();
for ply in plies {
transcript.add(ply);
}
transcript
}
/// Construct a transcript from the given profile.
pub fn from_profile(profile: Profile<M, P>) -> Transcript<M, P> {
profile.to_transcript()
}
/// Convert this transcript to a profile, if possible.
///
/// Returns `None` if the transcript does not contain exactly one move per player.
pub fn to_profile(&self) -> Option<Profile<M, P>> {
if self.summary == Summary::simultaneous() {
PerPlayer::generate(|player| self.first_move_by_player(player))
.all_some()
.map(Profile::from_per_player)
} else {
None
}
}
/// Add a ply to the transcript.
pub fn add(&mut self, ply: Ply<M, P>) {
self.plies.push_back(ply);
match ply.player {
Some(p) => self.summary.increment_moves_by_player(p),
None => self.summary.increment_moves_by_chance(),
}
}
/// Add a move played by chance to the transcript.
pub fn add_chance_move(&mut self, the_move: M) {
self.add(Ply::new(None, the_move));
self.summary.increment_moves_by_chance()
}
/// Add a move played by a player (not chance) to the transcript.
pub fn add_player_move(&mut self, player: PlayerIndex<P>, the_move: M) {
self.add(Ply::new(Some(player), the_move));
self.summary.increment_moves_by_player(player)
}
/// Get all moves played by a given player (`Some`) or by chance (`None`).
pub fn moves_by(&self, player: Option<PlayerIndex<P>>) -> Vec<M> {
self.plies
.iter()
.filter(|played| played.player == player)
.map(|played| played.the_move)
.collect()
}
/// Get all moves played by chance.
pub fn moves_by_chance(&self) -> Vec<M> {
self.moves_by(None)
}
/// Get all moves played by a given player.
pub fn moves_by_player(&self, player: PlayerIndex<P>) -> Vec<M> {
self.moves_by(Some(player))
}
/// Get the first move played by a given player (`Some`) or by chance (`None`).
///
/// Returns `None` if the given player or chance has not played any moves.
pub fn first_move_by(&self, player: Option<PlayerIndex<P>>) -> Option<M> {
self.plies
.iter()
.find(|played| played.player == player)
.map(|played| played.the_move)
}
/// Get the first move played by chance.
///
/// Returns `None` if chance has not played any moves.
pub fn first_move_by_chance(&self) -> Option<M> {
self.first_move_by(None)
}
/// Get the first move played by a given player.
///
/// Returns `None` if the given player has not played any moves.
pub fn first_move_by_player(&self, player: PlayerIndex<P>) -> Option<M> {
self.first_move_by(Some(player))
}
/// Get the last move played by a given player (`Some`) or by chance (`None`).
///
/// Returns `None` if the given player or chance has not played any moves.
pub fn last_move_by(&self, player: Option<PlayerIndex<P>>) -> Option<M> {
self.plies
.iter()
.rev()
.find(|played| played.player == player)
.map(|played| played.the_move)
}
/// Get the last move played by chance.
///
/// Returns `None` if chance has not played any moves.
pub fn last_move_by_chance(&self) -> Option<M> {
self.last_move_by(None)
}
/// Get the last move played by a given player.
///
/// Returns `None` if the given player has not played any moves.
pub fn last_move_by_player(&self, player: PlayerIndex<P>) -> Option<M> {
self.last_move_by(Some(player))
}
}