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