DEVELOPMENT ENVIRONMENT

~liljamo/aoc2024

6d04837a924cc606e7970e02877834fc5b8d70ce — Jonni Liljamo a month ago d978ab7 master
feat: day17 partly

* part two has non-viable bruteforcing going on, i'll figure it out
later
4 files changed, 264 insertions(+), 0 deletions(-)

A input/day17/example
A input/day17/example2
A src/day17/mod.rs
M src/main.rs
A input/day17/example => input/day17/example +5 -0
@@ 0,0 1,5 @@
Register A: 729
Register B: 0
Register C: 0

Program: 0,1,5,4,3,0

A input/day17/example2 => input/day17/example2 +5 -0
@@ 0,0 1,5 @@
Register A: 2024
Register B: 0
Register C: 0

Program: 0,3,5,4,3,0

A src/day17/mod.rs => src/day17/mod.rs +246 -0
@@ 0,0 1,246 @@
use std::{
    fs::File,
    io::{BufRead, BufReader},
    path::Path,
    str::FromStr,
};

pub fn solve(input: &Path) -> anyhow::Result<()> {
    println!("part one: {}", part_one(input)?);
    println!("part two: {}", part_two(input)?);

    Ok(())
}

#[derive(Debug)]
enum ProgramPart {
    Instruction(Ins),
    LiteralOperand(usize),
}

#[derive(Debug)]
enum Ins {
    Adv,
    Bxl,
    Bst,
    Jnz,
    Bxc,
    Out,
    Bdv,
    Cdv,
}

impl FromStr for Ins {
    type Err = Box<dyn std::error::Error>;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "0" => Ok(Self::Adv),
            "1" => Ok(Self::Bxl),
            "2" => Ok(Self::Bst),
            "3" => Ok(Self::Jnz),
            "4" => Ok(Self::Bxc),
            "5" => Ok(Self::Out),
            "6" => Ok(Self::Bdv),
            "7" => Ok(Self::Cdv),
            _ => unreachable!(),
        }
    }
}

#[derive(Debug)]
enum Copr {
    Literal(usize),
    RA,
    RB,
    RC,
}

impl Copr {
    fn from_usize(n: usize) -> Self {
        match n {
            0 => Self::Literal(0),
            1 => Self::Literal(1),
            2 => Self::Literal(2),
            3 => Self::Literal(3),
            4 => Self::RA,
            5 => Self::RB,
            6 => Self::RC,
            _ => unreachable!(),
        }
    }

    fn real(&self, ra: i32, rb: i32, rc: i32) -> i32 {
        match self {
            Self::Literal(n) => *n as i32,
            Self::RA => ra,
            Self::RB => rb,
            Self::RC => rc,
        }
    }
}

fn part_one(input: &Path) -> anyhow::Result<String> {
    let reader = BufReader::new(File::open(input)?);

    let mut ra: i32 = 0;
    let mut rb: i32 = 0;
    let mut rc: i32 = 0;
    let mut program: Vec<ProgramPart> = vec![];
    for line in reader.lines() {
        let line = line?;
        if line.starts_with("Register A") {
            ra = line.split_whitespace().last().unwrap().parse()?;
        } else if line.starts_with("Register B") {
            rb = line.split_whitespace().last().unwrap().parse()?;
        } else if line.starts_with("Register C") {
            rc = line.split_whitespace().last().unwrap().parse()?;
        } else if line.starts_with("Program") {
            program.append(
                &mut line
                    .split_whitespace()
                    .last()
                    .unwrap()
                    .split(",")
                    .enumerate()
                    .filter_map(|(i, c)| {
                        if c == "," {
                            None
                        } else if i % 2 == 0 {
                            Some(ProgramPart::Instruction(c.parse().unwrap()))
                        } else {
                            Some(ProgramPart::LiteralOperand(c.parse().unwrap()))
                        }
                    })
                    .collect::<Vec<_>>(),
            );
        }
    }

    let mut out: Vec<i32> = vec![];
    let mut ptr = 0;
    while ptr < program.len() {
        let mut incr = true;

        if let ProgramPart::Instruction(ins) = &program[ptr] {
            if let ProgramPart::LiteralOperand(lit_opr) = &program[ptr + 1] {
                let combo_opr = Copr::from_usize(*lit_opr).real(ra, rb, rc);
                match &ins {
                    Ins::Adv => ra /= 2_i32.pow(combo_opr as u32),
                    Ins::Bxl => rb ^= *lit_opr as i32,
                    Ins::Bst => rb = combo_opr % 8,
                    Ins::Jnz => {
                        if ra != 0 {
                            ptr = *lit_opr;
                            incr = false;
                        }
                    }
                    Ins::Bxc => rb ^= rc,
                    Ins::Out => out.push(combo_opr % 8),
                    Ins::Bdv => rb = ra / 2_i32.pow(combo_opr as u32),
                    Ins::Cdv => rc = ra / 2_i32.pow(combo_opr as u32),
                }
            } else {
                unreachable!();
            }
        } else {
            unreachable!();
        }

        if incr {
            ptr += 2;
        }
    }

    Ok(out
        .iter()
        .map(|o| o.to_string())
        .collect::<Vec<_>>()
        .join(","))
}

