1use crate::types::Player;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum TurnState {
9 P1Turn { moves_left: u8 },
10 P2Turn { moves_left: u8 },
11 GameOver,
12}
13
14impl TurnState {
15 pub fn current_player(self) -> Option<Player> {
17 match self {
18 TurnState::P1Turn { .. } => Some(Player::P1),
19 TurnState::P2Turn { .. } => Some(Player::P2),
20 TurnState::GameOver => None,
21 }
22 }
23
24 pub fn moves_remaining(self) -> Option<u8> {
26 match self {
27 TurnState::P1Turn { moves_left } => Some(moves_left),
28 TurnState::P2Turn { moves_left } => Some(moves_left),
29 TurnState::GameOver => None,
30 }
31 }
32
33 pub fn advance(self, winner: bool) -> TurnState {
39 if winner {
40 return TurnState::GameOver;
41 }
42 match self {
43 TurnState::P1Turn { moves_left: 2 } => TurnState::P1Turn { moves_left: 1 },
44 TurnState::P1Turn { moves_left: 1 } => TurnState::P2Turn { moves_left: 2 },
45 TurnState::P1Turn { moves_left: _ } => unreachable!("invalid moves_left for P1Turn"),
46 TurnState::P2Turn { moves_left: 2 } => TurnState::P2Turn { moves_left: 1 },
47 TurnState::P2Turn { moves_left: 1 } => TurnState::P1Turn { moves_left: 2 },
48 TurnState::P2Turn { moves_left: _ } => unreachable!("invalid moves_left for P2Turn"),
49 TurnState::GameOver => TurnState::GameOver,
50 }
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[test]
61 fn current_player_p1_turn() {
62 let state = TurnState::P1Turn { moves_left: 2 };
63 assert_eq!(state.current_player(), Some(Player::P1));
64 }
65
66 #[test]
67 fn current_player_p2_turn() {
68 let state = TurnState::P2Turn { moves_left: 2 };
69 assert_eq!(state.current_player(), Some(Player::P2));
70 }
71
72 #[test]
73 fn current_player_game_over() {
74 assert_eq!(TurnState::GameOver.current_player(), None);
75 }
76
77 #[test]
80 fn moves_remaining_start_of_turn() {
81 let state = TurnState::P2Turn { moves_left: 2 };
82 assert_eq!(state.moves_remaining(), Some(2));
83 }
84
85 #[test]
86 fn moves_remaining_after_first_placement() {
87 let state = TurnState::P2Turn { moves_left: 1 };
88 assert_eq!(state.moves_remaining(), Some(1));
89 }
90
91 #[test]
92 fn moves_remaining_game_over() {
93 assert_eq!(TurnState::GameOver.moves_remaining(), None);
94 }
95
96 #[test]
99 fn advance_decrements_moves_left_from_2_to_1() {
100 let state = TurnState::P2Turn { moves_left: 2 };
101 let next = state.advance(false);
102 assert_eq!(next, TurnState::P2Turn { moves_left: 1 });
103 }
104
105 #[test]
106 fn advance_switches_player_when_moves_left_reaches_0() {
107 let state = TurnState::P2Turn { moves_left: 1 };
108 let next = state.advance(false);
109 assert_eq!(next, TurnState::P1Turn { moves_left: 2 });
110 }
111
112 #[test]
113 fn advance_winner_mid_turn_transitions_to_game_over() {
114 let state = TurnState::P2Turn { moves_left: 2 };
115 let next = state.advance(true);
116 assert_eq!(next, TurnState::GameOver);
117 }
118
119 #[test]
120 fn advance_winner_on_last_move_transitions_to_game_over() {
121 let state = TurnState::P1Turn { moves_left: 1 };
122 let next = state.advance(true);
123 assert_eq!(next, TurnState::GameOver);
124 }
125
126 #[test]
127 fn advance_game_over_returns_game_over() {
128 let next = TurnState::GameOver.advance(false);
129 assert_eq!(next, TurnState::GameOver);
130 }
131
132 #[test]
135 fn full_turn_cycle_p2_to_p1_to_p2() {
136 let s0 = TurnState::P2Turn { moves_left: 2 };
137 assert_eq!(s0.current_player(), Some(Player::P2));
138 assert_eq!(s0.moves_remaining(), Some(2));
139
140 let s1 = s0.advance(false);
141 assert_eq!(s1, TurnState::P2Turn { moves_left: 1 });
142 assert_eq!(s1.current_player(), Some(Player::P2));
143 assert_eq!(s1.moves_remaining(), Some(1));
144
145 let s2 = s1.advance(false);
146 assert_eq!(s2, TurnState::P1Turn { moves_left: 2 });
147 assert_eq!(s2.current_player(), Some(Player::P1));
148 assert_eq!(s2.moves_remaining(), Some(2));
149
150 let s3 = s2.advance(false);
151 assert_eq!(s3, TurnState::P1Turn { moves_left: 1 });
152 assert_eq!(s3.current_player(), Some(Player::P1));
153 assert_eq!(s3.moves_remaining(), Some(1));
154
155 let s4 = s3.advance(false);
156 assert_eq!(s4, TurnState::P2Turn { moves_left: 2 });
157 assert_eq!(s4.current_player(), Some(Player::P2));
158 assert_eq!(s4.moves_remaining(), Some(2));
159 }
160
161 #[test]
162 fn advance_p1_decrements_from_2_to_1() {
163 let state = TurnState::P1Turn { moves_left: 2 };
164 let next = state.advance(false);
165 assert_eq!(next, TurnState::P1Turn { moves_left: 1 });
166 }
167}