Performance Improvements and updated challenge.txt for 2023 day 10

This commit is contained in:
Burnus 2023-12-10 16:39:41 +01:00
parent 5e609eebf1
commit 52c6637f80
2 changed files with 156 additions and 24 deletions

View file

@ -128,6 +128,134 @@ Here are the distances for each tile on that loop:
Find the single giant loop starting at `S`. *How many steps along the loop does it take to get from the starting position to the point farthest from the starting position?* Find the single giant loop starting at `S`. *How many steps along the loop does it take to get from the starting position to the point farthest from the starting position?*
To begin, [get your puzzle input](10/input). Your puzzle answer was `6697`.
Answer: \--- Part Two ---
----------
You quickly reach the farthest point of the loop, but the animal never emerges. Maybe its nest is *within the area enclosed by the loop*?
To determine whether it's even worth taking the time to search for such a nest, you should calculate how many tiles are contained within the loop. For example:
```
...........
.S-------7.
.|F-----7|.
.||.....||.
.||.....||.
.|L-7.F-J|.
.|..|.|..|.
.L--J.L--J.
...........
```
The above loop encloses merely *four tiles* - the two pairs of `.` in the southwest and southeast (marked `I` below). The middle `.` tiles (marked `O` below) are *not* in the loop. Here is the same loop again with those regions marked:
```
...........
.S-------7.
.|F-----7|.
.||OOOOO||.
.||OOOOO||.
.|L-7OF-J|.
.|II|O|II|.
.L--JOL--J.
.....O.....
```
In fact, there doesn't even need to be a full tile path to the outside for tiles to count as outside the loop - squeezing between pipes is also allowed! Here, `I` is still within the loop and `O` is still outside the loop:
```
..........
.S------7.
.|F----7|.
.||OOOO||.
.||OOOO||.
.|L-7F-J|.
.|II||II|.
.L--JL--J.
..........
```
In both of the above examples, `*4*` tiles are enclosed by the loop.
Here's a larger example:
```
.F----7F7F7F7F-7....
.|F--7||||||||FJ....
.||.FJ||||||||L7....
FJL7L7LJLJ||LJ.L-7..
L--J.L7...LJS7F-7L7.
....F-J..F7FJ|L7L7L7
....L7.F7||L7|.L7L7|
.....|FJLJ|FJ|F7|.LJ
....FJL-7.||.||||...
....L---J.LJ.LJLJ...
```
The above sketch has many random bits of ground, some of which are in the loop (`I`) and some of which are outside it (`O`):
```
OF----7F7F7F7F-7OOOO
O|F--7||||||||FJOOOO
O||OFJ||||||||L7OOOO
FJL7L7LJLJ||LJIL-7OO
L--JOL7IIILJS7F-7L7O
OOOOF-JIIF7FJ|L7L7L7
OOOOL7IF7||L7|IL7L7|
OOOOO|FJLJ|FJ|F7|OLJ
OOOOFJL-7O||O||||OOO
OOOOL---JOLJOLJLJOOO
```
In this larger example, `*8*` tiles are enclosed by the loop.
Any tile that isn't part of the main loop can count as being enclosed by the loop. Here's another example with many bits of junk pipe lying around that aren't connected to the main loop at all:
```
FF7FSF7F7F7F7F7F---7
L|LJ||||||||||||F--J
FL-7LJLJ||||||LJL-77
F--JF--7||LJLJ7F7FJ-
L---JF-JLJ.||-FJLJJ7
|F|F-JF---7F7-L7L|7|
|FFJF7L7F-JF7|JL---7
7-L-JL7||F7|L7F-7F7|
L.L7LFJ|||||FJL7||LJ
L7JLJL-JLJLJL--JLJ.L
```
Here are just the tiles that are *enclosed by the loop* marked with `I`:
```
FF7FSF7F7F7F7F7F---7
L|LJ||||||||||||F--J
FL-7LJLJ||||||LJL-77
F--JF--7||LJLJIF7FJ-
L---JF-JLJIIIIFJLJJ7
|F|F-JF---7IIIL7L|7|
|FFJF7L7F-JF7IIL---7
7-L-JL7||F7|L7F-7F7|
L.L7LFJ|||||FJL7||LJ
L7JLJL-JLJLJL--JLJ.L
```
In this last example, `*10*` tiles are enclosed by the loop.
Figure out whether you have time to search for the nest by calculating the area within the loop. *How many tiles are enclosed by the loop?*
Your puzzle answer was `423`.
Both parts of this puzzle are complete! They provide two gold stars: \*\*
At this point, you should [return to your Advent calendar](/2023) and try another puzzle.
If you still want to see it, you can [get your puzzle input](10/input).

View file

