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))
    }
}