Skip to content

Commit a7a92d5

Browse files
authored
Merge pull request #98 from jwodder/gh-38
Flatten Grid's innards
2 parents 46638f4 + 6efbdf5 commit a7a92d5

File tree

3 files changed

+197
-107
lines changed

3 files changed

+197
-107
lines changed

adventutil/src/grid.rs

+128-79
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ use thiserror::Error;
1717
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1818
pub struct Grid<T> {
1919
// Invariants:
20-
// - `data` is nonempty.
21-
// - Every row in `data` is nonempty.
22-
// - Every row in `data` has the same length.
23-
data: Vec<Vec<T>>,
20+
// - `data.len() == bounds.height * bounds.width`
21+
// - `bounds.height` is nonzero.
22+
// - `bounds.width` is nonzero.
23+
data: Vec<T>,
24+
bounds: GridBounds,
2425
}
2526

2627
impl<T> Grid<T> {
@@ -32,12 +33,10 @@ impl<T> Grid<T> {
3233
// TODO: Panic if `bounds` is empty
3334
Grid {
3435
data: (0..bounds.height)
35-
.map(|y| {
36-
(0..bounds.width)
37-
.map(|x| f(C::from(Coords::new(y, x))))
38-
.collect::<Vec<_>>()
39-
})
36+
.flat_map(|y| (0..bounds.width).map(move |x| (y, x)))
37+
.map(|(y, x)| f(C::from(Coords::new(y, x))))
4038
.collect(),
39+
bounds,
4140
}
4241
}
4342

@@ -46,25 +45,36 @@ impl<T> Grid<T> {
4645
T: Clone,
4746
{
4847
Grid {
49-
data: vec![vec![value; bounds.width]; bounds.height],
48+
data: vec![value; bounds.width * bounds.height],
49+
bounds,
5050
}
5151
}
5252

5353
pub fn height(&self) -> usize {
54-
self.data.len()
54+
self.bounds.height
5555
}
5656

5757
pub fn width(&self) -> usize {
58-
self.data[0].len()
58+
self.bounds.width
5959
}
6060

6161
pub fn bounds(&self) -> GridBounds {
62-
GridBounds::new(self.height(), self.width())
62+
self.bounds
63+
}
64+
65+
fn get_index(&self, x: usize, y: usize) -> Option<usize> {
66+
if x < self.bounds.width && y < self.bounds.height {
67+
y.checked_mul(self.bounds.width)
68+
.and_then(|yw| yw.checked_add(x))
69+
} else {
70+
None
71+
}
6372
}
6473

6574
pub fn get<C: Into<(usize, usize)>>(&self, coords: C) -> Option<&T> {
6675
let (y, x) = coords.into();
67-
self.data.get(y).and_then(|row| row.get(x))
76+
let i = self.get_index(x, y)?;
77+
self.data.get(i)
6878
}
6979

7080
pub fn get_wrap(&self, (y, x): (isize, isize)) -> &T {
@@ -81,23 +91,40 @@ impl<T> Grid<T> {
8191

8292
pub fn get_mut<C: Into<(usize, usize)>>(&mut self, coords: C) -> Option<&mut T> {
8393
let (y, x) = coords.into();
84-
self.data.get_mut(y).and_then(|row| row.get_mut(x))
94+
let i = self.get_index(x, y)?;
95+
self.data.get_mut(i)
8596
}
8697

87-
// Panics on out-of-bounds
98+
// Does nothing on out-of-bounds
8899
pub fn set<C: Into<(usize, usize)>>(&mut self, coords: C, value: T) {
89100
let (y, x) = coords.into();
90-
self.data[y][x] = value;
101+
if let Some(i) = self.get_index(x, y) {
102+
self.data[i] = value;
103+
}
91104
}
92105

93106
// Panics on out-of-bounds
94107
pub fn row_slice<R: RangeBounds<usize>>(&self, range: R) -> Grid<T>
95108
where
96109
T: Clone,
97110
{
98-
let bounds = (range.start_bound().cloned(), range.end_bound().cloned());
111+
let start_bound = match range.start_bound().cloned() {
112+
std::ops::Bound::Included(y) => y,
113+
std::ops::Bound::Excluded(y) => y + 1,
114+
std::ops::Bound::Unbounded => 0,
115+
};
116+
let end_bound = match range.end_bound().cloned() {
117+
std::ops::Bound::Included(y) => y + 1,
118+
std::ops::Bound::Excluded(y) => y,
119+
std::ops::Bound::Unbounded => self.bounds.height,
120+
};
99121
Grid {
100-
data: self.data[bounds].to_vec(),
122+
data: self.data[(start_bound * self.bounds.width)..(end_bound * self.bounds.width)]
123+
.to_vec(),
124+
bounds: GridBounds {
125+
width: self.bounds.width,
126+
height: end_bound - start_bound,
127+
},
101128
}
102129
}
103130

@@ -106,9 +133,26 @@ impl<T> Grid<T> {
106133
where
107134
T: Clone,
108135
{
109-
let bounds = (range.start_bound().cloned(), range.end_bound().cloned());
136+
let start_bound = match range.start_bound().cloned() {
137+
std::ops::Bound::Included(x) => x,
138+
std::ops::Bound::Excluded(x) => x + 1,
139+
std::ops::Bound::Unbounded => 0,
140+
};
141+
let end_bound = match range.end_bound().cloned() {
142+
std::ops::Bound::Included(x) => x + 1,
143+
std::ops::Bound::Excluded(x) => x,
144+
std::ops::Bound::Unbounded => self.bounds.width,
145+
};
146+
110147
Grid {
111-
data: self.data.iter().map(|row| row[bounds].to_vec()).collect(),
148+
data: (0..self.bounds.height)
149+
.map(|y| y * self.bounds.width)
150+
.flat_map(|yw| self.data[(yw + start_bound)..(yw + end_bound)].to_vec())
151+
.collect(),
152+
bounds: GridBounds {
153+
width: end_bound - start_bound,
154+
height: self.bounds.height,
155+
},
112156
}
113157
}
114158

@@ -117,11 +161,8 @@ impl<T> Grid<T> {
117161
F: FnMut(T) -> U,
118162
{
119163
Grid {
120-
data: self
121-
.data
122-
.into_iter()
123-
.map(|row| row.into_iter().map(&mut f).collect())
124-
.collect(),
164+
data: self.data.into_iter().map(&mut f).collect(),
165+
bounds: self.bounds,
125166
}
126167
}
127168

@@ -130,30 +171,30 @@ impl<T> Grid<T> {
130171
F: FnMut(T) -> Result<U, E>,
131172
{
132173
let mut data = Vec::with_capacity(self.data.len());
133-
for row in self.data {
134-
let mut new_row = Vec::with_capacity(row.len());
135-
for value in row {
136-
new_row.push(f(value)?);
137-
}
138-
data.push(new_row);
174+
for value in self.data {
175+
data.push(f(value)?);
139176
}
140-
Ok(Grid { data })
177+
Ok(Grid {
178+
data,
179+
bounds: self.bounds,
180+
})
141181
}
142182

143183
pub fn map_cells<U, F>(&self, mut f: F) -> Grid<U>
144184
where
145185
F: FnMut(Cell<'_, T>) -> U,
146186
{
147-
let mut data = Vec::with_capacity(self.height());
187+
let mut data = Vec::with_capacity(self.data.len());
148188
for y in 0..self.height() {
149-
let mut new_row = Vec::with_capacity(self.width());
150189
for x in 0..self.width() {
151190
let cell = Cell::new(self, y, x);
152-
new_row.push(f(cell));
191+
data.push(f(cell));
153192
}
154-
data.push(new_row);
155193
}
156-
Grid { data }
194+
Grid {
195+
data,
196+
bounds: self.bounds,
197+
}
157198
}
158199

159200
pub fn enumerate(&self) -> Enumerate<'_, T> {
@@ -176,15 +217,16 @@ impl<T> Grid<T> {
176217
where
177218
P: FnMut(&Vec<T>) -> bool,
178219
{
179-
Grid::try_from(self.data.into_iter().filter(predicate).collect::<Vec<_>>()).ok()
220+
Grid::try_from(self.into_rows().filter(predicate).collect::<Vec<_>>()).ok()
180221
}
181222

182-
pub fn into_rows(self) -> impl Iterator<Item = Vec<T>> {
183-
self.data.into_iter()
223+
pub fn into_rows(mut self) -> impl Iterator<Item = Vec<T>> {
224+
std::iter::repeat_n(self.bounds.width, self.bounds.height)
225+
.map(move |w| self.data.drain(..w).collect())
184226
}
185227

186228
pub fn into_values(self) -> impl Iterator<Item = T> {
187-
self.data.into_iter().flatten()
229+
self.data.into_iter()
188230
}
189231

190232
pub fn iter_coords(&self) -> IterCoords {
@@ -308,14 +350,14 @@ impl<T: FromStr> Grid<T> {
308350

309351
impl<T: fmt::Display> fmt::Display for Grid<T> {
310352
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311-
let mut first = true;
312-
for row in &self.data {
313-
if !std::mem::replace(&mut first, false) {
353+
let mut col = 0;
354+
for value in &self.data {
355+
if col >= self.bounds.width {
356+
col = 0;
314357
writeln!(f)?;
315358
}
316-
for cell in row {
317-
write!(f, "{cell}")?;
318-
}
359+
write!(f, "{value}")?;
360+
col += 1;
319361
}
320362
Ok(())
321363
}
@@ -325,6 +367,7 @@ impl<T> TryFrom<Vec<Vec<T>>> for Grid<T> {
325367
type Error = GridFromError;
326368

327369
fn try_from(data: Vec<Vec<T>>) -> Result<Grid<T>, GridFromError> {
370+
let height = data.len();
328371
let width = match data.first() {
329372
Some(row) => row.len(),
330373
None => return Err(GridFromError::Empty),
@@ -337,7 +380,10 @@ impl<T> TryFrom<Vec<Vec<T>>> for Grid<T> {
337380
if width == 0 {
338381
return Err(GridFromError::Empty);
339382
}
340-
Ok(Grid { data })
383+
Ok(Grid {
384+
data: data.into_iter().flatten().collect(),
385+
bounds: GridBounds { width, height },
386+
})
341387
}
342388
}
343389

@@ -376,14 +422,14 @@ pub struct Draw<'a>(&'a Grid<bool>);
376422

377423
impl fmt::Display for Draw<'_> {
378424
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
379-
let mut first = true;
380-
for row in &self.0.data {
381-
if !std::mem::replace(&mut first, false) {
425+
let mut col = 0;
426+
for &cell in &self.0.data {
427+
if col >= self.0.bounds.width {
428+
col = 0;
382429
writeln!(f)?;
383430
}
384-
for &cell in row {
385-
write!(f, "{}", if cell { '#' } else { '.' })?;
386-
}
431+
write!(f, "{}", if cell { '#' } else { '.' })?;
432+
col += 1;
387433
}
388434
Ok(())
389435
}
@@ -565,11 +611,11 @@ mod tests {
565611
assert_eq!(
566612
gr,
567613
Grid {
568-
data: vec![
569-
vec!['a', 'b', 'c'],
570-
vec!['d', 'e', 'f'],
571-
vec!['g', 'h', 'i']
572-
]
614+
data: vec!['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'],
615+
bounds: GridBounds {
616+
width: 3,
617+
height: 3
618+
},
573619
}
574620
);
575621
}
@@ -580,7 +626,11 @@ mod tests {
580626
assert_eq!(
581627
gr,
582628
Grid {
583-
data: vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]]
629+
data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9],
630+
bounds: GridBounds {
631+
width: 3,
632+
height: 3
633+
},
584634
}
585635
);
586636
}
@@ -599,12 +649,13 @@ mod tests {
599649
gr,
600650
Grid {
601651
data: vec![
602-
vec![22, 13, 17, 11, 0],
603-
vec![8, 2, 23, 4, 24],
604-
vec![21, 9, 14, 16, 7],
605-
vec![6, 10, 3, 18, 5],
606-
vec![1, 12, 20, 15, 19],
607-
]
652+
22, 13, 17, 11, 0, 8, 2, 23, 4, 24, 21, 9, 14, 16, 7, 6, 10, 3, 18, 5, 1, 12,
653+
20, 15, 19,
654+
],
655+
bounds: GridBounds {
656+
width: 5,
657+
height: 5
658+
},
608659
}
609660
);
610661
}
@@ -622,11 +673,11 @@ mod tests {
622673
assert_eq!(
623674
gr.row_slice(2..),
624675
Grid {
625-
data: vec![
626-
vec![21, 9, 14, 16, 7],
627-
vec![6, 10, 3, 18, 5],
628-
vec![1, 12, 20, 15, 19],
629-
]
676+
data: vec![21, 9, 14, 16, 7, 6, 10, 3, 18, 5, 1, 12, 20, 15, 19],
677+
bounds: GridBounds {
678+
width: 5,
679+
height: 3
680+
},
630681
}
631682
);
632683
}
@@ -644,13 +695,11 @@ mod tests {
644695
assert_eq!(
645696
gr.column_slice(..3),
646697
Grid {
647-
data: vec![
648-
vec![22, 13, 17],
649-
vec![8, 2, 23],
650-
vec![21, 9, 14],
651-
vec![6, 10, 3],
652-
vec![1, 12, 20],
653-
]
698+
data: vec![22, 13, 17, 8, 2, 23, 21, 9, 14, 6, 10, 3, 1, 12, 20,],
699+
bounds: GridBounds {
700+
width: 3,
701+
height: 5
702+
},
654703
}
655704
);
656705
}

0 commit comments

Comments
 (0)