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)
}