shared/
grid.rs

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            // TODO: rows should not be copy
203            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        // indices are row, column
317        // middle-ish
318        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        // left edge
324        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        // right edge
330        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        // top edge
336        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        // bottom edge
342        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        // indices are row, column
353        // middle-ish
354        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        // left edge
360        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        // right edge
366        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        // top edge
372        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        // bottom edge
378        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}