fn part_two(input: &Path) -> anyhow::Result<i32> {
    let reader = BufReader::new(File::open(input)?);

    let mut original_program = String::new();
    let mut program: Vec<ProgramPart> = vec![];
    for line in reader.lines() {
        let line = line?;
        if line.starts_with("Program") {
            original_program = line.split_whitespace().last().unwrap().into();
            program = original_program
                .clone()
                .split(",")
                .enumerate()
                .filter_map(|(i, c)| {
                    if c == "," {
                        None
                    } else if i % 2 == 0 {
                        Some(ProgramPart::Instruction(c.parse().unwrap()))
                    } else {
                        Some(ProgramPart::LiteralOperand(c.parse().unwrap()))
                    }
                })
                .collect::<Vec<_>>();
        }
    }

    let mut ra_init = 8;
    let mut ra: i32;
    let mut rb: i32;
    let mut rc: i32;
    loop {
        ra_init += 1;
        ra = ra_init;
        rb = 0;
        rc = 0;

        println!("trying {}", ra_init);

        let mut out: Vec<i32> = vec![];
        let mut ptr = 0;
        while ptr < program.len() {
            let mut incr = true;

            if let ProgramPart::Instruction(ins) = &program[ptr] {
                if let ProgramPart::LiteralOperand(lit_opr) = &program[ptr + 1] {
                    let combo_opr = Copr::from_usize(*lit_opr).real(ra, rb, rc);
                    match &ins {
                        Ins::Adv => ra /= 2_i32.pow(combo_opr as u32),
                        Ins::Bxl => rb ^= *lit_opr as i32,
                        Ins::Bst => rb = combo_opr % 8,
                        Ins::Jnz => {
                            if ra != 0 {
                                ptr = *lit_opr;
                                incr = false;
                            }
                        }
                        Ins::Bxc => rb ^= rc,
                        Ins::Out => out.push(combo_opr % 8),
                        Ins::Bdv => rb = ra / 2_i32.pow(combo_opr as u32),
                        Ins::Cdv => rc = ra / 2_i32.pow(combo_opr as u32),
                    }
                } else {
                    unreachable!();
                }
            } else {
                unreachable!();
            }

            if incr {
                ptr += 2;
            }
        }

        let out_str = out
            .iter()
            .map(|o| o.to_string())
            .collect::<Vec<_>>()
            .join(",");

        if out_str == original_program {
            break;
        }
    }

    Ok(ra_init)
}

M src/main.rs => src/main.rs +8 -0
@@ 9,6 9,7 @@ mod day12;
mod day13;
mod day14;
mod day15;
mod day17;
mod day2;
mod day3;
mod day4;


@@ 86,6 87,10 @@ enum DayArgs {
        #[arg(short)]
        input: PathBuf,
    },
    Day17 {
        #[arg(short)]
        input: PathBuf,
    },
}

fn main() -> anyhow::Result<()> {


@@ 137,6 142,9 @@ fn main() -> anyhow::Result<()> {
        DayArgs::Day15 { input } => {
            day15::solve(&input)?;
        }
        DayArgs::Day17 { input } => {
            day17::solve(&input)?;
        }
    }

    Ok(())