minsweeper_rs/
minsweeper.rs1use 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 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