1use utils::geometry::Vec2;
2use utils::prelude::*;
3
4#[derive(Clone, Debug)]
6pub struct Day13 {
7 machines: Vec<Machine>,
8}
9
10#[derive(Copy, Clone, Debug)]
11struct Machine {
12 button_a: Vec2<u64>,
13 button_b: Vec2<u64>,
14 prize: Vec2<u64>,
15}
16
17impl 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+").with_eol())
23 .then(parser::u64().with_prefix("Button B: X+"))
24 .then(parser::u64().with_prefix(", Y+").with_eol())
25 .then(parser::u64().with_prefix("Prize: X="))
26 .then(parser::u64().with_prefix(", Y=").with_eol())
27 .map_res(|(ax, ay, bx, by, px, py)| {
28 let m = Machine {
29 button_a: Vec2::new(ax, ay),
30 button_b: Vec2::new(bx, by),
31 prize: Vec2::new(px, py),
32 };
33
34 if det(m.button_a, m.button_b) == 0 {
37 Err("expected buttons to be linearly independent")
38 } else {
39 Ok(m)
40 }
41 })
42 .parse_lines(input)?,
43 })
44 }
45
46 #[must_use]
47 pub fn part1(&self) -> u64 {
48 self.machines
49 .iter()
50 .map(|m| m.required_tokens().unwrap_or(0))
51 .sum()
52 }
53
54 #[must_use]
55 pub fn part2(&self) -> u64 {
56 self.machines
57 .iter()
58 .map(|&(mut m)| {
59 m.prize += Vec2::new(10000000000000, 10000000000000);
60 m.required_tokens().unwrap_or(0)
61 })
62 .sum()
63 }
64}
65
66impl Machine {
67 fn required_tokens(&self) -> Option<u64> {
68 self.solve().map(|(a, b)| a * 3 + b)
69 }
70
71 fn solve(&self) -> Option<(u64, u64)> {
72 let det_denominator = det(self.button_a, self.button_b);
74 if det_denominator == 0 {
75 return None;
76 }
77
78 let det_a = det(self.prize, self.button_b);
79 let det_b = det(self.button_a, self.prize);
80 if det_a % det_denominator != 0 || det_b % det_denominator != 0 {
81 return None;
82 }
83
84 if let Ok(count_a) = (det_a / det_denominator).try_into()
85 && let Ok(count_b) = (det_b / det_denominator).try_into()
86 {
87 return Some((count_a, count_b));
88 }
89
90 None
91 }
92}
93
94fn det(a: Vec2<u64>, b: Vec2<u64>) -> i64 {
95 (a.x as i64) * (b.y as i64) - (b.x as i64) * (a.y as i64)
96}
97
98examples!(Day13 -> (u64, u64) [
99 {file: "day13_example0.txt", part1: 480},
100]);