1use crate::board::Board;
2use crate::types::{Coord, Player, WIN_AXES};
3
4fn count_consecutive(board: &Board, coord: Coord, dir: Coord, player: Player, max: u8) -> u8 {
8 let mut count = 0u8;
9 let mut cur = (coord.0 + dir.0, coord.1 + dir.1);
10 while count < max {
11 match board.get(cur) {
12 Some(p) if p == player => {
13 count += 1;
14 cur = (cur.0 + dir.0, cur.1 + dir.1);
15 }
16 _ => break,
17 }
18 }
19 count
20}
21
22pub fn check_win(board: &Board, coord: Coord, player: Player, win_length: u8) -> bool {
25 let max = win_length - 1; for &(dq, dr) in &WIN_AXES {
27 let fwd = count_consecutive(board, coord, (dq, dr), player, max);
28 let bwd = count_consecutive(board, coord, (-dq, -dr), player, max.saturating_sub(fwd));
29 if 1 + fwd + bwd >= win_length {
30 return true;
31 }
32 }
33 false
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39
40 fn build_board(stones: &[(Coord, Player)]) -> Board {
46 let mut board = Board::new();
53 for &(coord, player) in stones {
54 if coord == (0, 0) {
55 debug_assert_eq!(player, Player::P1, "Board::new puts P1 at origin");
58 continue;
59 }
60 board.place(coord, player).expect("test: duplicate stone in build_board");
61 }
62 board
63 }
64
65 #[test]
68 fn count_consecutive_empty_returns_zero() {
69 let board = build_board(&[]);
70 let n = count_consecutive(&board, (0, 0), (1, 0), Player::P1, u8::MAX);
72 assert_eq!(n, 0);
73 }
74
75 #[test]
76 fn count_consecutive_three_in_a_row() {
77 let board = build_board(&[
79 ((0, 0), Player::P1),
80 ((1, 0), Player::P1),
81 ((2, 0), Player::P1),
82 ((3, 0), Player::P1),
83 ]);
84 let n = count_consecutive(&board, (0, 0), (1, 0), Player::P1, u8::MAX);
85 assert_eq!(n, 3);
86 }
87
88 #[test]
89 fn count_consecutive_stops_at_opponent() {
90 let board = build_board(&[
92 ((0, 0), Player::P1),
93 ((1, 0), Player::P1),
94 ((2, 0), Player::P2),
95 ((3, 0), Player::P1),
96 ]);
97 let n = count_consecutive(&board, (0, 0), (1, 0), Player::P1, u8::MAX);
98 assert_eq!(n, 1); }
100
101 #[test]
102 fn count_consecutive_stops_at_empty_gap() {
103 let board = build_board(&[
105 ((0, 0), Player::P1),
106 ((1, 0), Player::P1),
107 ((3, 0), Player::P1),
108 ]);
109 let n = count_consecutive(&board, (0, 0), (1, 0), Player::P1, u8::MAX);
110 assert_eq!(n, 1); }
112
113 #[test]
116 fn check_win_single_stone_no_win() {
117 let board = build_board(&[((0, 0), Player::P1)]);
119 assert!(!check_win(&board, (0, 0), Player::P1, 6));
120 }
121
122 #[test]
123 fn check_win_six_in_a_row_axis_q() {
124 let board = build_board(&[
126 ((0, 0), Player::P1),
127 ((1, 0), Player::P1),
128 ((2, 0), Player::P1),
129 ((3, 0), Player::P1),
130 ((4, 0), Player::P1),
131 ((5, 0), Player::P1),
132 ]);
133 assert!(check_win(&board, (3, 0), Player::P1, 6));
134 }
135
136 #[test]
137 fn check_win_six_in_a_row_axis_r() {
138 let board = build_board(&[
140 ((10, 0), Player::P1),
141 ((10, 1), Player::P1),
142 ((10, 2), Player::P1),
143 ((10, 3), Player::P1),
144 ((10, 4), Player::P1),
145 ((10, 5), Player::P1),
146 ]);
147 assert!(check_win(&board, (10, 2), Player::P1, 6));
148 }
149
150 #[test]
151 fn check_win_six_in_a_row_axis_diagonal() {
152 let board = build_board(&[
154 ((10, 0), Player::P1),
155 ((11, -1), Player::P1),
156 ((12, -2), Player::P1),
157 ((13, -3), Player::P1),
158 ((14, -4), Player::P1),
159 ((15, -5), Player::P1),
160 ]);
161 assert!(check_win(&board, (12, -2), Player::P1, 6));
162 }
163
164 #[test]
165 fn check_win_five_is_not_enough_for_six() {
166 let board = build_board(&[
168 ((0, 0), Player::P1),
169 ((1, 0), Player::P1),
170 ((2, 0), Player::P1),
171 ((3, 0), Player::P1),
172 ((4, 0), Player::P1),
173 ]);
174 assert!(!check_win(&board, (2, 0), Player::P1, 6));
175 }
176
177 #[test]
178 fn check_win_seven_also_wins() {
179 let board = build_board(&[
181 ((0, 0), Player::P1),
182 ((1, 0), Player::P1),
183 ((2, 0), Player::P1),
184 ((3, 0), Player::P1),
185 ((4, 0), Player::P1),
186 ((5, 0), Player::P1),
187 ((6, 0), Player::P1),
188 ]);
189 assert!(check_win(&board, (3, 0), Player::P1, 6));
190 }
191
192 #[test]
193 fn check_win_wrong_player_no_win() {
194 let board = build_board(&[
196 ((0, 0), Player::P1),
197 ((1, 0), Player::P1),
198 ((2, 0), Player::P1),
199 ((3, 0), Player::P1),
200 ((4, 0), Player::P1),
201 ((5, 0), Player::P1),
202 ]);
203 assert!(!check_win(&board, (5, 0), Player::P2, 6));
204 }
205
206 #[test]
207 fn check_win_four_in_a_row_curriculum_variant() {
208 let board = build_board(&[
210 ((0, 0), Player::P1),
211 ((1, 0), Player::P1),
212 ((2, 0), Player::P1),
213 ((3, 0), Player::P1),
214 ]);
215 assert!(check_win(&board, (2, 0), Player::P1, 4));
216 }
217
218 #[test]
219 fn check_win_three_not_enough_for_four() {
220 let board = build_board(&[
222 ((0, 0), Player::P1),
223 ((1, 0), Player::P1),
224 ((2, 0), Player::P1),
225 ]);
226 assert!(!check_win(&board, (1, 0), Player::P1, 4));
227 }
228}