use itertools::Itertools; use pest::{ iterators::{Pair, Pairs}, Parser, }; const OFFSETS: (u64, u64) = (10000000000000, 10000000000000); pub fn day13() { let cost: f64 = parse_machines(&crate::input(13)) .iter() .filter_map(|machine| { let (c, d) = machine.prize; let (a, b) = &machine.buttons; let bn = (d - c * a.y / a.x) / (b.y - b.x * a.y / a.x); let an = (c - bn * b.x) / a.x; // Both solutions must be whole numbers let winnable = [an, bn] .iter() .all(|&n| n >= -0.01 && (n.round() - n).abs() <= 0.01); if winnable { Some(an.round() * a.cost + bn.round() * b.cost) } else { None } }) .sum(); println!("Minimum Cost: {:?}", cost); } struct Machine { buttons: (Button, Button), prize: (f64, f64), } struct Button { cost: f64, x: f64, y: f64, } #[derive(pest_derive::Parser)] #[grammar = "grammars/day13.pest"] struct MachinesParser {} fn parse_machines(input: &str) -> Vec { MachinesParser::parse(Rule::machines, input) .unwrap() .nth(0) .unwrap() .into_inner() .map(Pair::<'_, Rule>::into_inner) .map(Machine::parse) .collect() } impl Machine { fn parse(input: Pairs<'_, Rule>) -> Self { let (a, b, prize) = input.map(|p| p.into_inner()).collect_tuple().unwrap(); let buttons = [a, b] .into_iter() .map(Button::parse) .collect_tuple() .unwrap(); let prize = prize .map(|n| n.as_str().parse::().unwrap()) .collect_tuple() .map(|(x, y)| ((x + OFFSETS.0) as f64, (y + OFFSETS.1) as f64)) .unwrap(); Self { buttons, prize } } } impl Button { fn parse(input: Pairs<'_, Rule>) -> Self { let (t, x, y) = input.collect_tuple().unwrap(); let cost = match t.as_str() { "A" => 3., "B" => 1., _ => unreachable!(), }; Self { x: x.as_str().parse::().unwrap(), y: y.as_str().parse::().unwrap(), cost, } } }