1use crate::board::{Board, BoardSize, Point};
2use crate::solver::{GameResult, Solver};
3use crate::{check_interact, Cell, CellState, CellType, GameState, GameStatus, Minsweeper};
4use rand::Rng;
5use std::collections::HashSet;
6use std::ops::{Deref, DerefMut};
7
8trait InternalMinsweeper {
9
10 fn start(&mut self) -> &GameState;
11
12 fn on_win(&self);
13 fn on_lose(&self);
14
15 fn player_gamestate(&self) -> &GameState;
16 fn gamestate_mut(&mut self) -> impl DerefMut<Target = GameState>;
17
18 fn reveal(&mut self, point: Point) -> Result<&GameState, &GameState> {
19 if check_interact(self, point).is_err() {
20 return Err(self.player_gamestate())
21 }
22
23
24 let success = self.internal_reveal(point);
25
26 if !success {
27 self.gamestate_mut().status = GameStatus::Lost;
28
29 self.on_lose();
30
31 return Ok(self.player_gamestate())
32 }
33
34 if self.gamestate_mut().board.has_won() {
35 self.gamestate_mut().status = GameStatus::Won;
36
37 self.on_win();
38
39 return Ok(self.player_gamestate())
40 }
41
42 Ok(self.player_gamestate())
43
44 }
45
46 fn reveal_empty(board: &mut Board, point: Point) {
47 if !matches!(board[point], Cell { cell_type: CellType::EMPTY, cell_state: state } if state != CellState::Revealed) {
48 return
49 }
50
51 let empty_cell = Cell::new(CellType::EMPTY, CellState::Revealed);
52 board[point] = empty_cell;
53
54 let mut flood = HashSet::new();
55
56 flood.insert(point);
57
58 while !flood.is_empty() {
59 let point = *flood.iter().next().unwrap();
60 flood.remove(&point);
61
62 for point in board.size().neighbours(point) {
63 if let Cell { cell_type: CellType::Safe(number), cell_state: state } = board[point]
64 && state != CellState::Revealed {
65 board[point] = Cell::new(CellType::Safe(number), CellState::Revealed);
66
67 if number == 0 {
68 flood.insert(point);
69 }
70 }
71 }
72 }
73
74 }
75
76 fn internal_reveal(&mut self, point: Point) -> bool {
77 let mut state = self.gamestate_mut();
78 let board = &mut state.board;
80 if board[point].cell_state != CellState::Unknown {
81 return true
82 }
83
84 match board[point].cell_type {
85 CellType::Safe(number) => {
86 if number == 0 {
87 Self::reveal_empty(board, point)
88 } else {
89 board[point] = Cell::new(CellType::Safe(number), CellState::Revealed)
90 }
91 true
92 }
93 CellType::Mine => {
94 board[point] = Cell::new(CellType::Mine, CellState::Revealed);
95 false
96 }
97 _ => unreachable!()
98 }
99 }
100
101 fn clear_around(&mut self, point: Point) -> Result<&GameState, &GameState> {
102 if check_interact(self, point).is_err() {
103 return Err(self.player_gamestate())
104 }
105
106 let Cell { cell_type: CellType::Safe(number), cell_state: CellState::Revealed } = self.player_gamestate().board[point] else {
107 return Err(self.player_gamestate())
108 };
109
110 let flags = self.count_flags(point);
111
112 if flags != number as usize {
113 return Err(self.player_gamestate())
114 }
115
116 let mut success = true;
117
118 for point in self.player_gamestate().board.size().neighbours(point) {
119 success &= self.internal_reveal(point);
120 }
121
122 if !success {
123 self.gamestate_mut().status = GameStatus::Lost;
124
125 self.on_lose();
126
127 return Ok(self.player_gamestate())
128 }
129
130 if self.gamestate_mut().board.has_won() {
131 self.gamestate_mut().status = GameStatus::Won;
132
133 self.on_win();
134
135 return Ok(self.player_gamestate())
136 }
137
138 Ok(self.player_gamestate())
139 }
140
141 fn set_flagged(&mut self, point: Point, flagged: bool) -> Result<&GameState, &GameState> {
142 if check_interact(self, point).is_err() {
143 return Err(self.player_gamestate())
144 }
145
146 let mut mewo = self.gamestate_mut();
147 let state = mewo.deref_mut();
148 let cell = &mut state.board[point];
149
150 if cell.cell_state == CellState::Revealed {
151 drop(mewo);
152 return Err(self.player_gamestate())
153 }
154
155
156 if flagged != (cell.cell_state == CellState::Flagged) {
157 if flagged { state.remaining_mines -= 1 } else { state.remaining_mines += 1 }
158 }
159
160 cell.cell_state = if flagged { CellState::Flagged } else { CellState::Unknown };
161
162 drop(mewo);
163 Ok(self.player_gamestate())
164 }
165
166 fn count_flags(&self, point: Point) -> usize {
167 self.player_gamestate().board.size().neighbours(point)
168 .filter(|e| self.player_gamestate().board[*e].cell_state == CellState::Flagged)
169 .count()
170 }
171}
172
173impl<T: InternalMinsweeper + ?Sized> Minsweeper for T {
174 fn start(&mut self) -> &GameState {
175 self.start()
176 }
177
178 fn gamestate(&self) -> &GameState {
179 self.player_gamestate()
180 }
181
182 fn reveal(&mut self, point: Point) -> Result<&GameState, &GameState> {
183 self.reveal(point)
184 }
185
186 fn clear_around(&mut self, point: Point) -> Result<&GameState, &GameState> {
187 self.clear_around(point)
188 }
189
190 fn set_flagged(&mut self, point: Point, flagged: bool) -> Result<&GameState, &GameState> {
191 self.set_flagged(point, flagged)
192 }
193}
194
195
196pub fn generate_game(board_size: BoardSize) -> GameState {
197 let mut board = Board::empty(board_size);
198
199 let mine = Cell::new(CellType::Mine, CellState::Unknown);
200 let mut rng = rand::rng();
201 let mut mines = 0usize;
202 while mines < board_size.mines().into() {
203 let point = (rng.random_range(0..board_size.width().into()),
204 rng.random_range(0..board_size.height().into()));
205
206 if matches!(board[point].cell_type, CellType::Safe(_)) {
207 board[point] = mine;
208 mines += 1;
209 }
210 };
211
212 generate_nmbers(&mut board);
213
214 GameState::new(GameStatus::Playing, board, usize::from(board_size.mines()).try_into().unwrap())
215}
216
217fn generate_nmbers(board: &mut Board) {
218 let empty_unknown = Cell::new(CellType::EMPTY, CellState::Unknown);
219 for point in board.size().points() {
220 let cell = &mut board[point];
221
222 if matches!(cell.cell_type, CellType::Safe(_)) {
223 *cell = empty_unknown;
224 }
225 }
226 for point in board.size().points() {
227 if board[point].cell_type == CellType::Mine {
228 for point in board.size().neighbours(point) {
229 if let CellType::Safe(number) = board[point].cell_type {
230 board[point] = Cell::new(CellType::Safe(number + 1), CellState::Unknown);
231 }
232 }
233 }
234 }
235}
236
237pub struct MinsweeperGame {
238 board_size: BoardSize,
239 game_state: GameState,
240 player_game_state: GameState,
241 on_win: Box<dyn Fn()>,
242 on_lose: Box<dyn Fn()>,
243 first: bool,
244 solver: Option<Box<dyn Solver>>
245}
246
247impl MinsweeperGame {
248
249 pub fn new(board_size: BoardSize, on_win: Box<dyn Fn()>, on_lose: Box<dyn Fn()>) -> Self {
250 Self {
251 board_size,
252 game_state: GameState::new(GameStatus::Never, Board::empty(board_size), 0),
253 player_game_state: GameState::new(GameStatus::Never, Board::empty(board_size), 0),
254 on_win,
255 on_lose,
256 first: true,
257 solver: None
258 }
259 }
260
261 fn internal_start(&mut self, solver: Option<Box<dyn Solver>>) -> &GameState {
262 *self.gamestate_mut() = GameState::new(GameStatus::Playing, Board::empty(self.board_size),
263 usize::from(self.board_size.mines()).try_into().unwrap());
264
265 self.first = true;
266 self.solver = solver;
267
268 self.player_gamestate()
269 }
270
271 pub fn start_with_solver(&mut self, solver: Box<dyn Solver>) -> &GameState {
272 self.internal_start(solver.into())
273 }
274}
275
276impl InternalMinsweeper for MinsweeperGame {
277 fn start(&mut self) -> &GameState {
278 self.internal_start(None)
279 }
280
281 fn on_win(&self) {
282 (self.on_win)()
283 }
284
285 fn on_lose(&self) {
286 (self.on_lose)()
287 }
288
289 fn player_gamestate(&self) -> &GameState {
290 if self.game_state.status == GameStatus::Playing {
291 &self.player_game_state
292 } else {
293 &self.game_state
294 }
295 }
296
297 fn gamestate_mut(&mut self) -> impl DerefMut<Target = GameState> {
298 GameStateHandle {
299 game_state: &mut self.game_state,
300 obfuscated_game_state: &mut self.player_game_state
301 }
302 }
303
304 fn reveal(&mut self, point: Point) -> Result<&GameState, &GameState> {
305 if check_interact(self, point).is_err() {
306 return Err(self.player_gamestate())
307 }
308
309 if self.first {
310 self.first = false;
311
312 if let Some(solver) = &self.solver {
313 *self.gamestate_mut() = generate_solvable_game(self.board_size, solver.as_ref(), point);
314 } else {
315 *self.gamestate_mut() = generate_game(self.board_size);
316 }
317 }
318
319
320 let success = self.internal_reveal(point);
321
322 if !success {
323 self.gamestate_mut().status = GameStatus::Lost;
324
325 self.on_lose();
326
327 return Ok(self.player_gamestate())
328 }
329
330 if self.gamestate_mut().board.has_won() {
331 self.gamestate_mut().status = GameStatus::Won;
332
333 self.on_win();
334
335 return Ok(self.player_gamestate())
336 }
337
338 Ok(self.player_gamestate())
339 }
340
341 fn set_flagged(&mut self, point: Point, flagged: bool) -> Result<&GameState, &GameState> {
342 if check_interact(self, point).is_err() || self.first {
343 return Err(self.player_gamestate())
344 }
345
346 let mut mewo = self.gamestate_mut();
347 let state = mewo.deref_mut();
348 let cell = &mut state.board[point];
349
350 if cell.cell_state == CellState::Revealed {
351 drop(mewo);
352 return Err(self.player_gamestate())
353 }
354
355
356 if flagged != (cell.cell_state == CellState::Flagged) {
357 if flagged { state.remaining_mines -= 1 } else { state.remaining_mines += 1 }
358 }
359
360 cell.cell_state = if flagged { CellState::Flagged } else { CellState::Unknown };
361
362 drop(mewo);
363 Ok(self.player_gamestate())
364 }
365}
366
367pub fn generate_solvable_game(board_size: BoardSize, solver: &dyn Solver, point: Point) -> GameState {
368 loop {
369 let state = generate_game(board_size);
370
371 let mut game = SetMinsweeperGame::new(state.clone());
372 Minsweeper::reveal(&mut game, point)
373 .expect("should always be able to successfully reveal");
374
375 let result = solver.solve_game(&mut game);
376
377 if result == GameResult::Won {
378 return state;
379 }
380 }
381}
382
383#[derive(Clone, Debug)]
384pub struct SetMinsweeperGame {
385 game_state: GameState,
386 player_game_state: GameState
387}
388
389impl SetMinsweeperGame {
390 pub fn new(game_state: GameState) -> Self {
391 Self { player_game_state: game_state.hide_mines(), game_state }
392 }
393}
394
395impl InternalMinsweeper for SetMinsweeperGame {
396 fn start(&mut self) -> &GameState {
397 unimplemented!()
398 }
399
400 fn on_win(&self) {
401
402 }
403
404 fn on_lose(&self) {
405
406 }
407
408 fn player_gamestate(&self) -> &GameState {
409 &self.player_game_state
410 }
411
412 fn gamestate_mut(&mut self) -> impl DerefMut<Target = GameState> {
413 GameStateHandle {
414 game_state: &mut self.game_state,
415 obfuscated_game_state: &mut self.player_game_state,
416 }
417 }
418}
419
420struct GameStateHandle<'a> {
421 game_state: &'a mut GameState,
422 obfuscated_game_state: &'a mut GameState
423}
424
425impl AsMut<GameState> for GameStateHandle<'_> {
426 fn as_mut(&mut self) -> &mut GameState {
427 self.game_state
428 }
429}
430
431impl Deref for GameStateHandle<'_> {
432 type Target = GameState;
433
434 fn deref(&self) -> &Self::Target {
435 self.game_state
436 }
437}
438
439impl DerefMut for GameStateHandle<'_> {
440 fn deref_mut(&mut self) -> &mut Self::Target {
441 self.game_state
442 }
443}
444
445impl Drop for GameStateHandle<'_> {
446 fn drop(&mut self) {
447 *self.obfuscated_game_state = self.game_state.hide_mines()
448 }
449}
450