Skip to content

Commit d0b8e0a

Browse files
committed
Extend Grid
1 parent 8b3cd50 commit d0b8e0a

File tree

2 files changed

+86
-45
lines changed

2 files changed

+86
-45
lines changed

src/day15.rs

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use core::fmt;
22

33
use crate::{
4-
grid::{dyadic, Direction, Grid, GridParser, Index, Point, Vector},
4+
grid::{Direction, Grid, GridParseError, GridParser, Index, Point, Vector},
55
util::bfs,
66
};
77

@@ -49,12 +49,13 @@ struct Warehouse {
4949
impl Warehouse {
5050
fn new(plan: &str) -> Warehouse {
5151
// Here we iterate twice over plan - this not cost a meaningful amount of time
52-
let map = GridParser::from([
53-
('#', Thing::Wall),
54-
('.', Thing::Floor),
55-
('O', Thing::Box),
56-
('@', Thing::Floor),
57-
])
52+
let map = GridParser::new(|c| match c {
53+
'#' => Ok(Thing::Wall),
54+
'.' => Ok(Thing::Floor),
55+
'O' => Ok(Thing::Box),
56+
'@' => Ok(Thing::Floor),
57+
_ => Err(GridParseError),
58+
})
5859
.parse(plan)
5960
.unwrap();
6061
Warehouse {
@@ -63,7 +64,7 @@ impl Warehouse {
6364
.flat_map(|l| l.chars())
6465
.enumerate()
6566
.filter_map(|(i, c)| match c {
66-
'@' => Some(dyadic(map.size, i).try_into().unwrap()),
67+
'@' => Some(map.make_index(i).try_into().unwrap()),
6768
_ => None,
6869
})
6970
.next()
@@ -214,25 +215,21 @@ fn parse(input: &str) -> (Warehouse, Vec<Direction>) {
214215

215216
impl fmt::Display for Warehouse2 {
216217
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217-
fn to_char(t: Thing2) -> char {
218-
match t {
219-
Thing2::Wall => '#',
220-
Thing2::BoxLeft => '[',
221-
Thing2::BoxRight => ']',
222-
Thing2::Floor => '.',
223-
}
224-
}
225218
let robot = self.robot.try_into().unwrap();
226-
for row in self.map.iter_indices_by_rows().into_iter() {
227-
for idx in row {
228-
if idx == robot {
229-
write!(f, "@")?;
230-
} else {
231-
write!(f, "{}", to_char(self.map[idx]))?;
219+
write!(
220+
f,
221+
"{}",
222+
self.map.display(
223+
|i| if i == robot { Some('@') } else { None },
224+
|t| match t {
225+
Thing2::Wall => '#',
226+
Thing2::BoxLeft => '[',
227+
Thing2::BoxRight => ']',
228+
Thing2::Floor => '.',
232229
}
233-
}
234-
write!(f, "\n")?;
235-
}
230+
)
231+
)?;
232+
236233
Ok(())
237234
}
238235
}

src/grid.rs

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::{
2-
collections::HashMap,
2+
fmt,
33
hash::Hash,
44
num::TryFromIntError,
55
ops::{Add, AddAssign, IndexMut},
@@ -12,11 +12,11 @@ pub type Size = (usize, usize);
1212
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1313
pub struct Index((usize, usize));
1414

15-
pub fn monadic(size: Size, index: Index) -> usize {
15+
fn monadic(size: Size, index: Index) -> usize {
1616
index.0 .0 + index.0 .1 * size.0
1717
}
1818

19-
pub fn dyadic(size: Size, index: usize) -> Index {
19+
fn dyadic(size: Size, index: usize) -> Index {
2020
Index((index % size.0, index / size.0))
2121
}
2222

@@ -48,7 +48,7 @@ pub struct Grid<T> {
4848
impl<T> Grid<T> {
4949
pub fn new(size: Size, elements: Vec<T>) -> Grid<T> {
5050
if size.0 * size.1 != elements.len() {
51-
panic!("Misatched size");
51+
panic!("Mismatched size");
5252
}
5353
Grid { size, elements }
5454
}
@@ -79,42 +79,86 @@ impl<T> Grid<T> {
7979
let rows = self.size.0;
8080
self.iter_indices().chunks(rows)
8181
}
82-
}
8382

84-
pub struct GridParser<T> {
85-
mapping: HashMap<char, T>,
86-
}
83+
pub fn make_index(&self, monadic: usize) -> Index {
84+
dyadic(self.size, monadic)
85+
}
8786

88-
impl<T: Copy, const N: usize> From<[(char, T); N]> for GridParser<T> {
89-
fn from(arr: [(char, T); N]) -> Self {
90-
Self::new(arr.into())
87+
pub fn display<'a, O, M>(&'a self, overlay: O, mapping: M) -> GridDisplayer<'a, M, O, T>
88+
where
89+
O: Fn(Index) -> Option<char>,
90+
M: Fn(&T) -> char,
91+
{
92+
GridDisplayer::new(mapping, overlay, &self)
9193
}
9294
}
9395

96+
pub struct GridParser<M> {
97+
mapping: M,
98+
}
99+
94100
#[derive(Debug)]
95101
pub struct GridParseError;
96102

97-
impl<T: Copy> GridParser<T> {
98-
pub fn new(mapping: HashMap<char, T>) -> Self {
103+
impl<M: FnMut(char) -> Result<T, GridParseError>, T> GridParser<M> {
104+
pub fn new(mapping: M) -> Self {
99105
Self { mapping }
100106
}
101107

102-
pub fn parse(self, s: &str) -> Result<Grid<T>, GridParseError> {
108+
pub fn parse(mut self, s: &str) -> Result<Grid<T>, GridParseError> {
103109
let lines: Vec<_> = s.lines().collect();
104110
if lines.is_empty() {
105111
return Ok(Grid::new((0, 0), Vec::new()));
106112
}
107113
let size = (lines[0].len(), lines.len());
108-
let things: Option<Vec<_>> = lines
114+
let things: Vec<_> = lines
109115
.into_iter()
110116
.flat_map(|v| v.chars())
111-
.map(|c| self.mapping.get(&c).map(|t| *t))
112-
.collect();
117+
.map(|c| (self.mapping)(c))
118+
.collect::<Result<Vec<_>, _>>()?;
119+
120+
Ok(Grid::new(size, things))
121+
}
122+
}
123+
124+
pub struct GridDisplayer<'a, M, O, T> {
125+
mapping: M,
126+
overlay: O,
127+
grid: &'a Grid<T>,
128+
}
129+
130+
impl<'a, M, O, T> GridDisplayer<'a, M, O, T>
131+
where
132+
M: Fn(&T) -> char,
133+
O: Fn(Index) -> Option<char>,
134+
{
135+
pub fn new(mapping: M, overlay: O, grid: &'a Grid<T>) -> Self {
136+
Self {
137+
mapping,
138+
overlay,
139+
grid,
140+
}
141+
}
142+
}
113143

114-
match things {
115-
Some(things) => Ok(Grid::new(size, things)),
116-
None => Err(GridParseError),
144+
impl<'a, M, O, T> fmt::Display for GridDisplayer<'a, M, O, T>
145+
where
146+
// I would like to have this a FnMut (as it is Map),
147+
// but the fmt::Display-interface forbids that
148+
M: Fn(&T) -> char,
149+
O: Fn(Index) -> Option<char>,
150+
{
151+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152+
for row in self.grid.iter_indices_by_rows().into_iter() {
153+
writeln!(
154+
f,
155+
"{}",
156+
row.into_iter()
157+
.map(|i| ((self.overlay)(i)).unwrap_or_else(|| (self.mapping)(&self.grid[i])))
158+
.collect::<String>()
159+
)?;
117160
}
161+
Ok(())
118162
}
119163
}
120164

0 commit comments

Comments
 (0)