-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathday18.rs
More file actions
200 lines (175 loc) · 5 KB
/
day18.rs
File metadata and controls
200 lines (175 loc) · 5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
use std::{
collections::{HashSet, VecDeque},
hash::Hash,
};
// const N_ROWS: usize = 7;
// const N_COLS: usize = 7;
// const N_OBSTACLES: usize = 12;
const N_ROWS: usize = 71;
const N_COLS: usize = 71;
const N_OBSTACLES: usize = 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Point {
row: usize,
col: usize,
}
impl Point {
pub fn neighbors(&self) -> Vec<Point> {
let mut neighbors = Vec::new();
if self.row > 0 {
neighbors.push(Point {
row: self.row - 1,
col: self.col,
});
}
neighbors.push(Point {
row: self.row + 1,
col: self.col,
});
if self.col > 0 {
neighbors.push(Point {
row: self.row,
col: self.col - 1,
});
}
neighbors.push(Point {
row: self.row,
col: self.col + 1,
});
neighbors
}
pub fn short_string(&self) -> String {
format!("({},{})", self.col, self.row)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct Grid {
n_rows: usize,
n_cols: usize,
falling_obstacles: Vec<Point>,
}
impl Grid {
pub fn from_input(input: &str, n_rows: usize, n_cols: usize) -> Self {
let mut falling_obstacles = Vec::new();
for lin in input.lines() {
let (left, right) = lin.split_once(",").unwrap();
let col = left.parse().unwrap();
let row = right.parse().unwrap();
falling_obstacles.push(Point { row, col });
}
Grid {
n_rows,
n_cols,
falling_obstacles,
}
}
pub fn neighbors(&self, point: Point) -> Vec<Point> {
point
.neighbors()
.iter()
.filter_map(|p| {
if p.row < self.n_rows && p.col < self.n_cols {
Some(*p)
} else {
None
}
})
.collect()
}
pub fn bfs(&self, n_obstacles: usize) -> usize {
let mut visited = HashSet::new();
let mut queue = VecDeque::new();
queue.push_back((Point { row: 0, col: 0 }, 0));
visited.insert(Point { row: 0, col: 0 });
for p in self.falling_obstacles.iter().take(n_obstacles) {
visited.insert(*p);
}
while queue.len() > 0 {
let (point, step) = queue.pop_front().unwrap();
if point.row == self.n_rows - 1 && point.col == self.n_cols - 1 {
return step;
}
for neighbor in self.neighbors(point) {
if visited.contains(&neighbor) {
continue;
}
queue.push_back((neighbor, step + 1));
visited.insert(neighbor);
}
}
0
}
pub fn binary_search(&self) -> Point {
let mut low = 0;
// assuming that high does not work
let mut high = self.falling_obstacles.len();
while low < high {
let mid = (low + high) / 2;
let steps = self.bfs(mid);
if steps == 0 {
high = mid;
} else {
low = mid + 1;
}
}
// subtract one, because "mid" is the first byte that
// isn't dropped.
self.falling_obstacles.get(low - 1).unwrap().clone()
}
#[allow(dead_code)]
pub fn to_string(&self, visited: Option<&HashSet<Point>>) -> String {
let mut grid = vec![vec!['.'; self.n_cols]; self.n_rows];
let visited = match visited {
Some(v) => v,
None => &HashSet::from_iter(self.falling_obstacles.iter().cloned()),
};
for p in visited {
grid[p.row][p.col] = '#';
}
let mut s = String::new();
for row in grid.iter() {
for ch in row.iter() {
s.push(*ch);
}
s.push('\n');
}
s
}
}
pub fn task01(input: &str) -> String {
let grid = Grid::from_input(input, N_ROWS, N_COLS);
grid.bfs(N_OBSTACLES).to_string()
}
pub fn task02(input: &str) -> String {
let grid = Grid::from_input(input, N_ROWS, N_COLS);
grid.binary_search().short_string()
}
#[cfg(test)]
mod tests {
use super::super::fs_utils::{read_example, read_input};
use super::*;
#[test]
fn test_task01() {
let input = read_example(18, 1);
let grid = Grid::from_input(&input, 7, 7);
let res = grid.bfs(12).to_string();
assert_eq!(res, "22");
}
#[test]
fn run_task01() {
let input = read_input(18);
assert_eq!(task01(&input), "340");
}
#[test]
fn test_task02() {
let input = read_example(18, 1);
let grid = Grid::from_input(&input, 7, 7);
let res = grid.binary_search().short_string();
assert_eq!(res, "(6,1)");
}
#[test]
fn run_task02() {
let input = read_input(18);
assert_eq!(task02(&input), "(34,32)");
}
}