🏡 index : ~doyle/aoc.git

use std::io::Read;

use nom::{
    bytes::complete::tag,
    character::complete::{alpha1, digit1},
    combinator::map_parser,
    multi::separated_list1,
    sequence::{preceded, terminated},
    IResult,
};

fn main() -> Result<(), anyhow::Error> {
    let mut input = String::new();
    std::io::stdin().read_to_string(&mut input)?;

    let (rest, input) = parse_input(&input).unwrap();
    assert!(rest.is_empty());

    part1(&input);
    part2(&input);

    Ok(())
}

fn solve(input: &Input) -> i64 {
    let b = (input.prize.y * input.buttons[0].x - input.prize.x * input.buttons[0].y)
        / (input.buttons[1].y * input.buttons[0].x - input.buttons[1].x * input.buttons[0].y);
    let a = (input.prize.x - b * input.buttons[1].x) / input.buttons[0].x;

    if input.buttons[0].x * a + input.buttons[1].x * b == input.prize.x
        && input.buttons[0].y * a + input.buttons[1].y * b == input.prize.y
    {
        3 * a + b
    } else {
        0
    }
}

fn part1(input: &[Input]) {
    eprintln!("{}", input.iter().map(solve).sum::<i64>());
}

fn part2(input: &[Input]) {
    eprintln!(
        "{}",
        input
            .iter()
            .cloned()
            .map(|v| Input {
                prize: Coords {
                    x: v.prize.x + 10_000_000_000_000,
                    y: v.prize.y + 10_000_000_000_000,
                },
                ..v
            })
            .map(|v| solve(&v))
            .sum::<i64>()
    );
}

#[derive(Debug, Copy, Clone)]
pub struct Coords {
    x: i64,
    y: i64,
}

#[derive(Debug, Clone)]
pub struct Input {
    buttons: Vec<Coords>,
    prize: Coords,
}

fn parse_i64(s: &str) -> IResult<&str, i64> {
    map_parser(digit1, nom::character::complete::i64)(s)
}

fn parse_button(s: &str) -> IResult<&str, Coords> {
    let (s, _b) = preceded(tag("Button "), alpha1)(s)?;
    let (s, x) = preceded(tag(": X+"), parse_i64)(s)?;
    let (s, y) = preceded(tag(", Y+"), parse_i64)(s)?;
    Ok((s, Coords { x, y }))
}

fn parse_prize(s: &str) -> IResult<&str, Coords> {
    let (s, x) = preceded(tag("Prize: X="), parse_i64)(s)?;
    let (s, y) = preceded(tag(", Y="), parse_i64)(s)?;
    Ok((s, Coords { x, y }))
}

fn parse_block(s: &str) -> IResult<&str, Input> {
    let (s, buttons) = terminated(separated_list1(tag("\n"), parse_button), tag("\n"))(s)?;
    let (s, prize) = terminated(parse_prize, tag("\n"))(s)?;
    Ok((s, Input { buttons, prize }))
}

fn parse_input(s: &'_ str) -> IResult<&'_ str, Vec<Input>> {
    separated_list1(tag("\n"), parse_block)(s)
}