Skip to content

Commit 7a4f778

Browse files
committed
Day 15
1 parent a758057 commit 7a4f778

File tree

7 files changed

+301
-3
lines changed

7 files changed

+301
-3
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
2525
| [Day 12](./src/bin/12.rs) | `434.7µs` | `674.6µs` |
2626
| [Day 13](./src/bin/13.rs) | `90.9µs` | `91.8µs` |
2727
| [Day 14](./src/bin/14.rs) | `31.0µs` | `3.6ms` |
28+
| [Day 15](./src/bin/15.rs) | `119.7µs` | `591.0µs` |
2829

29-
**Total: 30.71ms**
30+
**Total: 31.43ms**
3031
<!--- benchmarking table --->
3132

3233
---

data/examples/15-1.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
########
2+
#..O.O.#
3+
##@.O..#
4+
#...O..#
5+
#.#.O..#
6+
#...O..#
7+
#......#
8+
########
9+
10+
<^^>>>vv<v>>v<<

data/examples/15-2.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
##########
2+
#..O..O.O#
3+
#......O.#
4+
#.OO..O.O#
5+
6+
#O#..O...#
7+
#O..O..O.#
8+
#.OO.O.OO#
9+
#....O...#
10+
##########
11+
12+
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
13+
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
14+
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
15+
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
16+
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
17+
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
18+
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
19+
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
20+
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
21+
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^

data/examples/15-3.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#######
2+
#...#.#
3+
#.....#
4+
#..OO@#
5+
#..O..#
6+
#.....#
7+
#######
8+
9+
<vv<<^^<<^^

mygrid/src/direction.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::hash::{Hash, Hasher};
2-
use std::ops::{Add, Mul};
2+
use std::ops::{Add, Mul, Sub};
33

44
use crate::point::Point;
55

@@ -180,6 +180,15 @@ impl Add<Point> for Direction {
180180
}
181181
}
182182

183+
impl Sub<Direction> for Point {
184+
type Output = Point;
185+
186+
#[inline]
187+
fn sub(self, rhs: Direction) -> Self::Output {
188+
Point::new(self.line - rhs.vertical, self.column - rhs.horizontal)
189+
}
190+
}
191+
183192
impl Mul<isize> for Direction {
184193
type Output = Direction;
185194

mygrid/src/point.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
// heavily inspired by the amazing maneatingape repo, from which I learned a lot, plz see:
44
// https://github.com/maneatingape/advent-of-code-rust/blob/main/src/util/point.rs
55

6-
use std::hash::{Hash, Hasher};
6+
use std::{
7+
hash::{Hash, Hasher},
8+
ops::Mul,
9+
};
710

811
use crate::direction::Direction;
912

@@ -110,6 +113,14 @@ impl std::fmt::Display for Point {
110113
}
111114
}
112115

