Skip to content

Commit 7f18cf0

Browse files
committed
Day 13 solutions
1 parent 7737e99 commit 7f18cf0

File tree

4 files changed

+1492
-4
lines changed

4 files changed

+1492
-4
lines changed

aoc-solver/src/y2024/day13.rs

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
use itertools::Itertools;
2+
3+
use crate::solution::{AocError, Solution};
4+
5+
#[derive(Debug, PartialEq)]
6+
struct Machine {
7+
a: (i64, i64),
8+
b: (i64, i64),
9+
price: (i64, i64),
10+
}
11+
12+
fn parse(input: &str) -> Result<Vec<Machine>, AocError> {
13+
let machines = input
14+
.trim()
15+
.split("\n\n")
16+
.map(|machine_input| {
17+
let (a_input, b_input, price_input) = machine_input
18+
.lines()
19+
.next_tuple()
20+
.ok_or_else(|| AocError::parse(machine_input, "Invalid machine"))?;
21+
22+
let a = a_input
23+
.strip_prefix("Button A: X+")
24+
.and_then(|a| a.split_once(", Y+"))
25+
.and_then(|(x_str, y_str)| {
26+
let x = x_str.parse::<i64>().ok()?;
27+
let y = y_str.parse::<i64>().ok()?;
28+
Some((x, y))
29+
})
30+
.ok_or_else(|| AocError::parse(a_input, "Invalid button A"))?;
31+
32+
let b = b_input
33+
.strip_prefix("Button B: X+")
34+
.and_then(|a| a.split_once(", Y+"))
35+
.and_then(|(x_str, y_str)| {
36+
let x = x_str.parse::<i64>().ok()?;
37+
let y = y_str.parse::<i64>().ok()?;
38+
Some((x, y))
39+
})
40+
.ok_or_else(|| AocError::parse(b_input, "Invalid button B"))?;
41+
42+
let price = price_input
43+
.strip_prefix("Prize: X=")
44+
.and_then(|a| a.split_once(", Y="))
45+
.and_then(|(x_str, y_str)| {
46+
let x = x_str.parse::<i64>().ok()?;
47+
let y = y_str.parse::<i64>().ok()?;
48+
Some((x, y))
49+
})
50+
.ok_or_else(|| AocError::parse(b_input, "Invalid price"))?;
51+
52+
Ok(Machine { a, b, price })
53+
})
54+
.try_collect()?;
55+
56+
Ok(machines)
57+
}
58+
59+
fn find_fewest_tokens(
60+
Machine { price, a, b }: &Machine,
61+
unit_conversion_error: bool,
62+
) -> Option<i64> {
63+
let price = if !unit_conversion_error {
64+
(price.0 + 10000000000000, price.1 + 10000000000000)
65+
} else {
66+
*price
67+
};
68+
69+
// Solving `a_presses` and `b_presses`
70+
// from a system of two equations, accepting only integer solutions:
71+
// a_presses * a.x + b_presses * b.x = price.x
72+
// a_presses * a.y + b_presses * b.y = price.y
73+
74+
let dividend_b = price.1 * a.0 - a.1 * price.0;
75+
let divisor_b = a.0 * b.1 - b.0 * a.1;
76+
77+
let b_presses = dividend_b / divisor_b;
78+
let a_presses = (price.0 - b_presses * b.0) / a.0;
79+
80+
let is_integer_a = (price.0 - b_presses * b.0) % a.0 == 0;
81+
let is_integer_b = dividend_b % divisor_b == 0;
82+
83+
(is_integer_a && is_integer_b).then_some(3 * a_presses + b_presses)
84+
}
85+
86+
pub struct Day13;
87+
impl Solution for Day13 {
88+
type A = i64;
89+
type B = i64;
90+
91+
fn default_input(&self) -> &'static str {
92+
include_str!("../../../inputs/2024/day13.txt")
93+
}
94+
95+
fn part_1(&self, input: &str) -> Result<i64, AocError> {
96+
let machines = parse(input)?;
97+
98+
let fewest = machines
99+
.iter()
100+
.filter_map(|machine| find_fewest_tokens(machine, true))
101+
.sum();
102+
103+
Ok(fewest)
104+
}
105+
106+
fn part_2(&self, input: &str) -> Result<i64, AocError> {
107+
let machines = parse(input)?;
108+
109+
let fewest = machines
110+
.iter()
111+
.filter_map(|machine| find_fewest_tokens(machine, false))
112+
.sum();
113+
114+
Ok(fewest)
115+
}
116+
}
117+
118+
#[cfg(test)]
119+
mod tests {
120+
use super::*;
121+
122+
#[test]
123+
fn it_parses_input() {
124+
assert_eq!(
125+
parse(
126+
"Button A: X+94, Y+34\n\
127+
Button B: X+22, Y+67\n\
128+
Prize: X=8400, Y=5400\n\
129+
\n\
130+
Button A: X+26, Y+66\n\
131+
Button B: X+67, Y+21\n\
132+
Prize: X=12748, Y=12176"
133+
),
134+
Ok(vec![
135+
Machine {
136+
a: (94, 34),
137+
b: (22, 67),
138+
price: (8400, 5400)
139+
},
140+
Machine {
141+
a: (26, 66),
142+
b: (67, 21),
143+
price: (12748, 12176)
144+
}
145+
])
146+
)
147+
}
148+
149+
#[test]
150+
fn it_solves_part1_example_1() {
151+
assert_eq!(
152+
Day13.part_1(
153+
"Button A: X+94, Y+34\n\
154+
Button B: X+22, Y+67\n\
155+
Prize: X=8400, Y=5400\n\
156+
\n\
157+
Button A: X+26, Y+66\n\
158+
Button B: X+67, Y+21\n\
159+
Prize: X=12748, Y=12176\n\
160+
\n\
161+
Button A: X+17, Y+86\n\
162+
Button B: X+84, Y+37\n\
163+
Prize: X=7870, Y=6450\n\
164+
\n\
165+
Button A: X+69, Y+23\n\
166+
Button B: X+27, Y+71\n\
167+
Prize: X=18641, Y=10279"
168+
),
169+
Ok(480)
170+
);
171+
}
172+
173+
#[test]
174+
fn it_solves_part1_example_single() {
175+
assert_eq!(
176+
Day13.part_1(
177+
"Button A: X+94, Y+34\n\
178+
Button B: X+22, Y+67\n\
179+
Prize: X=8400, Y=5400"
180+
),
181+
Ok(280)
182+
);
183+
}
184+
185+
#[test]
186+
fn it_solves_part2_example_1() {
187+
assert_eq!(
188+
Day13.part_2(
189+
"Button A: X+94, Y+34\n\
190+
Button B: X+22, Y+67\n\
191+
Prize: X=8400, Y=5400\n\
192+
\n\
193+
Button A: X+26, Y+66\n\
194+
Button B: X+67, Y+21\n\
195+
Prize: X=12748, Y=12176\n\
196+
\n\
197+
Button A: X+17, Y+86\n\
198+
Button B: X+84, Y+37\n\
199+
Prize: X=7870, Y=6450\n\
200+
\n\
201+
Button A: X+69, Y+23\n\
202+
Button B: X+27, Y+71\n\
203+
Prize: X=18641, Y=10279"
204+
),
205+
Ok(875318608908)
206+
);
207+
}
208+
}

