1use crate::tile::{Direction, Tile, TileCollection};
2use serde_derive::Serialize;
3use std::collections::HashSet;
4use std::fmt;
5
6#[derive(Clone, PartialEq, Eq, Hash, Serialize)]
7pub struct RectangularBoard {
8 #[serde(skip_serializing)]
9 pub width: usize,
10
11 #[serde(skip_serializing)]
12 pub height: usize,
13
14 pub board: Vec<Vec<bool>>,
15
16 #[serde(skip_serializing)]
17 counts: Vec<Vec<usize>>,
18}
19
20impl RectangularBoard {
21 pub fn new(width: usize, height: usize) -> Self {
22 let mut counts = vec![vec![0; width]; height];
23
24 for row in counts.iter_mut() {
25 row[0] = 1;
26 row[width - 1] = 1;
27 }
28
29 for j in 0..width {
30 counts[0][j] = 1;
31 counts[height - 1][j] = 1;
32 }
33
34 RectangularBoard {
35 width,
36 height,
37 board: vec![vec![false; width]; height],
38 counts,
39 }
40 }
41
42 pub fn l_board(n: usize, scale: usize) -> Self {
48 let mut board = RectangularBoard::new(n * scale, 2 * scale);
49
50 for row in 0..scale {
51 for col in scale..(n * scale) {
52 board.board[row][col] = true;
53 }
54 }
55
56 board
57 }
58
59 pub fn t_board(n: usize, scale: usize) -> Self {
65 let mut board = RectangularBoard::new((2 * n + 1) * scale, 2 * scale);
66
67 for row in 0..scale {
68 for col in 0..(n * scale) {
69 board.board[row][col] = true;
70 }
71 for col in ((n + 1) * scale)..((2 * n + 1) * scale) {
72 board.board[row][col] = true;
73 }
74 }
75
76 board
77 }
78
79 fn mark(&mut self, p: Position) {
93 for xp in (p.x - 1)..=(p.x + 1) {
94 if xp == p.x {
95 continue;
96 }
97 if self.is_valid(Position::from((xp, p.y))) {
98 self.counts[xp as usize][p.y as usize] += 1;
99 }
100 }
101 for yp in (p.y - 1)..=(p.y + 1) {
102 if yp == p.y {
103 continue;
104 }
105 if self.is_valid(Position::from((p.x, yp))) {
106 self.counts[p.x as usize][yp as usize] += 1;
107 }
108 }
109
110 self.board[p.x as usize][p.y as usize] = true;
111 }
112
113 pub fn is_all_marked(&self) -> bool {
121 for row in self.board.iter() {
122 for col in row.iter() {
123 if !(*col) {
124 return false;
125 }
126 }
127 }
128 true
129 }
130
131 pub fn place_tile(&self, tile_collection: &TileCollection) -> Vec<RectangularBoard> {
132 let mut largest_count = None;
133 let mut largest_position = None;
134
135 for j in 0..self.width {
137 for i in 0..self.height {
138 if !self.board[i][j] {
139 let count = self.counts[i][j];
140
141 if !tile_collection.contains_single_tile() && count == 4 {
144 return Vec::new();
145 }
146
147 if largest_count.is_none() || self.counts[i][j] > largest_count.unwrap() {
149 largest_count = Some(self.counts[i][j]);
150 largest_position = Some((i, j));
151 }
152 }
153 }
154 }
155
156 let mut fitting_tiles = Vec::new();
158
159 if let Some((i, j)) = largest_position {
160 for tile in tile_collection.iter() {
161 for start_index in 0..=tile.directions.len() {
162 if let Some(tp) =
163 self.tile_fits_at_position(tile, Position::from((i, j)), start_index)
164 {
165 if !fitting_tiles.contains(&tp) {
168 fitting_tiles.push(tp);
169 }
170 }
171 }
172 }
173 }
174
175 fitting_tiles
177 .into_iter()
178 .map(|tp| {
179 let mut child_board = self.clone();
180 child_board.mark_tile_at_position(tp);
181 child_board
182 })
183 .collect()
184 }
185
186 fn is_marked(&self, p: Position) -> bool {
187 assert!(self.is_valid(p));
188
189 self.board[p.x as usize][p.y as usize]
190 }
191
192 fn is_valid(&self, p: Position) -> bool {
193 p.x >= 0 && (p.x as usize) < self.height && p.y >= 0 && (p.y as usize) < self.width
194 }
195
196 fn move_in_direction(&self, p: Position, direction: Direction) -> Position {
197 let mut row = p.x;
198 let mut col = p.y;
199
200 col += match direction {
201 Direction::Left => -1,
202 Direction::Right => 1,
203 Direction::UpLeft => -1,
204 Direction::UpRight => 1,
205 Direction::DownLeft => -1,
206 Direction::DownRight => 1,
207 _ => 0,
208 };
209 row += match direction {
210 Direction::Up => -1,
211 Direction::Down => 1,
212 Direction::UpLeft => -1,
213 Direction::UpRight => -1,
214 Direction::DownLeft => 1,
215 Direction::DownRight => 1,
216 _ => 0,
217 };
218
219 Position::new(row, col)
220 }
221
222 fn tile_fits_at_position(
231 &self,
232 tile: &Tile,
233 position: Position,
234 start_index: usize,
235 ) -> Option<TilePosition> {
236 assert!(start_index <= tile.directions.len());
238
239 let mut current_position = position;
240
241 let valid_and_unmarked = |p: Position| self.is_valid(p) && !self.is_marked(p);
242
243 if !valid_and_unmarked(current_position) {
244 return None;
245 }
246
247 let mut covered = HashSet::new();
248 covered.insert(current_position);
249
250 for i in (0..start_index).rev() {
252 current_position =
253 self.move_in_direction(current_position, tile.directions[i].opposite());
254
255 if !valid_and_unmarked(current_position) {
256 return None;
257 }
258 covered.insert(current_position);
259 }
260
261 let mut current_position = position;
262
263 for i in start_index..tile.directions.len() {
265 current_position = self.move_in_direction(current_position, tile.directions[i]);
266
267 if !valid_and_unmarked(current_position) {
268 return None;
269 }
270
271 covered.insert(current_position);
272 }
273
274 Some(TilePosition::new(
275 position,
276 tile.clone(),
277 start_index,
278 covered,
279 ))
280 }
281
282 fn mark_tile_at_position(&mut self, tp: TilePosition) {
283 for position in tp.covered {
284 self.mark(position);
285 }
286 }
287}
288
289impl fmt::Debug for RectangularBoard {
290 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
291 let mut os = Vec::with_capacity((1 + self.width) * self.height);
292
293 for i in 0..self.height {
294 for j in 0..self.width {
295 os.push(if self.board[i][j] { "x" } else { "*" });
296 }
297 os.push("\n");
298 }
299
300 write!(f, "{}", os.join(""))
301 }
302}
303
304#[derive(Copy, Clone, PartialEq, Eq, Hash)]
305struct Position {
306 x: isize,
307 y: isize,
308}
309
310impl From<(isize, isize)> for Position {
311 fn from(p: (isize, isize)) -> Self {
312 Position::new(p.0, p.1)
313 }
314}
315
316impl From<(usize, usize)> for Position {
317 fn from(p: (usize, usize)) -> Self {
318 Position::new(p.0 as isize, p.1 as isize)
319 }
320}
321
322impl Position {
323 pub fn new(x: isize, y: isize) -> Self {
324 Position { x, y }
325 }
326}
327
328#[derive(Eq, Clone)]
329struct TilePosition {
330 position: Position,
331 tile: Tile,
332 start_index: usize,
333 covered: HashSet<Position>,
334}
335
336impl TilePosition {
337 pub fn new(
338 position: Position,
339 tile: Tile,
340 start_index: usize,
341 covered: HashSet<Position>,
342 ) -> Self {
343 TilePosition {
344 covered,
345 position,
346 tile,
347 start_index,
348 }
349 }
350}
351
352impl PartialEq for TilePosition {
353 fn eq(&self, other: &TilePosition) -> bool {
354 self.covered == other.covered
355 }
356}