Skip to main content

aoc_core/
grid.rs

1use std::ops::{Index, IndexMut};
2
3use grid::Grid;
4use itertools::Itertools;
5
6use crate::pos::{Pos, PosGet};
7
8/// Creates a new `u8` grid from a string slice.
9#[inline]
10#[must_use]
11pub fn bytes_grid(input: &str) -> Grid<u8> {
12    Grid::from(
13        input
14            .lines()
15            .map(|line| line.bytes().collect())
16            .collect_vec(),
17    )
18}
19
20// ------------------------------------------------------------------------------------------------
21// Grid trait implementations
22
23impl<V> PosGet<V> for Grid<V> {
24    #[inline]
25    fn pos_get(&self, pos: Pos) -> Option<&V> {
26        self.get(pos.x, pos.y)
27    }
28
29    #[inline]
30    fn pos_get_mut(&mut self, pos: Pos) -> Option<&mut V> {
31        self.get_mut(pos.x, pos.y)
32    }
33}
34
35impl<V> Index<Pos> for Grid<V> {
36    type Output = V;
37
38    fn index(&self, index: Pos) -> &Self::Output {
39        #[allow(clippy::cast_sign_loss)]
40        &self[(index.x as usize, index.y as usize)]
41    }
42}
43
44impl<V> IndexMut<Pos> for Grid<V> {
45    fn index_mut(&mut self, index: Pos) -> &mut Self::Output {
46        #[allow(clippy::cast_sign_loss)]
47        &mut self[(index.x as usize, index.y as usize)]
48    }
49}
50
51// ------------------------------------------------------------------------------------------------
52// GridMask
53
54/// Boolean mask for grid-like data.
55#[derive(Debug)]
56pub struct GridMask {
57    /// Underlying boolean array.
58    mask: Vec<bool>,
59
60    /// Columns number.
61    cols: usize,
62}
63
64impl GridMask {
65    /// Creates a new all-false grid mask with the specified `rows` and `cols`.
66    #[must_use]
67    pub fn new((rows, cols): (usize, usize)) -> Self {
68        Self {
69            mask: vec![false; rows * cols],
70            cols,
71        }
72    }
73
74    /// Sets the specified `pos` to `false`.
75    ///
76    /// Returns `true` if the value changed, or `false` if it did not.
77    #[inline]
78    pub fn set_false(&mut self, pos: Pos) -> bool {
79        if self[pos] {
80            self[pos] = false;
81            true
82        } else {
83            false
84        }
85    }
86
87    /// Sets the specified `pos` to `true`.
88    ///
89    /// Returns `true` if the value changed, or `false` if it did not.
90    #[inline]
91    pub fn set_true(&mut self, pos: Pos) -> bool {
92        if self[pos] {
93            false
94        } else {
95            self[pos] = true;
96            true
97        }
98    }
99
100    /// Toggles the specified `pos` between `false` and `true` and vice versa.
101    #[inline]
102    pub fn toggle(&mut self, pos: Pos) {
103        self[pos] = !self[pos];
104    }
105}
106
107impl Index<Pos> for GridMask {
108    type Output = bool;
109
110    #[inline]
111    fn index(&self, pos: Pos) -> &Self::Output {
112        #[allow(clippy::cast_sign_loss)]
113        &self.mask[pos.x as usize * self.cols + pos.y as usize]
114    }
115}
116
117impl IndexMut<Pos> for GridMask {
118    #[inline]
119    fn index_mut(&mut self, pos: Pos) -> &mut Self::Output {
120        #[allow(clippy::cast_sign_loss)]
121        &mut self.mask[pos.x as usize * self.cols + pos.y as usize]
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::{GridMask, PosGet, bytes_grid};
128    use crate::pos::Pos;
129
130    #[test]
131    fn bytes_grid_basic() {
132        let input = ("abc\ndef", [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]);
133        let expected = (2, 3, b"abcdef");
134        let grid = bytes_grid(input.0);
135        let output = (grid.rows(), grid.cols(), &input.1.map(|c| grid[c]));
136        assert_eq!(expected, output, "\n input: {input:?}");
137    }
138
139    // ------------------------------------------------------------------------------------------------
140    // PosGet
141
142    #[test]
143    fn grid_pos_get() {
144        let input = ("abc\ndef", [Pos::new(1, 1), Pos::new(0, 3)]);
145        let expected = [Some(&b'e'), None];
146        let grid = bytes_grid(input.0);
147        let output = input.1.map(|p| grid.pos_get(p));
148        assert_eq!(expected, output, "\n input: {input:?}");
149    }
150
151    #[test]
152    fn grid_pos_get_mut() {
153        let input = ("abc\ndef", (1, 1), b'x');
154        let expected = b'x';
155        let mut grid = bytes_grid(input.0);
156        *grid
157            .pos_get_mut(Pos::from(input.1))
158            .expect("Expected in-bound position") = input.2;
159        let output = grid[input.1];
160        assert_eq!(expected, output, "\n input: {input:?}");
161    }
162
163    #[test]
164    fn grid_index_pos() {
165        let input = ("abc\ndef", Pos::new(1, 1));
166        let expected = b'e';
167        let grid = bytes_grid(input.0);
168        let output = grid[input.1];
169        assert_eq!(expected, output, "\n input: {input:?}");
170    }
171
172    #[test]
173    fn grid_indexmut_pos() {
174        let input = ("abc\ndef", (1, 1), b'x');
175        let expected = b'x';
176        let mut grid = bytes_grid(input.0);
177        grid[Pos::from(input.1)] = input.2;
178        let output = grid[input.1];
179        assert_eq!(expected, output, "\n input: {input:?}");
180    }
181
182    // ---------- GridMask ----------
183
184    #[test]
185    fn gridmask_new() {
186        let input = (2, 3);
187        let expected = (vec![false; 6], 3);
188        let gridmask = GridMask::new(input);
189        let output = (gridmask.mask, gridmask.cols);
190        assert_eq!(expected, output, "\n input: {input:?}");
191    }
192
193    #[test]
194    fn gridmask_set_false() {
195        let input = ((2, 3), Pos::new(1, 1));
196        let expected = (false, false, true, false);
197        let mut gridmask = GridMask::new(input.0);
198        let output = (
199            gridmask[input.1],
200            gridmask.set_false(input.1),
201            {
202                gridmask[input.1] = true;
203                gridmask.set_false(input.1)
204            },
205            gridmask[input.1],
206        );
207        assert_eq!(expected, output, "\n input: {input:?}");
208    }
209
210    #[test]
211    fn gridmask_set_true() {
212        let input = ((2, 3), Pos::new(1, 1));
213        let expected = (false, true, true, false);
214        let mut gridmask = GridMask::new(input.0);
215        let output = (
216            gridmask[input.1],
217            gridmask.set_true(input.1),
218            gridmask[input.1],
219            gridmask.set_true(input.1),
220        );
221        assert_eq!(expected, output, "\n input: {input:?}");
222    }
223
224    #[test]
225    fn gridmask_toggle() {
226        let input = ((2, 3), Pos::new(1, 1));
227        let expected = (false, true, false);
228        let mut gridmask = GridMask::new(input.0);
229        let output = (
230            gridmask[input.1],
231            {
232                gridmask.toggle(input.1);
233                gridmask[input.1]
234            },
235            {
236                gridmask.toggle(input.1);
237                gridmask[input.1]
238            },
239        );
240        assert_eq!(expected, output, "\n input: {input:?}");
241    }
242
243    #[test]
244    fn gridmask_index_pos() {
245        let input = ((2, 3), [Pos::new(0, 2), Pos::new(1, 1)]);
246        let expected = [true, false];
247        let mut gridmask = GridMask::new(input.0);
248        gridmask.set_true(input.1[0]);
249        let output = input.1.map(|p| gridmask[p]);
250        assert_eq!(expected, output, "\n input: {input:?}");
251    }
252
253    #[test]
254    fn gridmask_indexmut_pos() {
255        let input = ((2, 3), [Pos::new(0, 2), Pos::new(1, 1)]);
256        let expected = [true, false, false, true];
257        let mut gridmask = GridMask::new(input.0);
258        gridmask.set_true(input.1[0]);
259        let mut output = [false; 4];
260        output[0] = gridmask[input.1[0]];
261        output[1] = gridmask[input.1[1]];
262        gridmask[input.1[0]] = !gridmask[input.1[0]];
263        gridmask[input.1[1]] = !gridmask[input.1[1]];
264        output[2] = gridmask[input.1[0]];
265        output[3] = gridmask[input.1[1]];
266        assert_eq!(expected, output, "\n input: {input:?}");
267    }
268}