Skip to main content

server/domain/phase/state/
playing.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{
4    display::format_vec,
5    domain::{
6        Card, Crib, Hands, HasCrib, HasHands, HasPegging, HasPending, HasPlayState, HasRoles,
7        HasScoreboard, HasStarterCut, Pegging, Pending, PlayState, Roles, Scoreboard, StarterCut,
8    },
9};
10
11/// Represents the state of a game during the *playing* phase.
12///
13/// This structure aggregates all state required to execute card play,
14/// including score tracking, player roles, player hands, the current
15/// play-sequence state machine, and items specific to the round such as
16/// the crib, starter card, and any pending actions.
17#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
18pub struct Playing {
19    scoreboard: Scoreboard,
20    roles: Roles,
21    hands: Hands,
22    play_state: PlayState,
23    crib: Crib,
24    starter_cut: StarterCut,
25    pending: Pending,
26}
27
28impl Playing {
29    /// Constructs a new `Playing` instance from all required subcomponents.
30    ///
31    /// All inputs must represent a consistent state at the beginning of the
32    /// playing phase. The function does not perform validation; callers are
33    /// responsible for ensuring that the inputs are coherent and legal for the
34    /// game being implemented.
35    pub const fn new(
36        scoreboard: Scoreboard,
37        roles: Roles,
38        hands: Hands,
39        play_state: PlayState,
40        crib: Crib,
41        starter_cut: StarterCut,
42        pending: Pending,
43    ) -> Self {
44        Self {
45            scoreboard,
46            roles,
47            hands,
48            play_state,
49            crib,
50            starter_cut,
51            pending,
52        }
53    }
54
55    /// Plays a card from the next player in turn order.
56    ///
57    /// This method:
58    /// 1. Determines which player is next to act via `play_state.next_to_play()`.
59    /// 2. Removes the specified `card` from that player’s hand.
60    /// 3. Forwards the card to the `play_state` state machine to update
61    ///    peg totals, legality, and transition conditions.
62    ///
63    /// Callers should ensure that the card is legal to play under the rules
64    /// enforced by `PlayState`.
65    pub fn play_card(&mut self, card: Card) {
66        let player = self.play_state.next_to_play();
67
68        let hand = &mut self.hands[player];
69        hand.remove(card);
70
71        let play_state = &mut self.play_state;
72        let _ = play_state.play(card);
73    }
74
75    /// Signals “go” for the next player in turn order.
76    ///
77    /// A “go” occurs when a player cannot legally play any card without
78    /// exceeding the allowable running total. This method delegates to the
79    /// `play_state` state machine to record the event and update turn order.
80    ///
81    /// Caller should ensure the player has no valid cards to play.
82    pub fn go(&mut self) {
83        let play_state = &mut self.play_state;
84        let _ = play_state.go();
85    }
86}
87
88impl HasScoreboard for Playing {
89    fn scoreboard(&self) -> &Scoreboard {
90        &self.scoreboard
91    }
92
93    fn scoreboard_mut(&mut self) -> &mut Scoreboard {
94        &mut self.scoreboard
95    }
96}
97
98impl HasRoles for Playing {
99    fn roles(&self) -> &Roles {
100        &self.roles
101    }
102
103    fn roles_mut(&mut self) -> &mut Roles {
104        &mut self.roles
105    }
106}
107
108impl HasHands for Playing {
109    fn hands(&self) -> &Hands {
110        &self.hands
111    }
112
113    fn hands_mut(&mut self) -> &mut Hands {
114        &mut self.hands
115    }
116}
117
118impl HasCrib for Playing {
119    fn crib(&self) -> &Crib {
120        &self.crib
121    }
122
123    fn crib_mut(&mut self) -> &mut Crib {
124        &mut self.crib
125    }
126}
127
128impl HasStarterCut for Playing {
129    fn starter_cut(&self) -> &StarterCut {
130        &self.starter_cut
131    }
132}
133
134impl HasPlayState for Playing {
135    fn play_state(&self) -> &PlayState {
136        &self.play_state
137    }
138
139    fn play_state_mut(&mut self) -> &mut PlayState {
140        &mut self.play_state
141    }
142}
143
144impl HasPegging for Playing {
145    fn pegging(&self) -> Option<&Pegging> {
146        self.scoreboard.latest_pegging()
147    }
148}
149
150impl HasPending for Playing {
151    fn pending(&self) -> &Pending {
152        &self.pending
153    }
154
155    fn pending_mut(&mut self) -> &mut Pending {
156        &mut self.pending
157    }
158}
159
160impl std::fmt::Display for Playing {
161    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162        #[rustfmt::skip]
163        let Self { scoreboard, roles, hands, play_state, starter_cut, crib, pending } = self;
164        let hands = format_vec(hands);
165
166        write!(
167            f,
168            r#"Playing(
169    scoreboard: {scoreboard},
170    roles: {roles},
171    hands: {hands},
172    play_state: {play_state},
173    cut: {starter_cut},
174    crib: {crib},
175    pending: {pending}
176)"#
177        )
178    }
179}