-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday21.rs
125 lines (110 loc) · 3.51 KB
/
day21.rs
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
use std::collections::HashSet;
use polynomial::Polynomial;
use aoc_lib::{
answer::Answer,
directions::{Cardinal, Direction},
matrix::Matrix,
solution::Solution,
vec2::Vec2,
};
pub struct Day21;
const SIZE: usize = 65;
impl Solution for Day21 {
fn part_a(&self, input: &[String]) -> Answer {
let parsed = parse(input);
let mut queue = HashSet::new();
queue.insert(parsed.starting_pos);
for _ in 0..64 {
let mut next = HashSet::new();
for pos in queue.iter() {
for t in next_tiles(&parsed.map, *pos) {
next.insert(t);
}
}
queue = next;
}
queue.len().into()
}
fn part_b(&self, input: &[String]) -> Answer {
let parsed = parse(input);
let mut queue = HashSet::new();
queue.insert(Vec2::<isize>::from(parsed.starting_pos));
let map = &parsed.map;
let mut points = vec![];
let mut steps = 0;
for run in 0..3 {
while steps < (65 + run * SIZE) {
let mut next = HashSet::new();
for pos in queue.iter() {
for t in next_tiles_scaled(map, *pos) {
next.insert(t);
}
}
queue = next;
steps += 1;
}
points.push(Vec2::new(steps, queue.len()));
}
let x = points.iter().map(|p| p.x as f64).collect::<Vec<_>>();
let y = points.iter().map(|p| p.y as f64).collect::<Vec<_>>();
let pol = Polynomial::lagrange(&x, &y).unwrap();
(pol.eval(26501365.0).round() as u64).into()
}
}
struct Parsed {
map: Matrix<char>,
starting_pos: Vec2<usize>,
}
fn parse(input: &[String]) -> Parsed {
let map = Matrix::from_chars(input);
let starting_pos = map.find('S').unwrap();
Parsed { map, starting_pos }
}
fn next_tiles(map: &Matrix<char>, pos: Vec2<usize>) -> Vec<Vec2<usize>> {
let mut possible = vec![];
for direction in Cardinal::all_clockwise() {
let next_pos = Vec2::<isize>::from(pos) + direction.to_offset();
let next_tile = map.get(&next_pos);
if let Some(next_tile) = next_tile {
let next_pos = Vec2::<usize>::try_from(&next_pos).unwrap();
if *next_tile != '#' {
possible.push(next_pos);
}
}
}
possible
}
fn next_tiles_scaled(map: &Matrix<char>, pos: Vec2<isize>) -> Vec<Vec2<isize>> {
let mut possible = vec![];
for direction in Cardinal::all_clockwise() {
let next_pos = pos + direction.to_offset();
// scaled is to virtually check on the real map
let scaled = scale_pos(next_pos);
let next_tile = map[scaled];
if next_tile != '#' {
possible.push(next_pos);
}
}
possible
}
fn scale_pos(pos: Vec2<isize>) -> Vec2<usize> {
let mut mapped = pos;
// only want
mapped = Vec2::new(
(SIZE as isize + mapped.x % SIZE as isize) % SIZE as isize,
(SIZE as isize + mapped.y % SIZE as isize) % SIZE as isize,
);
Vec2::<usize>::try_from(&mapped).unwrap()
}
#[cfg(test)]
mod test {
use aoc_lib::{self, answer::Answer, input, solution::Solution};
use super::Day21;
#[test]
fn test_a() {
let input =
input::read_file(&format!("{}day_21_test.txt", crate::FILES_PREFIX_TEST)).unwrap();
let answer = Day21.part_a(&input);
assert_eq!(<i32 as Into<Answer>>::into(42), answer);
}
}