minsweeper_rs/
minsweeper.rs

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};
6use std::sync::Arc;
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 state = state.as_mut();
79        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 mines = 0usize;
201    while mines < board_size.mines().into() {
202        let point = (fastrand::usize(0..board_size.width().into()),
203                     fastrand::usize(0..board_size.height().into()));
204
205        if matches!(board[point].cell_type, CellType::Safe(_)) {
206            board[point] = mine;
207            mines += 1;
208        }
209    };
210
211    generate_nmbers(&mut board);
212
213    GameState::new(GameStatus::Playing, board, usize::from(board_size.mines()).try_into().unwrap())
214}
215
216fn generate_nmbers(board: &mut Board) {
217    let empty_unknown = Cell::new(CellType::EMPTY, CellState::Unknown);
218    for point in board.size().points() {
219        let cell = &mut board[point];
220
221        if matches!(cell.cell_type, CellType::Safe(_)) {
222            *cell = empty_unknown;
223        }
224    }
225    for point in board.size().points() {
226        if board[point].cell_type == CellType::Mine {
227            for point in board.size().neighbours(point) {
228                if let CellType::Safe(number) = board[point].cell_type {
229                    board[point] = Cell::new(CellType::Safe(number + 1), CellState::Unknown);
230                }
231            }
232        }
233    }
234}
235
236pub struct MinsweeperGame<
237    S: Solver = Box<dyn Solver>,
238    OnWin: Fn() = Box<dyn Fn()>,
239    OnLose: Fn() = Box<dyn Fn()>,
240> {
241    board_size: BoardSize,
242    game_state: GameState,
243    player_game_state: GameState,
244    on_win: OnWin,
245    on_lose: OnLose,
246    first: bool,
247    solver: Option<S>
248}
249
250impl<S: Solver, OnWin: Fn(), OnLose: Fn()> MinsweeperGame<S, OnWin, OnLose> {
251
252    pub fn new(board_size: BoardSize, on_win: OnWin, on_lose: OnLose) -> Self {
253        Self {
254            board_size,
255            game_state: GameState::new(GameStatus::Never, Board::empty(board_size), 0),
256            player_game_state: GameState::new(GameStatus::Never, Board::empty(board_size), 0),
257            on_win,
258            on_lose,
259            first: true,
260            solver: None
261        }
262    }
263
264    fn internal_start(&mut self, solver: Option<S>) -> &GameState {
265        *self.gamestate_mut() = GameState::new(GameStatus::Playing, Board::empty(self.board_size),
266                                         usize::from(self.board_size.mines()).try_into().unwrap());
267
268        self.first = true;
269        self.solver = solver;
270
271        self.player_gamestate()
272    }
273
274    pub fn start_with_solver(&mut self, solver: S) -> &GameState {
275        self.internal_start(solver.into())
276    }
277}
278
279impl<S: Solver, OnWin: Fn(), OnLose: Fn()> InternalMinsweeper for MinsweeperGame<S, OnWin, OnLose> {
280    fn start(&mut self) -> &GameState {
281        self.internal_start(None)
282    }
283
284    fn on_win(&self) {
285        (self.on_win)()
286    }
287
288    fn on_lose(&self) {
289        (self.on_lose)()
290    }
291
292    fn player_gamestate(&self) -> &GameState {
293        if self.game_state.status == GameStatus::Playing {
294            &self.player_game_state
295        } else {
296            &self.game_state
297        }
298    }
299
300    fn gamestate_mut(&mut self) -> impl DerefMut<Target = GameState> {
301        GameStateHandle {
302            game_state: &mut self.game_state,
303            obfuscated_game_state: &mut self.player_game_state
304        }
305    }
306
307    fn reveal(&mut self, point: Point) -> Result<&GameState, &GameState> {
308        if check_interact(self, point).is_err() {
309            return Err(self.player_gamestate())
310        }
311
312        if self.first {
313            self.first = false;
314
315            if let Some(solver) = &self.solver {
316                *self.gamestate_mut() = generate_solvable_game(self.board_size, solver, point);
317            } else {
318                *self.gamestate_mut() = generate_game(self.board_size);
319            }
320        }
321
322
323        let success = self.internal_reveal(point);
324
325        if !success {
326            self.gamestate_mut().status = GameStatus::Lost;
327
328            self.on_lose();
329
330            return Ok(self.player_gamestate())
331        }
332
333        if self.gamestate_mut().board.has_won() {
334            self.gamestate_mut().status = GameStatus::Won;
335
336            self.on_win();
337
338            return Ok(self.player_gamestate())
339        }
340
341        Ok(self.player_gamestate())
342    }
343
344    fn set_flagged(&mut self, point: Point, flagged: bool) -> Result<&GameState, &GameState> {
345        if check_interact(self, point).is_err() || self.first {
346            return Err(self.player_gamestate())
347        }
348
349        let mut mewo = self.gamestate_mut();
350        let state = mewo.deref_mut();
351        let cell = &mut state.board[point];
352
353        if cell.cell_state == CellState::Revealed {
354            drop(mewo);
355            return Err(self.player_gamestate())
356        }
357
358
359        if flagged != (cell.cell_state == CellState::Flagged) {
360            if flagged { state.remaining_mines -= 1 } else { state.remaining_mines += 1 }
361        }
362
363        cell.cell_state = if flagged { CellState::Flagged } else { CellState::Unknown };
364
365        drop(mewo);
366        Ok(self.player_gamestate())
367    }
368}
369
370pub fn generate_solvable_game(board_size: BoardSize, solver: &dyn Solver, point: Point) -> GameState {
371    loop {
372        let state = generate_game(board_size);
373
374        let mut game = SetMinsweeperGame::new(state.clone());
375        Minsweeper::reveal(&mut game, point)
376                .expect("should always be able to successfully reveal");
377
378        let result = solver.solve_game(&mut game);
379
380        if result == GameResult::Won {
381            return state;
382        }
383    }
384}
385
386#[derive(Clone, Debug)]
387pub struct SetMinsweeperGame {
388    game_state: GameState,
389    player_game_state: GameState
390}
391
392impl SetMinsweeperGame {
393    pub fn new(game_state: GameState) -> Self {
394        Self { player_game_state: game_state.hide_mines(), game_state }
395    }
396}
397
398impl InternalMinsweeper for SetMinsweeperGame {
399    fn start(&mut self) -> &GameState {
400        unimplemented!()
401    }
402
403    fn on_win(&self) {
404
405    }
406
407    fn on_lose(&self) {
408
409    }
410
411    fn player_gamestate(&self) -> &GameState {
412        &self.player_game_state
413    }
414
415    fn gamestate_mut(&mut self) -> impl DerefMut<Target = GameState> {
416        GameStateHandle {
417            game_state: &mut self.game_state,
418            obfuscated_game_state: &mut self.player_game_state,
419        }
420    }
421}
422
423struct GameStateHandle<'a> {
424    game_state: &'a mut GameState,
425    obfuscated_game_state: &'a mut GameState
426}
427
428impl AsMut<GameState> for GameStateHandle<'_> {
429    fn as_mut(&mut self) -> &mut GameState {
430        self.game_state
431    }
432}
433
434impl Deref for GameStateHandle<'_> {
435    type Target = GameState;
436
437    fn deref(&self) -> &Self::Target {
438        self.game_state
439    }
440}
441
442impl DerefMut for GameStateHandle<'_> {
443    fn deref_mut(&mut self) -> &mut Self::Target {
444        self.game_state
445    }
446}
447
448impl Drop for GameStateHandle<'_> {
449    fn drop(&mut self) {
450        *self.obfuscated_game_state = self.game_state.hide_mines()
451    }
452}
453