@ -31,6 +31,7 @@ impl Display for ParseError<'_> {
struct ParseCharError { offending_char: char, } struct ParseCharError { offending_char: char, }
#[derive(PartialEq, Clone, Copy)] #[derive(PartialEq, Clone, Copy)]
#[repr(u8)]
enum Pipe { enum Pipe {
NorthSouth, // | NorthSouth, // |
EastWest, // - EastWest, // -
@ -95,12 +96,15 @@ fn try_parse_maze(input: &str) -> Result<(HashMap<(usize, usize), Pipe>, (usize,
} else { } else {
return Err(ParseError::DoubleStartError); return Err(ParseError::DoubleStartError);
}, },
Pipe::Ground => (),
p => { p => {
maze.insert((2*y+1, 2*x+1), p); maze.insert((2*y+1, 2*x+1), p);
let ((dy1, dx1), (dy2, dx2)) = p.get_neighbour_diffs(); if y%2 == x%2 {
let (n1, n2) = p.get_neighbours(); let ((dy1, dx1), (dy2, dx2)) = p.get_neighbour_diffs();
maze.insert((2*y+dy1, 2*x+dx1), n1); let (n1, n2) = p.get_neighbours();
maze.insert((2*y+dy2, 2*x+dx2), n2); maze.insert((2*y+dy1, 2*x+dx1), n1);
maze.insert((2*y+dy2, 2*x+dx2), n2);
}
}, },
} }
} }
@ -124,10 +128,12 @@ fn try_parse_maze(input: &str) -> Result<(HashMap<(usize, usize), Pipe>, (usize,
_ => Err(ParseError::InvalidStartNeighbours(n, s, w, e)), _ => Err(ParseError::InvalidStartNeighbours(n, s, w, e)),
}?; }?;
maze.insert((y, x), p); maze.insert((y, x), p);
let ((dy1, dx1), (dy2, dx2)) = p.get_neighbour_diffs(); if y%4 == x%4 {
let (n1, n2) = p.get_neighbours(); let ((dy1, dx1), (dy2, dx2)) = p.get_neighbour_diffs();
maze.insert((y+dy1-1, x+dx1-1), n1); let (n1, n2) = p.get_neighbours();
maze.insert((y+dy2-1, x+dx2-1), n2); maze.insert((y+dy1-1, x+dx1-1), n1);
maze.insert((y+dy2-1, x+dx2-1), n2);
}
Ok((maze, (y, x))) Ok((maze, (y, x)))
}, },
} }
@ -148,8 +154,7 @@ fn filter_and_return_max_dist(maze: &mut HashMap<(usize, usize), Pipe>, start: (
let ((dy1, dx1), (dy2, dx2)) = curr_tile.get_neighbour_diffs(); let ((dy1, dx1), (dy2, dx2)) = curr_tile.get_neighbour_diffs();
if visited.contains(&(y+dy1-1, x+dx1-1)) { if visited.contains(&(y+dy1-1, x+dx1-1)) {
if visited.contains(&(y+dy2-1, x+dx2-1)) { if visited.contains(&(y+dy2-1, x+dx2-1)) {
let mut new_maze: HashMap<_, _> = maze.keys().filter(|(y, x)| y%2 == 1 && x%2 == 1).map(|(y, x)| ((*y, *x), Pipe::Ground)).collect(); let mut new_maze = visited.iter().map(|(y, x)| ((*y, *x), *maze.get(&(*y, *x)).unwrap())).collect();
visited.iter().for_each(|(y, x)| { new_maze.insert((*y, *x), *maze.get(&(*y, *x)).unwrap()); });
std::mem::swap(&mut new_maze, maze); std::mem::swap(&mut new_maze, maze);
return *open_set.iter().map(|(dist, _pos)| dist).max().unwrap() / 2; return *open_set.iter().map(|(dist, _pos)| dist).max().unwrap() / 2;
} else { } else {
@ -170,6 +175,8 @@ fn filter_and_return_max_dist(maze: &mut HashMap<(usize, usize), Pipe>, start: (
fn count_enclosed(maze: &HashMap<(usize, usize), Pipe>, start: (usize, usize)) -> usize { fn count_enclosed(maze: &HashMap<(usize, usize), Pipe>, start: (usize, usize)) -> usize {
let mut starting: HashSet<_> = (start.0-1..start.0+2).flat_map(|y| (start.1-1..start.1+2).filter(|x| maze.get(&(y, *x)).is_none()).map(|x| (y, x)).collect::<HashSet<(usize, usize)>>()).collect(); let mut starting: HashSet<_> = (start.0-1..start.0+2).flat_map(|y| (start.1-1..start.1+2).filter(|x| maze.get(&(y, *x)).is_none()).map(|x| (y, x)).collect::<HashSet<(usize, usize)>>()).collect();
let y_max = maze.iter().map(|((y, _x), _p)| *y).max().unwrap();
let x_max = maze.iter().map(|((_y, x), _p)| *x).max().unwrap();
'starting_tile: while let Some(ground) = starting.iter().next() { 'starting_tile: while let Some(ground) = starting.iter().next() {
let mut new_ground = 0; let mut new_ground = 0;
let mut open_set = VecDeque::from([*ground]); let mut open_set = VecDeque::from([*ground]);
@ -181,19 +188,16 @@ fn count_enclosed(maze: &HashMap<(usize, usize), Pipe>, start: (usize, usize)) -
} }
let neighbours = [(y-1, x), (y+1, x), (y, x-1), (y, x+1)]; let neighbours = [(y-1, x), (y+1, x), (y, x-1), (y, x+1)];
for neighbour in neighbours { for neighbour in neighbours {
match maze.get(&neighbour) { if !maze.contains_key(&neighbour) {
None => if (y..y+2).all(|y1| (x..x+2).all(|x1| maze.get(&(y1, x1)).is_none())) { if y > y_max || x > x_max {
continue 'starting_tile; continue 'starting_tile;
} else if !visited.contains(&neighbour) { } else if !visited.contains(&neighbour) {
visited.insert(neighbour); visited.insert(neighbour);
open_set.push_back(neighbour); open_set.push_back(neighbour);
}, if neighbour.0%2==1 && neighbour.1%2==1 {
Some(Pipe::Ground) => if !visited.contains(&neighbour) {
visited.insert(neighbour);
open_set.push_back(neighbour);
new_ground += 1; new_ground += 1;
}, }
_ => (), }
} }
} }
} }