aoc2024/src/day13.rs
Keenan Tims d88f907c03
Some checks failed
test / AoC 2024 (push) Failing after 1m14s
clippies
2024-12-13 17:55:28 -08:00

136 lines
3.8 KiB
Rust

use aoc_runner_derive::aoc;
use itertools::Itertools;
use regex::Regex;
use std::str::Lines;
#[derive(Debug, Clone)]
pub struct MachineAction(i64, i64);
#[derive(Debug, Clone)]
struct ClawMachine {
button_a: MachineAction,
button_b: MachineAction,
prize: (i64, i64),
}
impl ClawMachine {
fn consume_from_input(input: &mut Lines) -> Option<Self> {
// consume any empty lines at the front
let ofs_re = Regex::new(r"X([+-]\d+), Y([+-]\d+)").unwrap();
let prize_re = Regex::new(r"X=(\d+), Y=(\d+)").unwrap();
// consume 3 lines - a, b, prize
if let Some((a_line, b_line, prize_line)) = input.filter(|l| !l.is_empty()).take(3).collect_tuple() {
let a_caps = ofs_re.captures(a_line).unwrap();
let b_caps = ofs_re.captures(b_line).unwrap();
let prize_caps = prize_re.captures(prize_line).unwrap();
let button_a = MachineAction(
a_caps.get(1).unwrap().as_str().parse().unwrap(),
a_caps.get(2).unwrap().as_str().parse().unwrap(),
);
let button_b = MachineAction(
b_caps.get(1).unwrap().as_str().parse().unwrap(),
b_caps.get(2).unwrap().as_str().parse().unwrap(),
);
let prize = (
prize_caps.get(1).unwrap().as_str().parse().unwrap(),
prize_caps.get(2).unwrap().as_str().parse().unwrap(),
);
Some(Self {
button_a,
button_b,
prize,
})
} else {
None
}
}
fn cost(moves: (i64, i64)) -> i64 {
moves.0 * 3 + moves.1
}
fn cheapest_prize(&self) -> Option<i64> {
let remainder_a = (self.button_b.0 * self.prize.1 - self.button_b.1 * self.prize.0)
% (self.button_b.0 * self.button_a.1 - self.button_a.0 * self.button_b.1);
let a = (self.button_b.0 * self.prize.1 - self.button_b.1 * self.prize.0)
/ (self.button_b.0 * self.button_a.1 - self.button_a.0 * self.button_b.1);
let remainder_b = (self.prize.0 - (self.button_a.0 * a)) % self.button_b.0;
let b = (self.prize.0 - (self.button_a.0 * a)) / self.button_b.0;
if remainder_a == 0 && remainder_b == 0 {
Some(Self::cost((a, b)))
} else {
None
}
}
fn offset(&mut self, offset: i64) {
self.prize = (self.prize.0 + offset, self.prize.1 + offset)
}
}
#[derive(Debug, Clone)]
struct ClawMachines {
machines: Vec<ClawMachine>,
}
impl From<&str> for ClawMachines {
fn from(input: &str) -> Self {
let mut machines = Vec::new();
let mut lines = input.lines();
while let Some(machine) = ClawMachine::consume_from_input(&mut lines) {
machines.push(machine);
}
Self { machines }
}
}
fn parse(input: &str) -> ClawMachines {
ClawMachines::from(input)
}
#[aoc(day13, part1)]
fn part1(input: &str) -> i64 {
let machines = parse(input);
machines.machines.iter().filter_map(|m| m.cheapest_prize()).sum()
}
#[aoc(day13, part2)]
fn part2(input: &str) -> i64 {
let mut machines = parse(input);
machines
.machines
.iter_mut()
.filter_map(|m| {
m.offset(10000000000000);
m.cheapest_prize()
})
.sum()
}
#[cfg(test)]
mod tests {
use super::*;
const EXAMPLE: &str = "Button A: X+94, Y+34
Button B: X+22, Y+67
Prize: X=8400, Y=5400
Button A: X+26, Y+66
Button B: X+67, Y+21
Prize: X=12748, Y=12176
Button A: X+17, Y+86
Button B: X+84, Y+37
Prize: X=7870, Y=6450
Button A: X+69, Y+23
Button B: X+27, Y+71
Prize: X=18641, Y=10279";
#[test]
fn part1_example() {
assert_eq!(part1(EXAMPLE), 480);
}
#[test]
fn part2_example() {
assert_eq!(part2(EXAMPLE), 875318608908);
}
}