-
Notifications
You must be signed in to change notification settings - Fork 4
/
day9.rs
129 lines (121 loc) · 3.36 KB
/
day9.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
126
127
128
129
use std::collections::HashSet;
fn moves<'a, I, S>(lines: I) -> Moves<I>
where
I: Iterator<Item = &'a S>,
S: AsRef<str> + 'a,
{
Moves {
lines,
pos: (0, 0),
dest: Some((0, 0)),
}
}
struct Moves<I: Iterator> {
lines: I,
pos: (i32, i32),
dest: Option<(i32, i32)>,
}
impl<'a, I, S> Iterator for Moves<I>
where
I: Iterator<Item = &'a S>,
S: AsRef<str> + 'a,
{
type Item = (i32, i32);
fn next(&mut self) -> Option<Self::Item> {
let pos = self.pos;
let dest = self.dest.or_else(|| {
self.lines
.by_ref()
.filter_map(|line| {
let line = line.as_ref();
let mut iter = line.split_ascii_whitespace();
let dir = iter.next()?;
let count = iter.next()?.parse::<i32>().ok()?;
match dir {
"L" => Some((pos.0 - count, pos.1)),
"R" => Some((pos.0 + count, pos.1)),
"U" => Some((pos.0, pos.1 - count)),
"D" => Some((pos.0, pos.1 + count)),
_ => None,
}
})
.find(|&dest| pos != dest)
})?;
self.pos = (
pos.0 + (dest.0 - pos.0).signum(),
pos.1 + (dest.1 - pos.1).signum(),
);
self.dest = if self.pos == dest { None } else { Some(dest) };
Some(self.pos)
}
}
fn follow<I: Iterator>(iter: I) -> Follow<I> {
Follow { iter, pos: None }
}
struct Follow<I: Iterator> {
iter: I,
pos: Option<(i32, i32)>,
}
impl<I: Iterator<Item = (i32, i32)>> Iterator for Follow<I> {
type Item = (i32, i32);
fn next(&mut self) -> Option<Self::Item> {
let Some(tail) = self.pos else {
self.pos = Some((0, 0));
return Some((0, 0));
};
let head = self
.iter
.find(|head| (head.0 - tail.0).abs() > 1 || (head.1 - tail.1).abs() > 1)?;
let delta = (head.0 - tail.0, head.1 - tail.1);
let pos = (
if delta.0.abs() >= delta.1.abs() {
head.0 - delta.0.signum()
} else {
head.0
},
if delta.0.abs() <= delta.1.abs() {
head.1 - delta.1.signum()
} else {
head.1
},
);
self.pos = Some(pos);
Some(pos)
}
}
pub fn part1<'a, I, S>(lines: I) -> usize
where
I: IntoIterator<Item = &'a S>,
S: AsRef<str> + 'a,
{
follow(moves(lines.into_iter()))
.collect::<HashSet<_>>()
.len()
}
pub fn part2<'a, I, S>(lines: I) -> usize
where
I: IntoIterator<Item = &'a S>,
S: AsRef<str> + 'a,
{
follow(follow(follow(follow(follow(follow(follow(follow(
follow(moves(lines.into_iter())),
))))))))
.collect::<HashSet<_>>()
.len()
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
static EXAMPLE_1: &[&str] = &["R 4", "U 4", "L 3", "D 1", "R 4", "D 1", "L 5", "R 2"];
static EXAMPLE_2: &[&str] = &["R 5", "U 8", "L 8", "D 3", "R 17", "D 10", "L 25", "U 20"];
#[test]
fn part1_examples() {
assert_eq!(13, part1(EXAMPLE_1));
}
#[test]
fn part2_examples() {
assert_eq!(1, part2(EXAMPLE_1));
assert_eq!(36, part2(EXAMPLE_2));
}
}