Skip to content

Commit c4d2c56

Browse files
committed
2019 day 20 part 2
1 parent ebc344c commit c4d2c56

File tree

1 file changed

+151
-29
lines changed

1 file changed

+151
-29
lines changed

src/y2019/day20.rs

Lines changed: 151 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ impl crate::Puzzle for Solver {
1717
}
1818

1919
fn part1(&self) -> String {
20-
solve(&self.maze).to_string()
20+
solve(&self.maze, false).unwrap().to_string()
2121
}
2222

2323
fn part2(&self) -> String {
24-
"unimplemented".to_string()
24+
solve(&self.maze, true).unwrap().to_string()
2525
}
2626
}
2727

@@ -33,6 +33,9 @@ fn parse_input(input: &str) -> Maze {
3333
// Map of labels to a vec of (entrance, local_exit) pair
3434
let mut warp_markers: HashMap<(char, char), Vec<(Pos, Pos)>> = HashMap::new();
3535

36+
let mut top_left_corner: Option<Pos> = None;
37+
let mut bottom_right_corner: Option<Pos> = None;
38+
3639
// iterate over each marker. Record entrance, exit and each warp with its local exit
3740
let each_pos: Vec<Pos> = grid.iter().map(|(p, _cell)| p).collect();
3841
for p in each_pos {
@@ -58,15 +61,40 @@ fn parse_input(input: &str) -> Maze {
5861
grid.set(p2, Empty);
5962
}
6063
}
64+
Wall => {
65+
// track these corners to determine if warps are outer/inner
66+
if top_left_corner.is_none() {
67+
top_left_corner = Some(p);
68+
}
69+
bottom_right_corner = Some(p);
70+
}
6171
_ => {}
6272
}
6373
}
6474

75+
// lines containing outer portals
76+
let outer_left_x = top_left_corner.unwrap().x - 1;
77+
let outer_right_x = bottom_right_corner.unwrap().x + 1;
78+
let outer_top_y = top_left_corner.unwrap().y - 1;
79+
let outer_bottom_y = bottom_right_corner.unwrap().y + 1;
80+
6581
// Each label should have exactly 2 (entrace,local_exits) pairs.
6682
// Insert them into the maze, swapping local exits so that they become warp exits.
6783
for (_label, v) in warp_markers.iter() {
68-
grid.set(v[0].0, Warp(v[1].1));
69-
grid.set(v[1].0, Warp(v[0].1));
84+
let p = v[0].0;
85+
if p.x == outer_left_x
86+
|| p.x == outer_right_x
87+
|| p.y == outer_top_y
88+
|| p.y == outer_bottom_y
89+
{
90+
// the first pair is outer
91+
grid.set(v[0].0, OuterPortal(v[1].1));
92+
grid.set(v[1].0, InnerPortal(v[0].1));
93+
} else {
94+
// the first pair is inner
95+
grid.set(v[0].0, InnerPortal(v[1].1));
96+
grid.set(v[1].0, OuterPortal(v[0].1));
97+
}
7098
}
7199