aoc-solver/src/y2024/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub mod day09;
1212
pub mod day10;
1313
pub mod day11;
1414
pub mod day12;
15-
// pub mod day13;
15+
pub mod day13;
1616
// pub mod day14;
1717
// pub mod day15;
1818
// pub mod day16;
@@ -26,7 +26,7 @@ pub mod day12;
2626
// pub mod day24;
2727
// pub mod day25;
2828

29-
pub const MAX_DAYS: u8 = 12;
29+
pub const MAX_DAYS: u8 = 13;
3030

3131
pub struct Y2024;
3232

@@ -45,7 +45,7 @@ impl Solver for Y2024 {
4545
10 => day10::Day10.run(input, 10, 2024),
4646
11 => day11::Day11.run(input, 11, 2024),
4747
12 => day12::Day12.run(input, 12, 2024),
48-
// 13 => day13::Day13.run(input, 13, 2024),
48+
13 => day13::Day13.run(input, 13, 2024),
4949
// 14 => day14::Day14.run(input, 14, 2024),
5050
// 15 => day15::Day15.run(input, 15, 2024),
5151
// 16 => day16::Day16.run(input, 16, 2024),
@@ -87,7 +87,7 @@ impl Solver for Y2024 {
8787
10 => include_str!("./day10.rs"),
8888
11 => include_str!("./day11.rs"),
8989
12 => include_str!("./day12.rs"),
90-
// 13 => include_str!("./day13.rs"),
90+
13 => include_str!("./day13.rs"),
9191
// 14 => include_str!("./day14.rs"),
9292
// 15 => include_str!("./day15.rs"),
9393
// 16 => include_str!("./day16.rs"),

aoc-web/src/header.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub fn header(props: &HeaderProps) -> Html {
3131
<NavLink route={Route::Solution { year: 2024, day: 10 }} current={props.route.clone()} text={"10"}/>
3232
<NavLink route={Route::Solution { year: 2024, day: 11 }} current={props.route.clone()} text={"11"}/>
3333
<NavLink route={Route::Solution { year: 2024, day: 12 }} current={props.route.clone()} text={"12"}/>
34+
<NavLink route={Route::Solution { year: 2024, day: 13 }} current={props.route.clone()} text={"13"}/>
3435
</>
3536
}
3637
},

0 commit comments

Comments
 (0)