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 .game_state
408 .clone()
409 }
410
411
412 pub async fn reveal(&self, point: Point) -> Result<GameState, GameState> {
413 let mut game = self.minsweeper_game.write().await;
414 if check_interact(&*game, point).is_err() {
415 return Err(game.player_gamestate().clone())
416 }
417
418 if game.first {
419 game.first = false;
420
421
422 let solver = game.solver.clone();
423 let size = game.board_size;
424 drop(game);
425 let generate_guard = self.generate_lock.lock();
426 let gamestate = if let Some(solver) = solver {
427 generate_solvable_game_async(size, &solver, point).await
428 } else {
429 generate_game(size)
430 };
431 *self.minsweeper_game.write().await.gamestate_mut() = gamestate;
432 drop(generate_guard);
433 }
434
435 let mut game = self.minsweeper_game.write().await;
436 Minsweeper::reveal(&mut *game, point)
437 .cloned()
438 .map_err(Clone::clone)
439 }
440
441
442 pub async fn clear_around(&self, point: Point) -> Result<GameState, GameState> {
443 Minsweeper::clear_around(&mut *self.minsweeper_game.write().await, point)
444 .cloned()
445 .map_err(Clone::clone)
446 }
447
448 pub async fn set_flagged(&self, point: Point, flagged: bool) -> Result<GameState, GameState> {
449 Minsweeper::set_flagged(&mut *self.minsweeper_game.write().await, point, flagged)
450 .cloned()
451 .map_err(Clone::clone)
452 }
453
454 pub async fn toggle_flag(&self, point: Point) -> Result<GameState, GameState> {
455 Minsweeper::toggle_flag(&mut *self.minsweeper_game.write().await, point)
456 .cloned()
457 .map_err(Clone::clone)
458 }
459
460 pub async fn left_click(&self, point: Point) -> Result<GameState, GameState> {
461 let game = self.minsweeper_game.read().await;
462 if check_interact(&*game, point).is_err() {
463 return Err(game.gamestate().clone())
464 }
465
466 let cell = game.gamestate().board[point];
467
468 match cell {
469 Cell { cell_type: CellType::Safe(_), cell_state: CellState::Revealed } => {
470 drop(game);
471 self.clear_around(point).await
472 },
473 Cell { cell_state: CellState::Unknown, .. } => {
474 drop(game);
475 self.reveal(point).await
476 },
477 _ => Err(game.gamestate().clone())
478 }
479 }
480
481 pub async fn right_click(&self, point: Point) -> Result<GameState, GameState> {
482 self.toggle_flag(point).await
483 }
484 }
485}
486
487pub fn generate_solvable_game(board_size: BoardSize, solver: &dyn Solver, point: Point) -> GameState {
488 loop {
489 let state = generate_game(board_size);
490
491 let mut game = SetMinsweeperGame::new(state.clone());
492 Minsweeper::reveal(&mut game, point)
493 .expect("should always be able to successfully reveal");
494
495 let result = solver.solve_game(&mut game);
496
497 if result == GameResult::Won {
498 return state;
499 }
500 }
501}
502
503pub async fn generate_solvable_game_async<S: Solver + Send + Sync>(board_size: BoardSize, solver: &S, point: Point) -> GameState {
504 loop {
505 let Some(state) = try_generate_solvable_game_async(board_size, solver, point).await else {
506 continue
507 };
508 return state
509 }
510}
511async fn try_generate_solvable_game_async<S: Solver + Send + Sync>(board_size: BoardSize, solver: &S, point: Point) -> Option<GameState> {
512 let state = generate_game(board_size);
513
514 let mut game = SetMinsweeperGame::new(state.clone());
515 Minsweeper::reveal(&mut game, point)
516 .expect("should always be able to successfully reveal");
517
518 let result = solver.solve_game(&mut game);
519
520 if result == GameResult::Won {
521 Some(state)
522 } else {
523 None
524 }
525}
526
527#[derive(Clone, Debug)]
528pub struct SetMinsweeperGame {
529 game_state: GameState,
530 player_game_state: GameState
531}
532
533impl SetMinsweeperGame {
534 pub fn new(game_state: GameState) -> Self {
535 Self { player_game_state: game_state.hide_mines(), game_state }
536 }
537}
538
539impl InternalMinsweeper for SetMinsweeperGame {
540 fn start(&mut self) -> &GameState {
541 unimplemented!()
542 }
543
544 fn on_win(&self) {
545
546 }
547
548 fn on_lose(&self) {
549
550 }
551
552 fn player_gamestate(&self) -> &GameState {
553 &self.player_game_state
554 }
555
556 fn gamestate_mut(&mut self) -> impl DerefMut<Target = GameState> {
557 GameStateHandle {
558 game_state: &mut self.game_state,
559 obfuscated_game_state: &mut self.player_game_state,
560 }
561 }
562}
563
564struct GameStateHandle<'a> {
565 game_state: &'a mut GameState,
566 obfuscated_game_state: &'a mut GameState
567}
568
569impl AsMut<GameState> for GameStateHandle<'_> {
570 fn as_mut(&mut self) -> &mut GameState {
571 self.game_state
572 }
573}
574
575impl Deref for GameStateHandle<'_> {
576 type Target = GameState;
577
578 fn deref(&self) -> &Self::Target {
579 self.game_state
580 }
581}
582
583impl DerefMut for GameStateHandle<'_> {
584 fn deref_mut(&mut self) -> &mut Self::Target {
585 self.game_state
586 }
587}
588
589impl Drop for GameStateHandle<'_> {
590 fn drop(&mut self) {
591 *self.obfuscated_game_state = self.game_state.hide_mines()
592 }
593}
594