minsweeper_rs/
board.rs

1use crate::{Cell, CellState, CellType};
2use std::error::Error;
3use std::fmt::{Display, Formatter};
4use std::iter::Flatten;
5use std::num::NonZeroUsize;
6use std::ops::{Index, IndexMut};
7use std::vec::IntoIter;
8
9#[derive(Clone, Debug)]
10pub struct Board {
11    grid: Vec<Vec<Cell>>,
12    size: BoardSize
13}
14
15pub type Point = (usize, usize);
16
17impl Board {
18
19    // pub(crate) const fn zero(board_size: BoardSize) -> Self {
20    //     Self {
21    //         grid: vec![],
22    //         size: board_size
23    //     }
24    // }
25
26    pub fn new(board_size: BoardSize, cell: Cell) -> Self {
27        Self {
28            grid: vec![vec![cell; board_size.height().into()]; board_size.width().into()],
29            size: board_size
30        }
31    }
32
33    pub fn empty(board_size: BoardSize) -> Self {
34        Self::new(board_size, Cell::EMPTY)
35    }
36
37    pub fn size(&self) -> BoardSize {
38        self.size
39    }
40
41    pub(crate) fn has_won(&self) -> bool {
42        !self.iter()
43                .any(|cell| match cell {
44                    Cell { cell_type: CellType::Mine, cell_state: CellState::Revealed } => true,
45                    Cell { cell_type: CellType::Safe(_), cell_state: state } if *state != CellState::Revealed => true,
46                    _ => false
47                })
48    }
49
50    pub(crate) fn hide_mines(&self) -> Self {
51        let mut board = self.clone();
52
53        board.grid = board.grid.into_iter()
54                .map(|e| e.into_iter()
55                        .map(|mut cell| {
56                            if cell.cell_state != CellState::Revealed {
57                                cell.cell_type = CellType::Unknown
58                            }
59                            cell
60                        })
61                        .collect())
62                .collect();
63
64        board
65    }
66
67    pub fn iter(&self) -> impl Iterator<Item = &Cell> {
68        self.into_iter()
69    }
70
71    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Cell> {
72        self.into_iter()
73    }
74}
75
76impl Index<Point> for Board {
77    type Output = Cell;
78
79    fn index(&self, index: Point) -> &Self::Output {
80        &self.grid[index.0][index.1]
81    }
82}
83
84impl IndexMut<Point> for Board {
85    fn index_mut(&mut self, index: Point) -> &mut Self::Output {
86        &mut self.grid[index.0][index.1]
87    }
88}
89
90impl IntoIterator for Board {
91    type Item = Cell;
92    type IntoIter = Flatten<IntoIter<Vec<Cell>>>;
93
94    fn into_iter(self) -> Self::IntoIter {
95        self.grid
96                .into_iter()
97                .flatten()
98    }
99}
100impl<'a> IntoIterator for &'a Board {
101    type Item = &'a Cell;
102    type IntoIter = Flatten<std::slice::Iter<'a, Vec<Cell>>>;
103
104    fn into_iter(self) -> Self::IntoIter {
105        self.grid
106                .iter()
107                .flatten()
108    }
109}
110
111impl<'a> IntoIterator for &'a mut Board {
112    type Item = &'a mut Cell;
113    type IntoIter = Flatten<std::slice::IterMut<'a, Vec<Cell>>>;
114
115    fn into_iter(self) -> Self::IntoIter {
116        self.grid
117                .iter_mut()
118                .flatten()
119    }
120}
121
122impl Display for Board {
123    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
124        for y in 0..self.size.height.into() {
125            for x in 0..self.size.width.into() {
126                write!(f, "{}", self[(x, y)])?;
127            }
128            writeln!(f)?;
129        }
130
131        Ok(())
132    }
133}
134
135#[derive(Debug)]
136pub enum BoardSizeError {
137    InvalidSize {
138        width: usize,
139        height: usize
140    },
141    TooManyMines {
142        mines: usize,
143        max_mines: usize
144    },
145    TooFewMines
146}
147
148impl Display for BoardSizeError {
149    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
150        match self {
151            BoardSizeError::InvalidSize { width, height } =>
152                write!(f, "board size cannot be {} by {}", width, height),
153            BoardSizeError::TooManyMines { mines, max_mines } =>
154                write!(f, "board cannot have {} mines (max: {})", mines, max_mines),
155            BoardSizeError::TooFewMines =>
156                write!(f, "board cannot have 0 mines")
157        }
158    }
159}
160
161impl Error for BoardSizeError {}
162
163#[derive(Copy, Clone, Debug, Eq, PartialEq)]
164pub struct BoardSize {
165    width: NonZeroUsize,
166    height: NonZeroUsize,
167    mines: NonZeroUsize
168}
169
170impl BoardSize {
171    pub fn new(width: usize, height: usize, mines: usize) -> Result<Self, BoardSizeError> {
172
173        let w = NonZeroUsize::new(width)
174                .ok_or(BoardSizeError::InvalidSize { width, height })?;
175        let h = NonZeroUsize::new(height)
176                .ok_or(BoardSizeError::InvalidSize { width, height })?;
177        let m = NonZeroUsize::new(mines)
178                .ok_or(BoardSizeError::TooFewMines)?;
179
180        if mines >= width * height {
181            return Err(BoardSizeError::TooManyMines {
182                mines,
183                max_mines: width * height
184            })
185        }
186
187        Ok(Self {
188            width: w,
189            height: h,
190            mines: m
191        })
192    }
193
194    pub fn width(&self) -> NonZeroUsize {
195        self.width
196    }
197
198    pub fn height(&self) -> NonZeroUsize {
199        self.height
200    }
201
202    pub fn mines(&self) -> NonZeroUsize {
203        self.mines
204    }
205
206    pub fn neighbours(&self, point: Point) -> impl Iterator<Item = Point> {
207        let mut neighbours = vec![];
208
209        for y in point.1.saturating_sub(1)..=usize::min(usize::from(self.height()) - 1, point.1.saturating_add(1)) {
210            for x in point.0.saturating_sub(1)..=usize::min(usize::from(self.width()) - 1, point.0.saturating_add(1)) {
211                if (x, y) != point {
212                    neighbours.push((x, y))
213                }
214            }
215        }
216
217        neighbours.into_iter()
218    }
219
220    pub fn points(&self) -> impl Iterator<Item = Point> {
221        (0..self.height.into())
222                .flat_map(|y| (0..self.width.into())
223                        .map(move |x| (x, y)))
224    }
225}
226
227#[derive(Copy, Clone, Debug)]
228pub enum ConventionalSize {
229    Beginner,
230    Intermediate,
231    Expert
232}
233
234impl ConventionalSize {
235    pub fn size(self) -> BoardSize {
236        match self {
237            ConventionalSize::Beginner => BoardSize::new(9, 9, 10).unwrap(),
238            ConventionalSize::Intermediate => BoardSize::new(16, 16, 40).unwrap(),
239            ConventionalSize::Expert => BoardSize::new(30, 16, 99).unwrap()
240        }
241    }
242}