-
Notifications
You must be signed in to change notification settings - Fork 3
/
day_07.rs
145 lines (123 loc) Β· 3.61 KB
/
day_07.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use common::{solution, Answer};
solution!("Bridge Repair", 7);
fn part_a(input: &str) -> Answer {
solve(input, false).into()
}
fn part_b(input: &str) -> Answer {
solve(input, true).into()
}
fn solve(input: &str, part_b: bool) -> u64 {
let problem = Problem::parse(input);
// For each of the equations, we will add its result value if it is valid.
// For part a, we check if its valid using `is_valid` with part_b = false,
// because an equation that is valid for part a is must be valid for part b,
// we can get a small speedup by only doing the more intense part_b = true
// check if needed.
problem
.cases
.into_iter()
.filter(|x| x.is_valid(false) || (part_b && x.is_valid(true)))
.map(|x| x.result)
.sum::<u64>()
}
struct Problem {
cases: Vec<TestCase>,
}
struct TestCase {
result: u64,
inputs: Vec<u64>,
}
#[derive(Clone, Copy, Debug)]
enum Operations {
Add,
Multiply,
Concat,
}
impl Problem {
fn parse(input: &str) -> Self {
let cases = input
.lines()
.map(|x| {
let (result, inputs) = x.split_once(": ").unwrap();
let result = result.parse::<u64>().unwrap();
let inputs = inputs
.split_whitespace()
.map(|x| x.parse::<u64>().unwrap())
.collect::<Vec<_>>();
TestCase { result, inputs }
})
.collect::<Vec<_>>();
Self { cases }
}
}
impl TestCase {
fn is_valid(&self, part_b: bool) -> bool {
let op_count = self.inputs.len() - 1;
let mut ops = vec![0; op_count];
'outer: loop {
// Set the result to be the first input value to start. Then update
// the result for each operation using the previous result and the
// next number as inputs.
let mut result = self.inputs[0];
for (&op, &input) in ops.iter().zip(self.inputs.iter().skip(1)) {
let op = [Operations::Add, Operations::Multiply, Operations::Concat][op];
result = op.evaluate(result, input);
}
// If the result we get after applying the operations gets us the
// expected result, this equation is valid.
if result == self.result {
return true;
}
// Increments the leftmost operation, carrying if it exceeds 1 for
// part a or 2 for part b.
for op in ops.iter_mut() {
*op += 1;
if *op <= (1 + part_b as usize) {
continue 'outer;
}
*op = 0;
}
return false;
}
}
}
impl Operations {
fn evaluate(&self, a: u64, b: u64) -> u64 {
match self {
Operations::Add => a + b,
Operations::Multiply => a * b,
Operations::Concat => {
let mut out = a;
let mut tmp = b;
while tmp > 0 {
tmp /= 10;
out *= 10;
}
out + b
}
}
}
}
#[cfg(test)]
mod test {
use indoc::indoc;
const CASE: &str = indoc! {"
190: 10 19
3267: 81 40 27
83: 17 5
156: 15 6
7290: 6 8 6 15
161011: 16 10 13
192: 17 8 14
21037: 9 7 18 13
292: 11 6 16 20
"};
#[test]
fn part_a() {
assert_eq!(super::part_a(CASE), 3749.into());
}
#[test]
fn part_b() {
assert_eq!(super::part_b(CASE), 11387.into());
}
}