1use core::ops::{Index, IndexMut};
2
3use embedded_graphics::{
4 Drawable,
5 image::{Image, ImageDrawable},
6 pixelcolor::BinaryColor,
7 prelude::{DrawTarget, Point, Primitive, Size},
8 primitives::{PrimitiveStyle, Rectangle},
9 transform::Transform,
10};
11
12#[derive(Copy, Clone, PartialEq)]
13pub enum Direction {
14 Up,
15 Down,
16 Left,
17 Right,
18}
19
20impl Direction {
21 pub fn opposite(&self) -> Direction {
22 match self {
23 Direction::Up => Direction::Down,
24 Direction::Down => Direction::Up,
25 Direction::Left => Direction::Right,
26 Direction::Right => Direction::Left,
27 }
28 }
29}
30
31#[derive(Copy, Clone)]
32pub struct Row<T, const COLS: usize>(pub [Option<T>; COLS]);
33
34impl<T, const COLS: usize> Default for Row<T, COLS>
35where
36 T: Copy + ImageDrawable<Color = BinaryColor>,
37{
38 fn default() -> Self {
39 Self([const { None }; COLS])
40 }
41}
42
43pub struct Grid<T, const ROWS: usize, const COLS: usize> {
44 rows: [Row<T, COLS>; ROWS],
45 rng: fastrand::Rng,
46 translation: Point,
47}
48
49impl<T, const ROWS: usize, const COLS: usize> Default for Grid<T, ROWS, COLS>
50where
51 T: Copy + ImageDrawable<Color = BinaryColor>,
52{
53 fn default() -> Self {
54 Self::new(0)
55 }
56}
57
58impl<T, const ROWS: usize, const COLS: usize> Grid<T, ROWS, COLS>
59where
60 T: Copy + ImageDrawable<Color = BinaryColor>,
61{
62 pub fn new(seed: u64) -> Self {
63 Grid {
64 rows: [Row::default(); ROWS],
65 rng: fastrand::Rng::with_seed(seed),
66 translation: Point::default(),
67 }
68 }
69
70 pub fn neighbour_index(
71 &self,
72 location: (usize, usize),
73 direction: Direction,
74 ) -> (usize, usize) {
75 match direction {
76 Direction::Up => {
77 if location.0 == 0 {
78 (self.rows.len() - 1, location.1)
79 } else {
80 (location.0 - 1, location.1)
81 }
82 }
83 Direction::Down => {
84 if location.0 == (self.rows.len() - 1) {
85 (0, location.1)
86 } else {
87 (location.0 + 1, location.1)
88 }
89 }
90 Direction::Right => {
91 if location.1 == (self.rows[0].0.len() - 1) {
92 (location.0, 0)
93 } else {
94 (location.0, location.1 + 1)
95 }
96 }
97 Direction::Left => {
98 if location.1 == 0 {
99 (location.0, self.rows[0].0.len() - 1)
100 } else {
101 (location.0, location.1 - 1)
102 }
103 }
104 }
105 }
106
107 fn random(&mut self) -> Option<(usize, usize)> {
108 let ys = 0..ROWS;
109 let xs = 0..COLS;
110 let cross = ys.flat_map(|y| xs.clone().map(move |x| (x, y)));
111 let empty_cells = cross.filter(|(x, y)| self.rows[*y].0[*x].is_none());
112
113 let count = 0..(empty_cells.clone().count());
114
115 let i = self.rng.usize(count);
116 empty_cells
117 .enumerate()
118 .find(|(x, _)| *x == i)
119 .map(|(_, x)| x)
120 }
121
122 pub fn place_randomly(&mut self, cell: T) {
123 if let Some((column_index, row_index)) = self.random() {
124 self.rows[row_index].0[column_index] = Some(cell);
125 }
126 }
127}
128
129impl<T, const ROWS: usize, const COLS: usize> Index<(usize, usize)> for Grid<T, ROWS, COLS>
130where
131 T: Copy + ImageDrawable<Color = BinaryColor>,
132{
133 type Output = Option<T>;
134
135 fn index(&self, (row_index, column_index): (usize, usize)) -> &Self::Output {
136 &self.rows[row_index].0[column_index]
137 }
138}
139
140impl<T, const ROWS: usize, const COLS: usize> IndexMut<(usize, usize)> for Grid<T, ROWS, COLS>
141where
142 T: Copy + ImageDrawable<Color = BinaryColor>,
143{
144 fn index_mut(&mut self, (row_index, column_index): (usize, usize)) -> &mut Self::Output {
145 &mut self.rows[row_index].0[column_index]
146 }
147}
148
149impl<T, const ROWS: usize, const COLS: usize> Drawable for Grid<T, ROWS, COLS>
150where
151 T: Copy + ImageDrawable<Color = BinaryColor>,
152{
153 type Color = BinaryColor;
154 type Output = ();
155
156 fn draw<D>(&self, target: &mut D) -> Result<Self::Output, <D as DrawTarget>::Error>
157 where
158 D: DrawTarget<Color = Self::Color>,
159 {
160 for (row_index, row) in self.rows.iter().enumerate() {
161 for (column_index, cell) in row.0.iter().enumerate() {
162 match cell {
163 Some(drawable) => {
164 let _ = Image::new(
165 drawable,
166 Point::new(
167 <usize as TryInto<i32>>::try_into(column_index).unwrap() * 4
168 + self.translation.x,
169 <usize as TryInto<i32>>::try_into(row_index).unwrap() * 4
170 + self.translation.y,
171 ),
172 )
173 .draw(target);
174 }
175 None => {
176 let _ = Rectangle::new(
177 Point::new(
178 <usize as TryInto<i32>>::try_into(column_index).unwrap() * 4
179 + self.translation.x,
180 <usize as TryInto<i32>>::try_into(row_index).unwrap() * 4
181 + self.translation.y,
182 ),
183 Size::new(4, 4),
184 )
185 .into_styled(PrimitiveStyle::with_fill(BinaryColor::On))
186 .draw(target);
187 }
188 }
189 }
190 }
191
192 Ok(())
193 }
194}
195
196impl<T, const ROWS: usize, const COLS: usize> Transform for Grid<T, ROWS, COLS>
197where
198 T: Copy + ImageDrawable<Color = BinaryColor>,
199{
200 fn translate(&self, by: Point) -> Self {
201 Grid {
202 rows: self.rows,
204 rng: self.rng.clone(),
205 translation: by,
206 }
207 }
208
209 fn translate_mut(&mut self, by: Point) -> &mut Self {
210 self.translation = by;
211 self
212 }
213}
214
215#[cfg(test)]
216mod test {
217 use super::*;
218
219 #[derive(Debug, PartialEq, Copy, Clone)]
220 enum Cell {
221 Nought,
222 Cross,
223 }
224
225 use embedded_graphics::{
226 geometry::{OriginDimensions, Size},
227 image::ImageRaw,
228 mock_display::MockDisplay,
229 pixelcolor::BinaryColor,
230 prelude::*,
231 };
232
233 #[rustfmt::skip]
234 const DATA: &[u8] = &[
235 0b1001_0110,
236 0b0110_1001,
237 0b0110_1001,
238 0b1001_0110,
239 ];
240
241 impl ImageDrawable for Cell {
242 type Color = BinaryColor;
243
244 fn draw<
245 D: embedded_graphics::draw_target::DrawTarget<
246 Color = <Self as embedded_graphics::image::ImageDrawable>::Color,
247 >,
248 >(
249 &self,
250 display: &mut D,
251 ) -> Result<(), <D as embedded_graphics::draw_target::DrawTarget>::Error> {
252 let raw: ImageRaw<BinaryColor> = ImageRaw::new(DATA, 8);
253 match self {
254 Cell::Nought => {
255 let _ = raw
256 .sub_image(&Rectangle::new(Point::new(0, 0), Size::new(4, 4)))
257 .draw(display);
258 }
259 Cell::Cross => {
260 let _ = raw
261 .sub_image(&Rectangle::new(Point::new(4, 0), Size::new(4, 4)))
262 .draw(display);
263 }
264 }
265 Ok(())
266 }
267
268 fn draw_sub_image<
269 D: embedded_graphics::draw_target::DrawTarget<
270 Color = <Self as embedded_graphics::image::ImageDrawable>::Color,
271 >,
272 >(
273 &self,
274 _: &mut D,
275 _: &Rectangle,
276 ) -> Result<(), <D as embedded_graphics::draw_target::DrawTarget>::Error> {
277 Ok(())
278 }
279 }
280
281 impl OriginDimensions for Cell {
282 fn size(&self) -> Size {
283 Size::new(4, 4)
284 }
285 }
286
287 #[test]
288 fn test_draw() {
289 let mut grid: crate::grid::Grid<Cell, 3, 3> = Grid::new(0);
290 grid[(0, 0)] = Some(Cell::Cross);
291 grid[(0, 1)] = Some(Cell::Nought);
292
293 let mut display = MockDisplay::new();
294 let _ = grid.draw(&mut display);
295
296 display.assert_pattern(&[
297 ".##.#..#####",
298 "#..#.##.####",
299 "#..#.##.####",
300 ".##.#..#####",
301 "############",
302 "############",
303 "############",
304 "############",
305 "############",
306 "############",
307 "############",
308 "############",
309 ]);
310 }
311
312 #[test]
313 fn test_neighbour_index() {
314 let grid: crate::grid::Grid<Cell, 9, 20> = Grid::new(0);
315
316 assert_eq!(grid.neighbour_index((4, 10), Direction::Up), (3, 10),);
319 assert_eq!(grid.neighbour_index((4, 10), Direction::Left), (4, 9),);
320 assert_eq!(grid.neighbour_index((4, 10), Direction::Down), (5, 10),);
321 assert_eq!(grid.neighbour_index((4, 10), Direction::Right), (4, 11),);
322
323 assert_eq!(grid.neighbour_index((4, 0), Direction::Up), (3, 0),);
325 assert_eq!(grid.neighbour_index((4, 0), Direction::Left), (4, 19),);
326 assert_eq!(grid.neighbour_index((4, 0), Direction::Down), (5, 0),);
327 assert_eq!(grid.neighbour_index((4, 0), Direction::Right), (4, 1),);
328
329 assert_eq!(grid.neighbour_index((4, 19), Direction::Up), (3, 19),);
331 assert_eq!(grid.neighbour_index((4, 19), Direction::Left), (4, 18),);
332 assert_eq!(grid.neighbour_index((4, 19), Direction::Down), (5, 19),);
333 assert_eq!(grid.neighbour_index((4, 19), Direction::Right), (4, 0),);
334
335 assert_eq!(grid.neighbour_index((0, 10), Direction::Up), (8, 10),);
337 assert_eq!(grid.neighbour_index((0, 10), Direction::Left), (0, 9),);
338 assert_eq!(grid.neighbour_index((0, 10), Direction::Down), (1, 10),);
339 assert_eq!(grid.neighbour_index((0, 10), Direction::Right), (0, 11),);
340
341 assert_eq!(grid.neighbour_index((8, 10), Direction::Up), (7, 10),);
343 assert_eq!(grid.neighbour_index((8, 10), Direction::Left), (8, 9),);
344 assert_eq!(grid.neighbour_index((8, 10), Direction::Down), (0, 10),);
345 assert_eq!(grid.neighbour_index((8, 10), Direction::Right), (8, 11),);
346 }
347
348 #[test]
349 fn test_small_neighbour_index() {
350 let grid: crate::grid::Grid<Cell, 3, 3> = Grid::new(0);
351
352 assert_eq!(grid.neighbour_index((1, 1), Direction::Up), (0, 1),);
355 assert_eq!(grid.neighbour_index((1, 1), Direction::Left), (1, 0),);
356 assert_eq!(grid.neighbour_index((1, 1), Direction::Down), (2, 1),);
357 assert_eq!(grid.neighbour_index((1, 1), Direction::Right), (1, 2),);
358
359 assert_eq!(grid.neighbour_index((1, 0), Direction::Up), (0, 0),);
361 assert_eq!(grid.neighbour_index((1, 0), Direction::Left), (1, 2),);
362 assert_eq!(grid.neighbour_index((1, 0), Direction::Down), (2, 0),);
363 assert_eq!(grid.neighbour_index((1, 0), Direction::Right), (1, 1),);
364
365 assert_eq!(grid.neighbour_index((1, 2), Direction::Up), (0, 2),);
367 assert_eq!(grid.neighbour_index((1, 2), Direction::Left), (1, 1),);
368 assert_eq!(grid.neighbour_index((1, 2), Direction::Down), (2, 2),);
369 assert_eq!(grid.neighbour_index((1, 2), Direction::Right), (1, 0),);
370
371 assert_eq!(grid.neighbour_index((0, 1), Direction::Up), (2, 1),);
373 assert_eq!(grid.neighbour_index((0, 1), Direction::Left), (0, 0),);
374 assert_eq!(grid.neighbour_index((0, 1), Direction::Down), (1, 1),);
375 assert_eq!(grid.neighbour_index((0, 1), Direction::Right), (0, 2),);
376
377 assert_eq!(grid.neighbour_index((2, 1), Direction::Up), (1, 1),);
379 assert_eq!(grid.neighbour_index((2, 1), Direction::Left), (2, 0),);
380 assert_eq!(grid.neighbour_index((2, 1), Direction::Down), (0, 1),);
381 assert_eq!(grid.neighbour_index((2, 1), Direction::Right), (2, 2),);
382 }
383
384 #[test]
385 fn test_place_randomly() {
386 let mut grid: crate::grid::Grid<Cell, 3, 3> = Grid::new(0);
387 grid.place_randomly(Cell::Cross);
388 assert_eq!(grid[(0, 0)], None);
389 assert_eq!(grid[(0, 1)], None);
390 assert_eq!(grid[(0, 2)], None);
391 assert_eq!(grid[(1, 0)], None);
392 assert_eq!(grid[(1, 1)], None);
393 assert_eq!(grid[(2, 2)], None);
394 assert_eq!(grid[(2, 0)], None);
395 assert_eq!(grid[(2, 1)], None);
396 assert_eq!(grid[(1, 2)], Some(Cell::Cross));
397 }
398
399 #[test]
400 fn test_place_randomly_other_seed() {
401 let mut grid: crate::grid::Grid<Cell, 3, 3> = Grid::new(2);
402 grid.place_randomly(Cell::Cross);
403 assert_eq!(grid[(0, 0)], None);
404 assert_eq!(grid[(0, 1)], Some(Cell::Cross));
405 assert_eq!(grid[(0, 2)], None);
406 assert_eq!(grid[(1, 0)], None);
407 assert_eq!(grid[(1, 1)], None);
408 assert_eq!(grid[(2, 2)], None);
409 assert_eq!(grid[(2, 0)], None);
410 assert_eq!(grid[(2, 1)], None);
411 assert_eq!(grid[(1, 2)], None);
412 }
413}