1use crate::board::{Board, BoardSize, Point};
2use crate::solver::{GameResult, Solver};
3use crate::{check_interact, Cell, CellState, CellType, GameState, GameStatus, Minsweeper};
4use std::collections::HashSet;
5use std::ops::{Deref, DerefMut};
6
7trait InternalMinsweeper {
8
9 fn start(&mut self) -> &GameState;
10
11 fn on_win(&self);
12 fn on_lose(&self);
13
14 fn player_gamestate(&self) -> &GameState;
15 fn gamestate_mut(&mut self) -> impl DerefMut<Target = GameState>;
16
17 fn reveal(&mut self, point: Point) -> Result<&GameState, &GameState> {
18 if check_interact(self, point).is_err() {
19 return Err(self.player_gamestate())
20 }
21
22
23 let success = self.internal_reveal(point);
24
25 if !success {
26 self.gamestate_mut().status = GameStatus::Lost;
27
28 self.on_lose();
29
30 return Ok(self.player_gamestate())
31 }
32
33 if self.gamestate_mut().board.has_won() {
34 self.gamestate_mut().status = GameStatus::Won;
35
36 self.on_win();
37
38 return Ok(self.player_gamestate())
39 }
40
41 Ok(self.player_gamestate())
42
43 }
44
45 fn reveal_empty(board: &mut Board, point: Point) {
46 if !matches!(board[point], Cell { cell_type: CellType::EMPTY, cell_state: state } if state != CellState::Revealed) {
47 return
48 }
49
50 let empty_cell = Cell::new(CellType::EMPTY, CellState::Revealed);
51 board[point] = empty_cell;
52
53 let mut flood = HashSet::new();
54
55 flood.insert(point);
56
57 while !flood.is_empty() {
58 let point = *flood.iter().next().unwrap();
59 flood.remove(&point);
60
61 for point in board.size().neighbours(point) {
62 if let Cell { cell_type: CellType::Safe(number), cell_state: state } = board[point]
63 && state != CellState::Revealed {
64 board[point] = Cell::new(CellType::Safe(number), CellState::Revealed);
65
66 if number == 0 {
67 flood.insert(point);
68 }
69 }
70 }
71 }
72
73 }
74
75 fn internal_reveal(&mut self, point: Point) -> bool {
76 let mut state = self.gamestate_mut();
77 let board = &mut state.board;
79 if board[point].cell_state != CellState::Unknown {
80 return true
81 }
82
83 match board[point].cell_type {
84 CellType::Safe(number) => {
85 if number == 0 {
86 Self::reveal_empty(board, point)
87 } else {
88 board[point] = Cell::new(CellType::Safe(number), CellState::Revealed)
89 }
90 true
91 }
92 CellType::Mine => {
93 board[point] = Cell::new(CellType::Mine, CellState::Revealed);
94 false
95 }
96 _ => unreachable!()
97 }
98 }
99
100 fn clear_around(&mut self, point: Point) -> Result<&GameState, &GameState> {
101 if check_interact(self, point).is_err() {
102 return Err(self.player_gamestate())
103 }
104
105 let Cell { cell_type: CellType::Safe(number), cell_state: CellState::Revealed } = self.player_gamestate().board[point] else {
106 return Err(self.player_gamestate())
107 };
108
109 let flags = self.count_flags(point);
110
111 if flags != number as usize {
112 return Err(self.player_gamestate())
113 }
114
115 let mut success = true;
116
117 for point in self.player_gamestate().board.size().neighbours(point) {
118 success &= self.internal_reveal(point);
119 }
120
121 if !success {
122 self.gamestate_mut().status = GameStatus::Lost;
123
124 self.on_lose();
125
126 return Ok(self.player_gamestate())
127 }
128
129 if self.gamestate_mut().board.has_won() {
130 self.gamestate_mut().status = GameStatus::Won;
131
132 self.on_win();
133
134 return Ok(self.player_gamestate())
135 }
136
137 Ok(self.player_gamestate())
138 }
139
140 fn set_flagged(&mut self, point: Point, flagged: bool) -> Result<&GameState, &GameState> {
141 if check_interact(self, point).is_err() {
142 return Err(self.player_gamestate())
143 }
144
145 let mut mewo = self.gamestate_mut();
146 let state = mewo.deref_mut();
147 let cell = &mut state.board[point];
148
149 if cell.cell_state == CellState::Revealed {
150 drop(mewo);
151 return Err(self.player_gamestate())
152 }
153
154
155 if flagged != (cell.cell_state == CellState::Flagged) {
156 if flagged { state.remaining_mines -= 1 } else { state.remaining_mines += 1 }
157 }
158
159 cell.cell_state = if flagged { CellState::Flagged } else { CellState::Unknown };
160
161 drop(mewo);
162 Ok(self.player_gamestate())
163 }
164
165 fn count_flags(&self, point: Point) -> usize {
166 self.player_gamestate().board.size().neighbours(point)
167 .filter(|e| self.player_gamestate().board[*e].cell_state == CellState::Flagged)
168 .count()
169 }
170}
171
172impl<T: InternalMinsweeper + ?Sized> Minsweeper for T {
173 fn start(&mut self) -> &GameState {
174 self.start()
175 }
176
177 fn gamestate(&self) -> &GameState {
178 self.player_gamestate()
179 }
180
181 fn reveal(&mut self, point: Point) -> Result<&GameState, &GameState> {
182 self.reveal(point)
183 }
184
185 fn clear_around(&mut self, point: Point) -> Result<&GameState, &GameState> {
186 self.clear_around(point)
187 }
188
189 fn set_flagged(&mut self, point: Point, flagged: bool) -> Result<&GameState, &GameState> {
190 self.set_flagged(point, flagged)
191 }
192}
193
194
195pub fn generate_game(board_size: BoardSize) -> GameState {
196 let mut board = Board::empty(board_size);
197
198 let mine = Cell::new(CellType::Mine, CellState::Unknown);
199 let mut mines = 0usize;
200 while mines < board_size.mines().into() {
201 let point = (fastrand::usize(0..board_size.width().into()),
202 fastrand::usize(0..board_size.height().into()));
203
204 if matches!(board[point].cell_type, CellType::Safe(_)) {
205 board[point] = mine;
206 mines += 1;
207 }
208 };
209
210 generate_nmbers(&mut board);
211
212 GameState::new(GameStatus::Playing, board, usize::from(board_size.mines()).try_into().unwrap())
213}
214
215fn generate_nmbers(board: &mut Board) {
216 let empty_unknown = Cell::new(CellType::EMPTY, CellState::Unknown);
217 for point in board.size().points() {
218 let cell = &mut board[point];
219
220 if matches!(cell.cell_type, CellType::Safe(_)) {
221 *cell = empty_unknown;
222 }
223 }
224 for point in board.size().points() {
225 if board[point].cell_type == CellType::Mine {
226 for point in board.size().neighbours(point) {
227 if let CellType::Safe(number) = board[point].cell_type {
228 board[point] = Cell::new(CellType::Safe(number + 1), CellState::Unknown);
229 }
230 }
231 }
232 }
233}
234
235pub struct MinsweeperGame<
236 S: Solver = Box<dyn Solver>,
237 OnWin: Fn() = Box<dyn Fn()>,
238 OnLose: Fn() = Box<dyn Fn()>,
239> {
240 board_size: BoardSize,
241 game_state: GameState,
242 player_game_state: GameState,
243 on_win: OnWin,
244 on_lose: OnLose,
245 first: bool,
246 solver: Option<S>
247}
248
249impl<S: Solver, OnWin: Fn(), OnLose: Fn()> MinsweeperGame<S, OnWin, OnLose> {
250
251 pub fn new(board_size: BoardSize, on_win: OnWin, on_lose: OnLose) -> Self {
252 Self {
253 board_size,
254 game_state: GameState::new(GameStatus::Never, Board::empty(board_size), 0),
255 player_game_state: GameState::new(GameStatus::Never, Board::empty(board_size), 0),
256 on_win,
257 on_lose,
258 first: true,
259 solver: None
260 }
261 }
262
263 fn internal_start(&mut self, solver: Option<S>) -> &GameState {
264 *self.gamestate_mut() = GameState::new(GameStatus::Playing, Board::empty(self.board_size),
265 usize::from(self.board_size.mines()).try_into().unwrap());
266
267 self.first = true;
268 self.solver = solver;
269
270 self.player_gamestate()
271 }
272
273 pub fn start_with_solver(&mut self, solver: S) -> &GameState {
274 self.internal_start(solver.into())
275 }
276}
277
278impl<S: Solver, OnWin: Fn(), OnLose: Fn()> InternalMinsweeper for MinsweeperGame<S, OnWin, OnLose> {
279 fn start(&mut self) -> &GameState {
280 self.internal_start(None)
281 }
282
283 fn on_win(&self) {
284 (self.on_win)()
285 }
286
287 fn on_lose(&self) {
288 (self.on_lose)()
289 }
290
291 fn player_gamestate(&self) -> &GameState {
292 if self.game_state.status == GameStatus::Playing {
293 &self.player_game_state
294 } else {
295 &self.game_state
296 }
297 }
298
299 fn gamestate_mut(&mut self) -> impl DerefMut<Target = GameState> {
300 GameStateHandle {
301 game_state: &mut self.game_state,
302 obfuscated_game_state: &mut self.player_game_state
303 }
304 }
305
306 fn reveal(&mut self, point: Point) -> Result<&GameState, &GameState> {
307 if check_interact(self, point).is_err() {
308 return Err(self.player_gamestate())
309 }
310
311 if self.first {
312 self.first = false;
313
314 if let Some(solver) = &self.solver {
315 *self.gamestate_mut() = generate_solvable_game(self.board_size, solver, point);
316 } else {
317 *self.gamestate_mut() = generate_game(self.board_size);
318 }
319 }
320
321
322 let success = self.internal_reveal(point);
323
324 if !success {
325 self.gamestate_mut().status = GameStatus::Lost;
326
327 self.on_lose();
328
329 return Ok(self.player_gamestate())
330 }
331
332 if self.gamestate_mut().board.has_won() {
333 self.gamestate_mut().status = GameStatus::Won;
334
335 self.on_win();
336
337 return Ok(self.player_gamestate())
338 }
339
340 Ok(self.player_gamestate())
341 }
342
343 fn set_flagged(&mut self, point: Point, flagged: bool) -> Result<&GameState, &GameState> {
344 if check_interact(self, point).is_err() || self.first {
345 return Err(self.player_gamestate())
346 }
347
348 let mut mewo = self.gamestate_mut();
349 let state = mewo.deref_mut();
350 let cell = &mut state.board[point];
351
352 if cell.cell_state == CellState::Revealed {
353 drop(mewo);
354 return Err(self.player_gamestate())
355 }
356
357
358 if flagged != (cell.cell_state == CellState::Flagged) {
359 if flagged { state.remaining_mines -= 1 } else { state.remaining_mines += 1 }
360 }
361
362 cell.cell_state = if flagged { CellState::Flagged } else { CellState::Unknown };
363
364 drop(mewo);
365 Ok(self.player_gamestate())
366 }
367}
368
369#[cfg(feature = "async")]
370pub mod nonblocking {
371 use crate::board::{BoardSize, Point};
372 use crate::minsweeper::{generate_game, generate_solvable_game_async, InternalMinsweeper, MinsweeperGame};
373 use crate::solver::Solver;
374 use crate::{check_interact, Cell, CellState, CellType, GameState, Minsweeper};
375 use tokio::sync::{Mutex, RwLock};
376
377 pub struct AsyncMinsweeperGame<S: Solver + Send + Sync, OnWin: Fn() + Send + Sync, OnLose: Fn() + Send + Sync> {
378 minsweeper_game: RwLock<MinsweeperGame<S, OnWin, OnLose>>,
379 generate_lock: Mutex<()>
380
381 }
382
383 impl<S: Solver + Send + Sync + Clone, OnWin: Fn() + Send + Sync, OnLose: Fn() + Send + Sync> AsyncMinsweeperGame<S, OnWin, OnLose> {
384
385 pub fn new(board_size: BoardSize, on_win: OnWin, on_lose: OnLose) -> Self {
386 Self {
387 minsweeper_game: MinsweeperGame::new(board_size, on_win, on_lose).into(),
388 generate_lock: Default::default(),
389 }
390 }
391
392 pub async fn start(&self) -> GameState {
393 Minsweeper::start(&mut *self.minsweeper_game.write().await)
394 .clone()
395 }
396
397 pub async fn start_with_solver(&self, solver: S) -> GameState {
398 self.minsweeper_game.write()
399 .await
400 .start_with_solver(solver)
401 .clone()
402 }
403
404 pub async fn gamestate(&self) -> GameState {
405 self.minsweeper_game.read()
406 .await
407 .player_gamestate()
408 .clone()
409 }
410
411 pub fn blocking_gamestate(&self) -> GameState {
412 self.minsweeper_game.blocking_read()
413 .player_gamestate()
414 .clone()
415 }
416
417
418 pub async fn reveal(&self, point: Point) -> Result<GameState, GameState> {
419 let mut game = self.minsweeper_game.write().await;
420 if check_interact(&*game, point).is_err() {
421 return Err(game.player_gamestate().clone())
422 }
423
424 if game.first {
425 game.first = false;
426
427
428 let solver = game.solver.clone();
429 let size = game.board_size;
430 drop(game);
431
432 let generate_guard = self.generate_lock.lock();
433 let gamestate = if let Some(solver) = solver {
434 generate_solvable_game_async(size, &solver, point).await
435 } else {
436 generate_game(size)
437 };
438 *self.minsweeper_game.write().await.gamestate_mut() = gamestate;
439 drop(generate_guard);
440 }
441
442 let mut game = self.minsweeper_game.write().await;
443 Minsweeper::reveal(&mut *game, point)
444 .cloned()
445 .map_err(Clone::clone)
446 }
447
448
449 pub async fn clear_around(&self, point: Point) -> Result<GameState, GameState> {
450 Minsweeper::clear_around(&mut *self.minsweeper_game.write().await, point)
451 .cloned()
452 .map_err(Clone::clone)
453 }
454
455 pub async fn set_flagged(&self, point: Point, flagged: bool) -> Result<GameState, GameState> {
456 Minsweeper::set_flagged(&mut *self.minsweeper_game.write().await, point, flagged)
457 .cloned()
458 .map_err(Clone::clone)
459 }
460
461 pub async fn toggle_flag(&self, point: Point) -> Result<GameState, GameState> {
462 Minsweeper::toggle_flag(&mut *self.minsweeper_game.write().await, point)
463 .cloned()
464 .map_err(Clone::clone)
465 }
466
467 pub async fn left_click(&self, point: Point) -> Result<GameState, GameState> {
468 let game = self.minsweeper_game.read().await;
469 if check_interact(&*game, point).is_err() {
470 return Err(game.gamestate().clone())
471 }
472
473 let cell = game.gamestate().board[point];
474
475 match cell {
476 Cell { cell_type: CellType::Safe(_), cell_state: CellState::Revealed } => {
477 drop(game);
478 self.clear_around(point).await
479 },
480 Cell { cell_state: CellState::Unknown, .. } => {
481 drop(game);
482 self.reveal(point).await
483 },
484 _ => Err(game.gamestate().clone())
485 }
486 }
487
488 pub async fn right_click(&self, point: Point) -> Result<GameState, GameState> {
489 self.toggle_flag(point).await
490 }
491 }
492}
493
494pub fn generate_solvable_game(board_size: BoardSize, solver: &dyn Solver, point: Point) -> GameState {
495 loop {
496 let state = generate_game(board_size);
497
498 let mut game = SetMinsweeperGame::new(state.clone());
499 Minsweeper::reveal(&mut game, point)
500 .expect("should always be able to successfully reveal");
501
502 let result = solver.solve_game(&mut game);
503
504 if result == GameResult::Won {
505 return state;
506 }
507 }
508}
509
510pub async fn generate_solvable_game_async<S: Solver + Send + Sync>(board_size: BoardSize, solver: &S, point: Point) -> GameState {
511 loop {
512 let Some(state) = try_generate_solvable_game_async(board_size, solver, point).await else {
513 #[cfg(feature = "tokio")]
514 tokio::task::yield_now().await;
515 continue
516 };
517 return state
518 }
519}
520async fn try_generate_solvable_game_async<S: Solver + Send + Sync>(board_size: BoardSize, solver: &S, point: Point) -> Option<GameState> {
521 let state = generate_game(board_size);
522
523 let mut game = SetMinsweeperGame::new(state.clone());
524 Minsweeper::reveal(&mut game, point)
525 .expect("should always be able to successfully reveal");
526
527 let result = solver.solve_game(&mut game);
528
529 if result == GameResult::Won {
530 Some(state)
531 } else {
532 None
533 }
534}
535
536#[derive(Clone, Debug)]
537pub struct SetMinsweeperGame {
538 game_state: GameState,
539 player_game_state: GameState
540}
541
542impl SetMinsweeperGame {
543 pub fn new(game_state: GameState) -> Self {
544 Self { player_game_state: game_state.hide_mines(), game_state }
545 }
546}
547
548impl InternalMinsweeper for SetMinsweeperGame {
549 fn start(&mut self) -> &GameState {
550 unimplemented!()
551 }
552
553 fn on_win(&self) {
554
555 }
556
557 fn on_lose(&self) {
558
559 }
560
561 fn player_gamestate(&self) -> &GameState {
562 &self.player_game_state
563 }
564
565 fn gamestate_mut(&mut self) -> impl DerefMut<Target = GameState> {
566 GameStateHandle {
567 game_state: &mut self.game_state,
568 obfuscated_game_state: &mut self.player_game_state,
569 }
570 }
571}
572
573struct GameStateHandle<'a> {
574 game_state: &'a mut GameState,
575 obfuscated_game_state: &'a mut GameState
576}
577
578impl AsMut<GameState> for GameStateHandle<'_> {
579 fn as_mut(&mut self) -> &mut GameState {
580 self.game_state
581 }
582}
583
584impl Deref for GameStateHandle<'_> {
585 type Target = GameState;
586
587 fn deref(&self) -> &Self::Target {
588 self.game_state
589 }
590}
591
592impl DerefMut for GameStateHandle<'_> {
593 fn deref_mut(&mut self) -> &mut Self::Target {
594 self.game_state
595 }
596}
597
598impl Drop for GameStateHandle<'_> {
599 fn drop(&mut self) {
600 *self.obfuscated_game_state = self.game_state.hide_mines()
601 }
602}
603