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 points: Vec<Point> = board_size.points().collect();
200 fastrand::shuffle(&mut points);
201 for point in &points[..board_size.mines().get()] {
202 board[*point] = mine;
203 }
204
205 generate_nmbers(&mut board);
206
207 GameState::new(GameStatus::Playing, board, usize::from(board_size.mines()).try_into().unwrap())
208}
209
210fn generate_nmbers(board: &mut Board) {
211 let empty_unknown = Cell::new(CellType::EMPTY, CellState::Unknown);
212 for point in board.size().points() {
213 let cell = &mut board[point];
214
215 if matches!(cell.cell_type, CellType::Safe(_)) {
216 *cell = empty_unknown;
217 }
218 }
219 for point in board.size().points() {
220 if board[point].cell_type == CellType::Mine {
221 for point in board.size().neighbours(point) {
222 if let CellType::Safe(number) = board[point].cell_type {
223 board[point] = Cell::new(CellType::Safe(number + 1), CellState::Unknown);
224 }
225 }
226 }
227 }
228}
229
230pub struct MinsweeperGame<
231 S: Solver = Box<dyn Solver>,
232 OnWin: Fn() = Box<dyn Fn()>,
233 OnLose: Fn() = Box<dyn Fn()>,
234> {
235 board_size: BoardSize,
236 game_state: GameState,
237 player_game_state: GameState,
238 on_win: OnWin,
239 on_lose: OnLose,
240 first: bool,
241 solver: Option<S>
242}
243
244impl<S: Solver, OnWin: Fn(), OnLose: Fn()> MinsweeperGame<S, OnWin, OnLose> {
245
246 pub fn new(board_size: BoardSize, on_win: OnWin, on_lose: OnLose) -> Self {
247 Self {
248 board_size,
249 game_state: GameState::new(GameStatus::Never, Board::empty(board_size), 0),
250 player_game_state: GameState::new(GameStatus::Never, Board::empty(board_size), 0),
251 on_win,
252 on_lose,
253 first: true,
254 solver: None
255 }
256 }
257
258 fn internal_start(&mut self, solver: Option<S>) -> &GameState {
259 *self.gamestate_mut() = GameState::new(GameStatus::Playing, Board::empty(self.board_size),
260 usize::from(self.board_size.mines()).try_into().unwrap());
261
262 self.first = true;
263 self.solver = solver;
264
265 self.player_gamestate()
266 }
267
268 pub fn start_with_solver(&mut self, solver: S) -> &GameState {
269 self.internal_start(solver.into())
270 }
271}
272
273impl<S: Solver, OnWin: Fn(), OnLose: Fn()> InternalMinsweeper for MinsweeperGame<S, OnWin, OnLose> {
274 fn start(&mut self) -> &GameState {
275 self.internal_start(None)
276 }
277
278 fn on_win(&self) {
279 (self.on_win)()
280 }
281
282 fn on_lose(&self) {
283 (self.on_lose)()
284 }
285
286 fn player_gamestate(&self) -> &GameState {
287 if self.game_state.status == GameStatus::Playing {
288 &self.player_game_state
289 } else {
290 &self.game_state
291 }
292 }
293
294 fn gamestate_mut(&mut self) -> impl DerefMut<Target = GameState> {
295 GameStateHandle {
296 game_state: &mut self.game_state,
297 obfuscated_game_state: &mut self.player_game_state
298 }
299 }
300
301 fn reveal(&mut self, point: Point) -> Result<&GameState, &GameState> {
302 if check_interact(self, point).is_err() {
303 return Err(self.player_gamestate())
304 }
305
306 if self.first {
307 self.first = false;
308
309 if let Some(solver) = &self.solver {
310 *self.gamestate_mut() = generate_solvable_game(self.board_size, solver, point);
311 } else {
312 *self.gamestate_mut() = generate_game(self.board_size);
313 }
314 }
315
316
317 let success = self.internal_reveal(point);
318
319 if !success {
320 self.gamestate_mut().status = GameStatus::Lost;
321
322 self.on_lose();
323
324 return Ok(self.player_gamestate())
325 }
326
327 if self.gamestate_mut().board.has_won() {
328 self.gamestate_mut().status = GameStatus::Won;
329
330 self.on_win();
331
332 return Ok(self.player_gamestate())
333 }
334
335 Ok(self.player_gamestate())
336 }
337
338 fn set_flagged(&mut self, point: Point, flagged: bool) -> Result<&GameState, &GameState> {
339 if check_interact(self, point).is_err() || self.first {
340 return Err(self.player_gamestate())
341 }
342
343 let mut mewo = self.gamestate_mut();
344 let state = mewo.deref_mut();
345 let cell = &mut state.board[point];
346
347 if cell.cell_state == CellState::Revealed {
348 drop(mewo);
349 return Err(self.player_gamestate())
350 }
351
352
353 if flagged != (cell.cell_state == CellState::Flagged) {
354 if flagged { state.remaining_mines -= 1 } else { state.remaining_mines += 1 }
355 }
356
357 cell.cell_state = if flagged { CellState::Flagged } else { CellState::Unknown };
358
359 drop(mewo);
360 Ok(self.player_gamestate())
361 }
362}
363
364#[cfg(feature = "async")]
365pub mod nonblocking {
366 use crate::board::{BoardSize, Point};
367 use crate::minsweeper::{generate_game, generate_solvable_game_async, InternalMinsweeper, MinsweeperGame};
368 use crate::solver::Solver;
369 use crate::{check_interact, Cell, CellState, CellType, GameState, Minsweeper};
370 use tokio::sync::{Mutex, RwLock};
371
372 pub struct AsyncMinsweeperGame<S: Solver + Send + Sync, OnWin: Fn() + Send + Sync, OnLose: Fn() + Send + Sync> {
373 minsweeper_game: RwLock<MinsweeperGame<S, OnWin, OnLose>>,
374 generate_lock: Mutex<()>
375
376 }
377
378 impl<S: Solver + Send + Sync + Clone, OnWin: Fn() + Send + Sync, OnLose: Fn() + Send + Sync> AsyncMinsweeperGame<S, OnWin, OnLose> {
379
380 pub fn new(board_size: BoardSize, on_win: OnWin, on_lose: OnLose) -> Self {
381 Self {
382 minsweeper_game: MinsweeperGame::new(board_size, on_win, on_lose).into(),
383 generate_lock: Default::default(),
384 }
385 }
386
387 pub async fn start(&self) -> GameState {
388 drop(self.generate_lock.lock().await);
389 Minsweeper::start(&mut *self.minsweeper_game.write().await)
390 .clone()
391 }
392
393 pub async fn start_with_solver(&self, solver: S) -> GameState {
394 drop(self.generate_lock.lock().await);
395 self.minsweeper_game.write()
396 .await
397 .start_with_solver(solver)
398 .clone()
399 }
400
401 pub async fn gamestate(&self) -> GameState {
402 self.minsweeper_game.read()
403 .await
404 .player_gamestate()
405 .clone()
406 }
407
408 pub fn blocking_gamestate(&self) -> GameState {
409 self.minsweeper_game.blocking_read()
410 .player_gamestate()
411 .clone()
412 }
413
414
415 pub async fn reveal(&self, point: Point) -> Result<GameState, GameState> {
416 drop(self.generate_lock.lock().await);
417 let mut game = self.minsweeper_game.write().await;
418 if check_interact(&*game, point).is_err() {
419 return Err(game.player_gamestate().clone())
420 }
421
422 if game.first {
423 game.first = false;
424
425
426 let solver = game.solver.clone();
427 let size = game.board_size;
428 drop(game);
429
430 let generate_guard = self.generate_lock.lock();
431 let gamestate = if let Some(solver) = solver {
432 generate_solvable_game_async(size, &solver, point).await
433 } else {
434 generate_game(size)
435 };
436 *self.minsweeper_game.write().await.gamestate_mut() = gamestate;
437 drop(generate_guard);
438
439 game = self.minsweeper_game.write().await;
440 }
441
442 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 drop(self.generate_lock.lock().await);
451 Minsweeper::clear_around(&mut *self.minsweeper_game.write().await, point)
452 .cloned()
453 .map_err(Clone::clone)
454 }
455
456 pub async fn set_flagged(&self, point: Point, flagged: bool) -> Result<GameState, GameState> {
457 drop(self.generate_lock.lock().await);
458 Minsweeper::set_flagged(&mut *self.minsweeper_game.write().await, point, flagged)
459 .cloned()
460 .map_err(Clone::clone)
461 }
462
463 pub async fn toggle_flag(&self, point: Point) -> Result<GameState, GameState> {
464 drop(self.generate_lock.lock().await);
465 Minsweeper::toggle_flag(&mut *self.minsweeper_game.write().await, point)
466 .cloned()
467 .map_err(Clone::clone)
468 }
469
470 pub async fn left_click(&self, point: Point) -> Result<GameState, GameState> {
471 let game = self.minsweeper_game.read().await;
472 if check_interact(&*game, point).is_err() {
473 return Err(game.gamestate().clone())
474 }
475
476 let cell = game.gamestate().board[point];
477
478 match cell {
479 Cell { cell_type: CellType::Safe(_), cell_state: CellState::Revealed } => {
480 drop(game);
481 self.clear_around(point).await
482 },
483 Cell { cell_state: CellState::Unknown, .. } => {
484 drop(game);
485 self.reveal(point).await
486 },
487 _ => Err(game.gamestate().clone())
488 }
489 }
490
491 pub async fn right_click(&self, point: Point) -> Result<GameState, GameState> {
492 self.toggle_flag(point).await
493 }
494 }
495}
496
497pub fn generate_solvable_game(board_size: BoardSize, solver: &dyn Solver, point: Point) -> GameState {
498 loop {
499 let state = generate_game(board_size);
500
501 let mut game = SetMinsweeperGame::new(state.clone());
502 Minsweeper::reveal(&mut game, point)
503 .expect("should always be able to successfully reveal");
504
505 let result = solver.solve_game(&mut game);
506
507 if result == GameResult::Won {
508 return state;
509 }
510 }
511}
512
513pub async fn generate_solvable_game_async<S: Solver + Send + Sync>(board_size: BoardSize, solver: &S, point: Point) -> GameState {
514 loop {
515 let Some(state) = try_generate_solvable_game_async(board_size, solver, point).await else {
516 #[cfg(feature = "tokio")]
517 tokio::task::yield_now().await;
518 continue
519 };
520 return state
521 }
522}
523async fn try_generate_solvable_game_async<S: Solver + Send + Sync>(board_size: BoardSize, solver: &S, point: Point) -> Option<GameState> {
524 let state = generate_game(board_size);
525
526 let mut game = SetMinsweeperGame::new(state.clone());
527 Minsweeper::reveal(&mut game, point)
528 .expect("should always be able to successfully reveal");
529
530 let result = solver.solve_game(&mut game);
531
532 if result == GameResult::Won {
533 Some(state)
534 } else {
535 None
536 }
537}
538
539#[derive(Clone, Debug)]
540pub struct SetMinsweeperGame {
541 game_state: GameState,
542 player_game_state: GameState
543}
544
545impl SetMinsweeperGame {
546 pub fn new(game_state: GameState) -> Self {
547 Self { player_game_state: game_state.hide_mines(), game_state }
548 }
549}
550
551impl InternalMinsweeper for SetMinsweeperGame {
552 fn start(&mut self) -> &GameState {
553 unimplemented!()
554 }
555
556 fn on_win(&self) {
557
558 }
559
560 fn on_lose(&self) {
561
562 }
563
564 fn player_gamestate(&self) -> &GameState {
565 &self.player_game_state
566 }
567
568 fn gamestate_mut(&mut self) -> impl DerefMut<Target = GameState> {
569 GameStateHandle {
570 game_state: &mut self.game_state,
571 obfuscated_game_state: &mut self.player_game_state,
572 }
573 }
574}
575
576struct GameStateHandle<'a> {
577 game_state: &'a mut GameState,
578 obfuscated_game_state: &'a mut GameState
579}
580
581impl AsMut<GameState> for GameStateHandle<'_> {
582 fn as_mut(&mut self) -> &mut GameState {
583 self.game_state
584 }
585}
586
587impl Deref for GameStateHandle<'_> {
588 type Target = GameState;
589
590 fn deref(&self) -> &Self::Target {
591 self.game_state
592 }
593}
594
595impl DerefMut for GameStateHandle<'_> {
596 fn deref_mut(&mut self) -> &mut Self::Target {
597 self.game_state
598 }
599}
600
601impl Drop for GameStateHandle<'_> {
602 fn drop(&mut self) {
603 *self.obfuscated_game_state = self.game_state.hide_mines()
604 }
605}
606