116+
impl Mul<isize> for Point {
117+
type Output = Point;
118+
119+
fn mul(self, direction: isize) -> Self {
120+
Point::new(self.line * direction, self.column * direction)
121+
}
122+
}
123+
113124
#[cfg(test)]
114125
mod tests {
115126
use super::*;

src/bin/15.rs

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
use heapless::Vec as HeaplessVec;
2+
use itertools::Itertools;
3+
use mygrid::{
4+
direction::{Direction, LEFT, RIGHT},
5+
grid::Grid,
6+
point::Point,
7+
};
8+
9+
advent_of_code::solution!(15);
10+
11+
#[inline]
12+
fn parse_input(input: &str) -> (Grid<char>, Point, Vec<Direction>) {
13+
let (grid_str, path_str) = input.split_once("\n\n").unwrap();
14+
let grid = Grid::new_char_grid_from_str(grid_str);
15+
let start = grid
16+
.iter_item_and_position()
17+
.find(|&(_, v)| *v == '@')
18+
.map(|(p, _)| p)
19+
.unwrap();
20+
let path = path_str
21+
.chars()
22+
.filter(|c| !c.is_whitespace())
23+
.map(|c| Direction::from(c))
24+
.collect();
25+
(grid, start, path)
26+
}
27+
28+
pub fn part_one(input: &str) -> Option<u32> {
29+
let (mut grid, start, path) = parse_input(input);
30+
31+
let mut robot_pos = start;
32+
for direction in path {
33+
//dbg!(&grid, &robot_pos, &direction.to_string());
34+
// find the first hole
35+
let mut lookup_pos = robot_pos;
36+
while grid[lookup_pos] != '.' && grid[lookup_pos] != '#' {
37+
lookup_pos = lookup_pos + direction;
38+
}
39+
if grid[lookup_pos] == '.' {
40+
//dbg!("found hole", lookup_pos);
41+
// backtrack to the robot position, moving everything in the way
42+
let mut backtrack_pos = lookup_pos;
43+
while backtrack_pos != robot_pos {
44+
let next_pos = backtrack_pos - direction;
45+
grid[backtrack_pos] = grid[next_pos];
46+
backtrack_pos = next_pos;
47+
}
48+
grid[robot_pos] = '.';
49+
robot_pos = robot_pos + direction;
50+
}
51+
}
52+
53+
Some(
54+
grid.iter_item_and_position()
55+
.filter(|&(_, v)| *v == 'O')
56+
.map(|(p, _)| 100 * p.line as u32 + p.column as u32)
57+
.sum(),
58+
)
59+
}
60+
61+
pub fn part_two(input: &str) -> Option<u32> {
62+
let (base_grid, base_start, path) = parse_input(input);
63+
64+
// transform grid for part 2
65+
let mut start: Point = base_start;
66+
let mut grid = Grid::new(base_grid.width * 2, base_grid.height, '.');
67+
base_grid.iter_item_and_position().for_each(|(p, v)| {
68+
let p = Point::new(p.line, p.column * 2);
69+
(grid[p], grid[p + RIGHT]) = match *v {
70+
'O' => ('[', ']'),
71+
'@' => {
72+
start = p;
73+
('@', '.')
74+
}
75+
_ => (*v, *v),
76+
};
77+
});
78+
79+
let mut robot_pos = start;
80+
for direction in path {
81+
// fast path when the next position is a hole
82+
let next_pos = robot_pos + direction;
83+
if grid[next_pos] == '.' {
84+
grid[robot_pos] = '.';
85+
grid[next_pos] = '@';
86+
robot_pos = robot_pos + direction;
87+
continue;
88+
}
89+
90+
// horizontal movement is just like p1
91+
if direction == LEFT || direction == RIGHT {
92+
let mut lookup_pos = robot_pos;
93+
while grid[lookup_pos] != '.' && grid[lookup_pos] != '#' {
94+
lookup_pos = lookup_pos + direction;
95+
}
96+
if grid[lookup_pos] == '.' {
97+
let mut backtrack_pos = lookup_pos;
98+
while backtrack_pos != robot_pos {
99+
let next_pos = backtrack_pos - direction;
100+
grid[backtrack_pos] = grid[next_pos];
101+
backtrack_pos = next_pos;
102+
}
103+
grid[robot_pos] = '.';
104+
robot_pos = robot_pos + direction;
105+
}
106+
107+
continue;
108+
}
109+
110+
/**
111+
* vertical movement is backtracking but remembering positions at each steps
112+
*
113+
* Interesting cases
114+
*
115+
* Standard multi-push:
116+
* ########## ##########
117+
* ##......## ##.[][].##
118+
* ##.[][].## -> ##..[]..##
119+
* ##..[]..## ##...@..##
120+
* ##...@..## ##......##
121+
* ########## ##########
122+
*
123+
* Extraction:
124+
* ########## ##########
125+
* ##......## ##.[][].##
126+
* ##.[][].## -> ##..[]..##
127+
* ##[][][]## ##[].@[]##
128+
* ##...@..## ##......##
129+
* ########## ##########
130+
*
131+
* The snake
132+
* ########## ##########
133+
* ##......## ##..[]..##
134+
* ##..[]..## ##...[].##
135+
* ##...[].## -> ##..[]..##
136+
* ##..[]..## ##...@..##
137+
* ##...@..## ##......##
138+
* ########## ##########
139+
*
140+
* moving up from @ will hit the hole at the bottom
141+
* moving down from @ will hit the hole at the top
142+
*/
143+
type LookupSteps = HeaplessVec<LookupPos, 16>;
144+
type LookupPos = HeaplessVec<Point, 128>;
145+
let mut lookup_steps: LookupSteps = LookupSteps::new();
146+
let mut lookup_pos: LookupPos = LookupPos::new();
147+
lookup_pos.push(robot_pos).unwrap();
148+
lookup_steps.push(lookup_pos.clone()).unwrap();
149+
let mut can_move = true;
150+
151+
while lookup_pos.len() > 0 && can_move {
152+
let mut next_lookup_pos: LookupPos = LookupPos::new();
153+
for &p in lookup_pos.iter() {
154+
if grid[p] == '.' {
155+
continue;
156+
}
157+
if grid[p] == '#' {
158+
can_move = false;
159+
break;
160+
}
161+
let next_pos = p + direction;
162+
match grid[next_pos] {
163+
'[' => {
164+
next_lookup_pos.push(next_pos).unwrap();
165+
next_lookup_pos.push(next_pos + RIGHT).unwrap();
166+
}
167+
']' => {
168+
next_lookup_pos.push(next_pos).unwrap();
169+
next_lookup_pos.push(next_pos + LEFT).unwrap();
170+
}
171+
_ => next_lookup_pos.push(next_pos).unwrap(),
172+
}
173+
}
174+
lookup_steps.push(next_lookup_pos.clone()).unwrap();
175+
lookup_pos = next_lookup_pos;
176+
}
177+
178+
if can_move {
179+
for (lookup_pos, prev_lookup_pos) in lookup_steps.iter().rev().tuple_windows() {
180+
for &p in lookup_pos.iter() {
181+
if prev_lookup_pos.contains(&(p - direction)) {
182+
grid[p] = grid[p - direction];
183+
} else {
184+
grid[p] = '.';
185+
}
186+
}
187+
}
188+
189+
grid[robot_pos] = '.';
190+
robot_pos = robot_pos + direction;
191+
}
192+
}
193+
194+
Some(
195+
grid.iter_item_and_position()
196+
.filter(|&(_, v)| *v == '[')
197+
.map(|(p, _)| 100 * p.line as u32 + p.column as u32)
198+
.sum(),
199+
)
200+
}
201+
202+
#[cfg(test)]
203+
mod tests {
204+
use super::*;
205+
206+
#[test]
207+
fn test_part_one_1() {
208+
let result = part_one(&advent_of_code::template::read_file_part(
209+
"examples", DAY, 1,
210+
));
211+
assert_eq!(result, Some(2028));
212+
}
213+
214+
#[test]
215+
fn test_part_one_2() {
216+
let result = part_one(&advent_of_code::template::read_file_part(
217+
"examples", DAY, 2,
218+
));
219+
assert_eq!(result, Some(10092));
220+
}
221+
222+
#[test]
223+
fn test_part_two_1() {
224+
let result = part_two(&advent_of_code::template::read_file_part(
225+
"examples", DAY, 3,
226+
));
227+
assert_eq!(result, Some(618));
228+
}
229+
230+
#[test]
231+
fn test_part_two_2() {
232+
let result = part_two(&advent_of_code::template::read_file_part(
233+
"examples", DAY, 2,
234+
));
235+
assert_eq!(result, Some(9021));
236+
}
237+
}

0 commit comments

Comments
 (0)