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 rand::Rng;
5use std::collections::HashSet;
6use std::ops::{Deref, DerefMut};
7use std::sync::Arc;
8
9trait InternalMinsweeper {
10
11    fn start(&mut self) -> &GameState;
12
13    fn on_win(&self);
14    fn on_lose(&self);
15
16    fn player_gamestate(&self) -> &GameState;
17    fn gamestate_mut(&mut self) -> impl DerefMut<Target = GameState>;
18
19    fn reveal(&mut self, point: Point) -> Result<&GameState, &GameState> {
20        if check_interact(self, point).is_err() {
21            return Err(self.player_gamestate())
22        }
23
24
25        let success = self.internal_reveal(point);
26
27        if !success {
28            self.gamestate_mut().status = GameStatus::Lost;
29
30            self.on_lose();
31
32            return Ok(self.player_gamestate())
33        }
34
35        if self.gamestate_mut().board.has_won() {
36            self.gamestate_mut().status = GameStatus::Won;
37
38            self.on_win();
39
40            return Ok(self.player_gamestate())
41        }
42
43        Ok(self.player_gamestate())
44
45    }
46
47    fn reveal_empty(board: &mut Board, point: Point) {
48        if !matches!(board[point], Cell { cell_type: CellType::EMPTY, cell_state: state } if state != CellState::Revealed) {
49            return
50        }
51
52        let empty_cell = Cell::new(CellType::EMPTY, CellState::Revealed);
53        board[point] = empty_cell;
54
55        let mut flood = HashSet::new();
56
57        flood.insert(point);
58
59        while !flood.is_empty() {
60            let point = *flood.iter().next().unwrap();
61            flood.remove(&point);
62
63            for point in board.size().neighbours(point) {
64                if let Cell { cell_type: CellType::Safe(number), cell_state: state } = board[point]
65                        && state != CellState::Revealed {
66                    board[point] = Cell::new(CellType::Safe(number), CellState::Revealed);
67
68                    if number == 0 {
69                        flood.insert(point);
70                    }
71                }
72            }
73        }
74
75    }
76
77    fn internal_reveal(&mut self, point: Point) -> bool {
78        let mut state = self.gamestate_mut();
79        // let state = state.as_mut();
80        let board = &mut state.board;
81        if board[point].cell_state != CellState::Unknown {
82            return true
83        }
84
85        match board[point].cell_type {
86            CellType::Safe(number) => {
87                if number == 0 {
88                    Self::reveal_empty(board, point)
89                } else {
90                    board[point] = Cell::new(CellType::Safe(number), CellState::Revealed)
91                }
92                true
93            }
94            CellType::Mine => {
95                board[point] = Cell::new(CellType::Mine, CellState::Revealed);
96                false
97            }
98            _ => unreachable!()
99        }
100    }
101
102    fn clear_around(&mut self, point: Point) -> Result<&GameState, &GameState> {
103        if check_interact(self, point).is_err() {
104            return Err(self.player_gamestate())
105        }
106
107        let Cell { cell_type: CellType::Safe(number), cell_state: CellState::Revealed } = self.player_gamestate().board[point] else {
108            return Err(self.player_gamestate())
109        };
110
111        let flags = self.count_flags(point);
112
113        if flags != number as usize {
114            return Err(self.player_gamestate())
115        }
116
117        let mut success = true;
118
119        for point in self.player_gamestate().board.size().neighbours(point) {
120            success &= self.internal_reveal(point);
121        }
122
123        if !success {
124            self.gamestate_mut().status = GameStatus::Lost;
125
126            self.on_lose();
127
128            return Ok(self.player_gamestate())
129        }
130
131        if self.gamestate_mut().board.has_won() {
132            self.gamestate_mut().status = GameStatus::Won;
133
134            self.on_win();
135
136            return Ok(self.player_gamestate())
137        }
138
139        Ok(self.player_gamestate())
140    }
141
142    fn set_flagged(&mut self, point: Point, flagged: bool) -> Result<&GameState, &GameState> {
143        if check_interact(self, point).is_err() {
144            return Err(self.player_gamestate())
145        }
146
147        let mut mewo = self.gamestate_mut();
148        let state = mewo.deref_mut();
149        let cell = &mut state.board[point];
150
151        if cell.cell_state == CellState::Revealed {
152            drop(mewo);
153            return Err(self.player_gamestate())
154        }
155
156
157        if flagged != (cell.cell_state == CellState::Flagged) {
158            if flagged { state.remaining_mines -= 1 } else { state.remaining_mines += 1 }
159        }
160
161        cell.cell_state = if flagged { CellState::Flagged } else { CellState::Unknown };
162
163        drop(mewo);
164        Ok(self.player_gamestate())
165    }
166
167    fn count_flags(&self, point: Point) -> usize {
168        self.player_gamestate().board.size().neighbours(point)
169                .filter(|e| self.player_gamestate().board[*e].cell_state == CellState::Flagged)
170                .count()
171    }
172}
173
174impl<T: InternalMinsweeper + ?Sized> Minsweeper for T {
175    fn start(&mut self) -> &GameState {
176        self.start()
177    }
178
179    fn gamestate(&self) -> &GameState {
180        self.player_gamestate()
181    }
182
183    fn reveal(&mut self, point: Point) -> Result<&GameState, &GameState> {
184        self.reveal(point)
185    }
186
187    fn clear_around(&mut self, point: Point) -> Result<&GameState, &GameState> {
188        self.clear_around(point)
189    }
190
191    fn set_flagged(&mut self, point: Point, flagged: bool) -> Result<&GameState, &GameState> {
192        self.set_flagged(point, flagged)
193    }
194}
195
196
197fn generate_game(board_size: BoardSize) -> GameState {
198    let mut board = Board::empty(board_size);
199
200    let mine = Cell::new(CellType::Mine, CellState::Unknown);
201    let mut rng = rand::rng();
202    let mut mines = 0usize;
203    while mines < board_size.mines().into() {
204        let point = (rng.random_range(0..board_size.width().into()),
205                     rng.random_range(0..board_size.height().into()));
206
207        if matches!(board[point].cell_type, CellType::Safe(_)) {
208            board[point] = mine;
209            mines += 1;
210        }
211    };
212
213    generate_nmbers(&mut board);
214
215    GameState::new(GameStatus::Playing, board, usize::from(board_size.mines()).try_into().unwrap())
216}
217
218fn generate_nmbers(board: &mut Board) {
219    let empty_unknown = Cell::new(CellType::EMPTY, CellState::Unknown);
220    for point in board.size().points() {
221        let cell = &mut board[point];
222
223        if matches!(cell.cell_type, CellType::Safe(_)) {
224            *cell = empty_unknown;
225        }
226    }
227    for point in board.size().points() {
228        if board[point].cell_type == CellType::Mine {
229            for point in board.size().neighbours(point) {
230                if let CellType::Safe(number) = board[point].cell_type {
231                    board[point] = Cell::new(CellType::Safe(number + 1), CellState::Unknown);
232                }
233            }
234        }
235    }
236}
237
238pub struct MinsweeperGame {
239    board_size: BoardSize,
240    game_state: GameState,
241    player_game_state: GameState,
242    on_win: Arc<dyn Fn()>,
243    on_lose: Arc<dyn Fn()>,
244    first: bool,
245    solver: Option<Box<dyn Solver>>
246}
247
248impl MinsweeperGame {
249
250    pub fn new(board_size: BoardSize, on_win: Arc<dyn Fn()>, on_lose: Arc<dyn Fn()>) -> Self {
251        Self {
252            board_size,
253            game_state: GameState::new(GameStatus::Never, Board::empty(board_size), 0),
254            player_game_state: GameState::new(GameStatus::Never, Board::empty(board_size), 0),
255            on_win,
256            on_lose,
257            first: true,
258            solver: None
259        }
260    }
261
262    fn internal_start(&mut self, solver: Option<Box<dyn Solver>>) -> &GameState {
263        *self.gamestate_mut() = GameState::new(GameStatus::Playing, Board::empty(self.board_size),
264                                         usize::from(self.board_size.mines()).try_into().unwrap());
265
266        self.first = true;
267        self.solver = solver;
268
269        self.player_gamestate()
270    }
271
272    pub fn start_with_solver(&mut self, solver: Box<dyn Solver>) -> &GameState {
273        self.internal_start(solver.into())
274    }
275}
276
277impl InternalMinsweeper for MinsweeperGame {
278    fn start(&mut self) -> &GameState {
279        self.internal_start(None)
280    }
281
282    fn on_win(&self) {
283        (self.on_win)()
284    }
285
286    fn on_lose(&self) {
287        (self.on_lose)()
288    }
289
290    fn player_gamestate(&self) -> &GameState {
291        if self.game_state.status == GameStatus::Playing {
292            &self.player_game_state
293        } else {
294            &self.game_state
295        }
296    }
297
298    fn gamestate_mut(&mut self) -> impl DerefMut<Target = GameState> {
299        GameStateHandle {
300            game_state: &mut self.game_state,
301            obfuscated_game_state: &mut self.player_game_state
302        }
303    }
304
305    fn reveal(&mut self, point: Point) -> Result<&GameState, &GameState> {
306        if check_interact(self, point).is_err() {
307            return Err(self.player_gamestate())
308        }
309
310        if self.first {
311            self.first = false;
312
313            if let Some(solver) = &self.solver {
314                loop {
315                    let state = generate_game(self.board_size);
316
317                    let mut game = SetMinsweeperGame::new(state.clone());
318                    Minsweeper::reveal(&mut game, point)
319                            .expect("should always be able to successfully reveal");
320
321                    let result = solver.solve_game(&mut game);
322
323                    if result == GameResult::Won {
324                        *self.gamestate_mut() = state;
325                        break;
326                    }
327                }
328            } else {
329                *self.gamestate_mut() = generate_game(self.board_size);
330            }
331        }
332
333
334        let success = self.internal_reveal(point);
335
336        if !success {
337            self.gamestate_mut().status = GameStatus::Lost;
338
339            self.on_lose();
340
341            return Ok(self.player_gamestate())
342        }
343
344        if self.gamestate_mut().board.has_won() {
345            self.gamestate_mut().status = GameStatus::Won;
346
347            self.on_win();
348
349            return Ok(self.player_gamestate())
350        }
351
352        Ok(self.player_gamestate())
353    }
354
355    fn set_flagged(&mut self, point: Point, flagged: bool) -> Result<&GameState, &GameState> {
356        if check_interact(self, point).is_err() || self.first {
357            return Err(self.player_gamestate())
358        }
359
360        let mut mewo = self.gamestate_mut();
361        let state = mewo.deref_mut();
362        let cell = &mut state.board[point];
363
364        if cell.cell_state == CellState::Revealed {
365            drop(mewo);
366            return Err(self.player_gamestate())
367        }
368
369
370        if flagged != (cell.cell_state == CellState::Flagged) {
371            if flagged { state.remaining_mines -= 1 } else { state.remaining_mines += 1 }
372        }
373
374        cell.cell_state = if flagged { CellState::Flagged } else { CellState::Unknown };
375
376        drop(mewo);
377        Ok(self.player_gamestate())
378    }
379}
380
381#[derive(Clone, Debug)]
382pub struct SetMinsweeperGame {
383    game_state: GameState,
384    player_game_state: GameState
385}
386
387impl SetMinsweeperGame {
388    pub fn new(game_state: GameState) -> Self {
389        Self { player_game_state: game_state.hide_mines(), game_state }
390    }
391}
392
393impl InternalMinsweeper for SetMinsweeperGame {
394    fn start(&mut self) -> &GameState {
395        unimplemented!()
396    }
397
398    fn on_win(&self) {
399
400    }
401
402    fn on_lose(&self) {
403
404    }
405
406    fn player_gamestate(&self) -> &GameState {
407        &self.player_game_state
408    }
409
410    fn gamestate_mut(&mut self) -> impl DerefMut<Target = GameState> {
411        GameStateHandle {
412            game_state: &mut self.game_state,
413            obfuscated_game_state: &mut self.player_game_state,
414        }
415    }
416}
417
418struct GameStateHandle<'a> {
419    game_state: &'a mut GameState,
420    obfuscated_game_state: &'a mut GameState
421}
422
423impl AsMut<GameState> for GameStateHandle<'_> {
424    fn as_mut(&mut self) -> &mut GameState {
425        self.game_state
426    }
427}
428
429impl Deref for GameStateHandle<'_> {
430    type Target = GameState;
431
432    fn deref(&self) -> &Self::Target {
433        self.game_state
434    }
435}
436
437impl DerefMut for GameStateHandle<'_> {
438    fn deref_mut(&mut self) -> &mut Self::Target {
439        self.game_state
440    }
441}
442
443impl Drop for GameStateHandle<'_> {
444    fn drop(&mut self) {
445        *self.obfuscated_game_state = self.game_state.hide_mines()
446    }
447}
448