72100
Maze {
@@ -112,8 +140,9 @@ enum Cell {
112140
Passage,
113141
Entrance,
114142
Exit,
115-
Label(char),
116-
Warp(Pos),
143+
Label(char), // temporary during parsing
144+
InnerPortal(Pos),
145+
OuterPortal(Pos),
117146
}
118147

119148
impl From<char> for Cell {
@@ -136,28 +165,75 @@ struct Maze {
136165

137166
const NEIGHBOURS: [Compass; 4] = [North, East, South, West];
138167

139-
fn solve(maze: &Maze) -> usize {
140-
bfs::search(
141-
&maze.entrance,
142-
|p| {
143-
NEIGHBOURS
144-
.iter()
145-
.filter_map(|dir| {
146-
let p2 = p.step(*dir);
147-
match maze.grid.get(p2) {
148-
Wall => None,
149-
Passage => Some((p2, dir)),
150-
Exit => Some((p2, dir)),
151-
Warp(to) => Some((*to, dir)),
152-
_ => None,
153-
}
154-
})
155-
.collect::<Vec<_>>()
168+
fn solve(maze: &Maze, recursive: bool) -> Option<usize> {
169+
let path = bfs::search(
170+
&(maze.entrance, 0),
171+
|(p, level)| {
172+
if recursive {
173+
recursive_neighbours(maze, p, level)
174+
} else {
175+
non_recursive_neighbours(maze, p)
176+
}
156177
},
157-
|p| *p == maze.exit,
158-
)
159-
.unwrap()
160-
.len()
178+
|(p, level)| *level == 0 && *p == maze.exit,
179+
);
180+
if let Some(v) = path {
181+
Some(v.len())
182+
} else {
183+
None
184+
}
185+
}
186+
187+
// original version, any portal warps to its companion with the same name
188+
fn non_recursive_neighbours(maze: &Maze, p: &Pos) -> Vec<((Pos, u32), Compass)> {
189+
NEIGHBOURS
190+
.iter()
191+
.filter_map(|dir| {
192+
let p2 = p.step(*dir);
193+
match maze.grid.get(p2) {
194+
Wall => None,
195+
Passage => Some(((p2, 0), *dir)),
196+
Exit => Some(((p2, 0), *dir)),
197+
InnerPortal(to) | OuterPortal(to) => Some(((*to, 0), *dir)),
198+
_ => None,
199+
}
200+
})
201+
.collect()
202+
}
203+
204+
// inner warps are now to a deeper copy of the maze,
205+
// outer warps are back to the previous copy
206+
// initial maze is outermost (level=0); warps do not lead anywhere
207+
// entrance and exit only exist on the outermost maze.
208+
fn recursive_neighbours(maze: &Maze, p: &Pos, level: &u32) -> Vec<((Pos, u32), Compass)> {
209+
NEIGHBOURS
210+
.iter()
211+
.filter_map(|dir| {
212+
let p2 = p.step(*dir);
213+
match maze.grid.get(p2) {
214+
Wall => None,
215+
Passage => Some(((p2, *level), *dir)),
216+
Exit => Some(((p2, *level), *dir)),
217+
InnerPortal(to) => {
218+
// A level limit is needed to avoid infinite recursion when there's
219+
// no path (like test example 2)
220+
if *level < 25 {
221+
Some(((*to, level + 1), *dir))
222+
} else {
223+
None
224+
}
225+
}
226+
OuterPortal(to) => {
227+
if *level > 0 {
228+
Some(((*to, level - 1), *dir))
229+
} else {
230+
None
231+
}
232+
}
233+
_ => None,
234+
}
235+
})
236+
.collect()
161237
}
162238

163239
#[test]
@@ -185,7 +261,8 @@ fn test() {
185261
]
186262
.join("");
187263
let maze = parse_input(&example1);
188-
assert_eq!(23, solve(&maze));
264+
assert_eq!(Some(23), solve(&maze, false));
265+
assert_eq!(Some(26), solve(&maze, true));
189266

190267
let example2 = [
191268
" A \n",
@@ -228,5 +305,50 @@ fn test() {
228305
]
229306
.join("");
230307
let maze = parse_input(&example2);
231-
assert_eq!(58, solve(&maze));
308+
assert_eq!(Some(58), solve(&maze, false));
309+
assert_eq!(None, solve(&maze, true));
310+
311+
let example3 = [
312+
" Z L X W C \n",
313+
" Z P Q B K \n",
314+
" ###########.#.#.#.#######.############### \n",
315+
" #...#.......#.#.......#.#.......#.#.#...# \n",
316+
" ###.#.#.#.#.#.#.#.###.#.#.#######.#.#.### \n",
317+
" #.#...#.#.#...#.#.#...#...#...#.#.......# \n",
318+
" #.###.#######.###.###.#.###.###.#.####### \n",
319+
" #...#.......#.#...#...#.............#...# \n",
320+
" #.#########.#######.#.#######.#######.### \n",
321+
" #...#.# F R I Z #.#.#.# \n",
322+
" #.###.# D E C H #.#.#.# \n",
323+
" #.#...# #...#.# \n",
324+
" #.###.# #.###.# \n",
325+
" #.#....OA WB..#.#..ZH\n",
326+
" #.###.# #.#.#.# \n",
327+
"CJ......# #.....# \n",
328+
" ####### ####### \n",
329+
" #.#....CK #......IC\n",
330+
" #.###.# #.###.# \n",
331+
" #.....# #...#.# \n",
332+
" ###.### #.#.#.# \n",
333+
"XF....#.# RF..#.#.# \n",
334+
" #####.# ####### \n",
335+
" #......CJ NM..#...# \n",
336+
" ###.#.# #.###.# \n",
337+
"RE....#.# #......RF\n",
338+
" ###.### X X L #.#.#.# \n",
339+
" #.....# F Q P #.#.#.# \n",
340+
" ###.###########.###.#######.#########.### \n",
341+
" #.....#...#.....#.......#...#.....#.#...# \n",
342+
" #####.#.###.#######.#######.###.###.#.#.# \n",
343+
" #.......#.......#.#.#.#.#...#...#...#.#.# \n",
344+
" #####.###.#####.#.#.#.#.###.###.#.###.### \n",
345+
" #.......#.....#.#...#...............#...# \n",
346+
" #############.#.#.###.################### \n",
347+
" A O F N \n",
348+
" A A D M \n",
349+
]
350+
.join("");
351+
let maze = parse_input(&example3);
352+
assert_eq!(Some(77), solve(&maze, false));
353+
assert_eq!(Some(396), solve(&maze, true));
232354
}

0 commit comments

Comments
 (0)