Skip to content

Commit a5ca72f

Browse files
committed
2024 day 13
1 parent af5b61a commit a5ca72f

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Button A: X+94, Y+34
2+
Button B: X+22, Y+67
3+
Prize: X=8400, Y=5400
4+
5+
Button A: X+26, Y+66
6+
Button B: X+67, Y+21
7+
Prize: X=12748, Y=12176
8+
9+
Button A: X+17, Y+86
10+
Button B: X+84, Y+37
11+
Prize: X=7870, Y=6450
12+
13+
Button A: X+69, Y+23
14+
Button B: X+27, Y+71
15+
Prize: X=18641, Y=10279

crates/year2024/src/day13.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use utils::point::Point2D;
2+
use utils::prelude::*;
3+
4+
/// Solving linear systems.
5+
#[derive(Clone, Debug)]
6+
pub struct Day13 {
7+
machines: Vec<Machine>,
8+
}
9+
10+
#[derive(Copy, Clone, Debug)]
11+
struct Machine {
12+
button_a: Point2D<u64>,
13+
button_b: Point2D<u64>,
14+
prize: Point2D<u64>,
15+
}
16+
17+
impl Day13 {
18+
pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
19+
Ok(Self {
20+
machines: parser::u64()
21+
.with_prefix("Button A: X+")
22+
.then(parser::u64().with_prefix(", Y+"))
23+
.then(parser::u64().with_prefix("\nButton B: X+"))
24+
.then(parser::u64().with_prefix(", Y+"))
25+
.then(parser::u64().with_prefix("\nPrize: X="))
26+
.then(parser::u64().with_prefix(", Y="))
27+
.with_suffix(parser::eol())
28+
.map_res(|(ax, ay, bx, by, px, py)| {
29+
let m = Machine {
30+
button_a: Point2D::new(ax, ay),
31+
button_b: Point2D::new(bx, by),
32+
prize: Point2D::new(px, py),
33+
};
34+
35+
// Check the two buttons are linear independent, meaning there is only one
36+
// solution for the linear equations.
37+
if det(m.button_a, m.button_b) == 0 {
38+
Err("expected buttons to be linearly independent")
39+
} else {
40+
Ok(m)
41+
}
42+
})
43+
.parse_lines(input)?,
44+
})
45+
}
46+
47+
#[must_use]
48+
pub fn part1(&self) -> u64 {
49+
self.machines
50+
.iter()
51+
.map(|m| m.required_tokens().unwrap_or(0))
52+
.sum()
53+
}
54+
55+
#[must_use]
56+
pub fn part2(&self) -> u64 {
57+
self.machines
58+
.iter()
59+
.map(|&(mut m)| {
60+
m.prize += Point2D::new(10000000000000, 10000000000000);
61+
m.required_tokens().unwrap_or(0)
62+
})
63+
.sum()
64+
}
65+
}
66+
67+
impl Machine {
68+
fn required_tokens(&self) -> Option<u64> {
69+
self.solve().map(|(a, b)| a * 3 + b)
70+
}
71+
72+
fn solve(&self) -> Option<(u64, u64)> {
73+
// https://en.wikipedia.org/wiki/Cramer%27s_rule#Explicit_formulas_for_small_systems
74+
let det_denominator = det(self.button_a, self.button_b);
75+
if det_denominator == 0 {
76+
return None;
77+
}
78+
79+
let det_a = det(self.prize, self.button_b);
80+
let det_b = det(self.button_a, self.prize);
81+
if det_a % det_denominator != 0 || det_b % det_denominator != 0 {
82+
return None;
83+
}
84+
85+
if let Ok(count_a) = (det_a / det_denominator).try_into() {
86+
if let Ok(count_b) = (det_b / det_denominator).try_into() {
87+
return Some((count_a, count_b));
88+
}
89+
}
90+
91+
None
92+
}
93+
}
94+
95+
fn det(a: Point2D<u64>, b: Point2D<u64>) -> i64 {
96+
(a.x as i64) * (b.y as i64) - (b.x as i64) * (a.y as i64)
97+
}
98+
99+
examples!(Day13 -> (u64, u64) [
100+
{file: "day13_example0.txt", part1: 480},
101+
]);

crates/year2024/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ utils::year!(2024 => year2024, ${
1414
10 => day10::Day10,
1515
11 => day11::Day11,
1616
12 => day12::Day12,
17+
13 => day13::Day13,
1718
});

0 commit comments

Comments
 (0)