Skip to content

Commit 0ddb58a

Browse files
committed
Clean up day 18
1 parent d7ad7cb commit 0ddb58a

File tree

2 files changed

+192
-0
lines changed

2 files changed

+192
-0
lines changed

data/examples/18.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
5,4
2+
4,2
3+
4,5
4+
3,0
5+
2,1
6+
6,3
7+
2,4
8+
1,5
9+
0,6
10+
3,3
11+
2,6
12+
5,1
13+
1,2
14+
5,5
15+
2,5
16+
6,5
17+
1,4
18+
0,4
19+
6,4
20+
1,1
21+
6,1
22+
1,0
23+
0,5
24+
1,6
25+
2,0

src/bin/18.rs

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
use std::collections::VecDeque;
2+
3+
advent_of_code::solution!(18);
4+
5+
const DIRECTIONS: [(isize, isize); 4] = [(1, 0), (0, 1), (-1, 0), (0, -1)];
6+
7+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8+
enum Space {
9+
Free,
10+
Byte,
11+
}
12+
struct Grid {
13+
tiles: Vec<Vec<Space>>,
14+
height: usize,
15+
width: usize,
16+
}
17+
18+
impl Grid {
19+
fn new(width: usize, height: usize) -> Self {
20+
let tiles = vec![vec![Space::Free; width]; height];
21+
Self {
22+
tiles,
23+
height,
24+
width,
25+
}
26+
}
27+
28+
fn corrupted_space(&mut self, row: usize, col: usize) {
29+
self.tiles[row][col] = Space::Byte;
30+
}
31+
32+
fn corrupt_space(&mut self, row: usize, col: usize) {
33+
self.tiles[row][col] = Space::Byte;
34+
}
35+
36+
fn corrupt_spaces(&mut self, spaces: &[(usize, usize)]) {
37+
for &(row, col) in spaces {
38+
self.corrupt_space(row, col);
39+
}
40+
}
41+
fn exists_in_bounds(&self, row: isize, col: isize) -> bool {
42+
row >= 0 && row < self.height as isize && col >= 0 && col < self.width as isize
43+
}
44+
45+
fn is_free(&self, row: usize, col: usize) -> bool {
46+
self.tiles[row][col] == Space::Free
47+
}
48+
49+
fn shortest_path(&self) -> usize {
50+
self.find_path(true)
51+
}
52+
53+
fn path_exists(&self) -> bool {
54+
self.find_path(false) != usize::MAX
55+
}
56+
57+
fn find_path(&self, return_distance: bool) -> usize {
58+
let start = (0, 0);
59+
let end = (self.height - 1, self.width - 1);
60+
let mut to_visit: VecDeque<(usize, usize, usize)> = VecDeque::new();
61+
let mut visited: Vec<Vec<bool>> = vec![vec![false; self.width]; self.height];
62+
63+
to_visit.push_back((start.0, start.1, 0));
64+
65+
while let Some((row, col, dist)) = to_visit.pop_front() {
66+
if (row, col) == end {
67+
return if return_distance { dist } else { 1 }; // return 1 if path exists
68+
}
69+
70+
if visited[row][col] {
71+
continue;
72+
}
73+
74+
visited[row][col] = true;
75+
76+
for (dr, dc) in DIRECTIONS {
77+
let new_row = row as isize + dr;
78+
let new_col = col as isize + dc;
79+
if self.exists_in_bounds(new_row, new_col)
80+
&& self.is_free(new_row as usize, new_col as usize)
81+
{
82+
let new_dist = dist + 1;
83+
to_visit.push_back((new_row as usize, new_col as usize, new_dist));
84+
}
85+
}
86+
}
87+
usize::MAX
88+
}
89+
90+
fn clear_bytes(&mut self) {
91+
for row in &mut self.tiles {
92+
row.fill(Space::Free);
93+
}
94+
}
95+
}
96+
97+
pub fn part_one(input: &str) -> Option<usize> {
98+
part_one_called::<71, 1024>(input)
99+
}
100+
101+
pub fn part_one_called<const RANGE: usize, const TAKEN_N: usize>(input: &str) -> Option<usize> {
102+
let mut grid = Grid::new(RANGE, RANGE);
103+
for byte in input.lines().take(TAKEN_N) {
104+
let (row, col) = byte.split_once(',').unwrap();
105+
106+
let row = row.parse().unwrap();
107+
let col = col.parse().unwrap();
108+
109+
grid.corrupted_space(row, col);
110+
}
111+
Some(grid.shortest_path())
112+
}
113+
114+
pub fn part_two(input: &str) -> Option<String> {
115+
part_two_called::<71, 1024>(input)
116+
}
117+
118+
pub fn part_two_called<const RANGE: usize, const TAKEN_N: usize>(input: &str) -> Option<String> {
119+
let mut grid = Grid::new(RANGE, RANGE);
120+
121+
let bytes = input
122+
.lines()
123+
.map(|l| {
124+
l.split_once(',')
125+
.map(|(a, b)| (a.parse().unwrap(), b.parse().unwrap()))
126+
.unwrap()
127+
})
128+
.collect::<Vec<(usize, usize)>>();
129+
130+
let mut max = bytes.len();
131+
let mut min = RANGE + 1;
132+
133+
while min != max {
134+
let mid = (min + max) / 2;
135+
136+
grid.corrupt_spaces(&bytes[..=mid]);
137+
if grid.path_exists() {
138+
min = mid + 1;
139+
} else {
140+
max = mid;
141+
}
142+
143+
grid.clear_bytes();
144+
}
145+
let (first_part, second_part) = bytes[max];
146+
let result = format!("{:?},{:?}", first_part, second_part);
147+
Some(result)
148+
}
149+
150+
#[cfg(test)]
151+
mod tests {
152+
use super::*;
153+
154+
#[test]
155+
fn test_part_one() {
156+
let result =
157+
part_one_called::<7, 12>(&advent_of_code::template::read_file("examples", DAY));
158+
assert_eq!(result, Some(22));
159+
}
160+
161+
#[test]
162+
fn test_part_two() {
163+
let result =
164+
part_two_called::<7, 12>(&advent_of_code::template::read_file("examples", DAY));
165+
assert_eq!(result, Some(String::from("6,1")));
166+
}
167+
}

0 commit comments

Comments
 (0)