-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathday1.rs
More file actions
195 lines (165 loc) · 4.33 KB
/
day1.rs
File metadata and controls
195 lines (165 loc) · 4.33 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
use std::{fmt::Display, fs};
struct Problem {
safe: Safe,
}
impl Problem {
pub fn from_string(string: &str) -> Self {
Self {
safe: Safe::from_string(string.trim()),
}
}
pub fn part_1(&self) -> u32 {
self.safe.do_rotations().zero_ends
}
pub fn part_2(&self) -> u32 {
self.safe.do_rotations().zero_clicks
}
}
struct Safe {
dial: Dial,
rotations: Vec<Rotation>,
}
impl Safe {
pub fn from_string(string: &str) -> Self {
Self {
dial: Dial::default(),
rotations: string.lines().map(Rotation::from_string).collect(),
}
}
pub fn do_rotations(&self) -> Dial {
self.rotations
.iter()
.fold(self.dial.clone(), |dial, rotation| dial.interact(rotation))
}
}
#[derive(Clone)]
struct Dial {
size: u32,
position: u32,
zero_clicks: u32,
zero_ends: u32,
}
impl Dial {
pub fn interact(&self, rotation: &Rotation) -> Self {
// Full revolutions
let revolutions = rotation.distance / self.size;
// Remainder distance
let rem_distance = rotation.distance % self.size;
// Net distance to be added (normalizes direction)
let add_distance = match rotation.direction {
// Subtracting D mod X is the same as adding X-D mod X
Direction::Left => self.size - rem_distance,
Direction::Right => rem_distance,
};
let new_position = (self.position + add_distance) % self.size;
let extra_rev = self.position != 0
&& match rotation.direction {
Direction::Left => new_position > self.position,
Direction::Right => new_position < self.position,
};
let mut zero_clicks = self.zero_clicks + revolutions;
// Add zero click ending at zero, or when starting from non-zero and making a revolution
if new_position == 0 || extra_rev {
zero_clicks += 1;
}
// println!(
// "Old pos: {}, Rotation: {}, New pos: {}, Revolutions: {}, Zero clicks: {}",
// self.position, rotation, new_position, revolutions, zero_clicks
// );
Self {
size: self.size,
position: new_position,
zero_clicks,
zero_ends: self.zero_ends + if new_position == 0 { 1 } else { 0 },
}
}
}
impl Default for Dial {
fn default() -> Self {
Self {
size: 100,
position: 50,
zero_clicks: 0,
zero_ends: 0,
}
}
}
enum Direction {
Left,
Right,
}
impl Direction {
pub fn from_char(chr: &char) -> Self {
match chr {
'L' => Self::Left,
'R' => Self::Right,
_ => panic!("Unsupported rotation direction"),
}
}
}
impl Display for Direction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Left => 'L',
Self::Right => 'R',
}
)
}
}
struct Rotation {
direction: Direction,
distance: u32,
}
impl Rotation {
pub fn from_string(string: &str) -> Self {
let mut chars = string.chars();
Self {
direction: Direction::from_char(&chars.next().expect("Missing direction letter")),
distance: chars
.as_str()
.parse::<u32>()
.expect("Unable to parse rotation distance as unsigned int"),
}
}
}
impl Display for Rotation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}{}", self.direction, self.distance)
}
}
fn main() {
let problem = Problem::from_string(
fs::read_to_string("input/day1.txt")
.expect("Failed to read input")
.as_str(),
);
println!("Part 1: {}", problem.part_1()); // Attempts: 33, 989
println!("Part 2: {}", problem.part_2()); // Attempts: 5949, 5941
}
#[cfg(test)]
mod tests {
use super::*;
const SAMPLE: &str = r#"
L68
L30
R48
L5
R60
L55
L1
L99
R14
L82
"#;
#[test]
fn test_sample_part_1() {
assert_eq!(3, Problem::from_string(SAMPLE).part_1());
}
#[test]
fn test_sample_part_2() {
assert_eq!(6, Problem::from_string(SAMPLE).part_2());
}
}