diff --git a/input/24.txt b/input/24.txt new file mode 100644 index 0000000..4fda49e --- /dev/null +++ b/input/24.txt @@ -0,0 +1,252 @@ +inp w +mul x 0 +add x z +mod x 26 +div z 1 +add x 12 +eql x w +eql x 0 +mul y 0 +add y 25 +mul y x +add y 1 +mul z y +mul y 0 +add y w +add y 7 +mul y x +add z y +inp w +mul x 0 +add x z +mod x 26 +div z 1 +add x 13 +eql x w +eql x 0 +mul y 0 +add y 25 +mul y x +add y 1 +mul z y +mul y 0 +add y w +add y 8 +mul y x +add z y +inp w +mul x 0 +add x z +mod x 26 +div z 1 +add x 13 +eql x w +eql x 0 +mul y 0 +add y 25 +mul y x +add y 1 +mul z y +mul y 0 +add y w +add y 10 +mul y x +add z y +inp w +mul x 0 +add x z +mod x 26 +div z 26 +add x -2 +eql x w +eql x 0 +mul y 0 +add y 25 +mul y x +add y 1 +mul z y +mul y 0 +add y w +add y 4 +mul y x +add z y +inp w +mul x 0 +add x z +mod x 26 +div z 26 +add x -10 +eql x w +eql x 0 +mul y 0 +add y 25 +mul y x +add y 1 +mul z y +mul y 0 +add y w +add y 4 +mul y x +add z y +inp w +mul x 0 +add x z +mod x 26 +div z 1 +add x 13 +eql x w +eql x 0 +mul y 0 +add y 25 +mul y x +add y 1 +mul z y +mul y 0 +add y w +add y 6 +mul y x +add z y +inp w +mul x 0 +add x z +mod x 26 +div z 26 +add x -14 +eql x w +eql x 0 +mul y 0 +add y 25 +mul y x +add y 1 +mul z y +mul y 0 +add y w +add y 11 +mul y x +add z y +inp w +mul x 0 +add x z +mod x 26 +div z 26 +add x -5 +eql x w +eql x 0 +mul y 0 +add y 25 +mul y x +add y 1 +mul z y +mul y 0 +add y w +add y 13 +mul y x +add z y +inp w +mul x 0 +add x z +mod x 26 +div z 1 +add x 15 +eql x w +eql x 0 +mul y 0 +add y 25 +mul y x +add y 1 +mul z y +mul y 0 +add y w +add y 1 +mul y x +add z y +inp w +mul x 0 +add x z +mod x 26 +div z 1 +add x 15 +eql x w +eql x 0 +mul y 0 +add y 25 +mul y x +add y 1 +mul z y +mul y 0 +add y w +add y 8 +mul y x +add z y +inp w +mul x 0 +add x z +mod x 26 +div z 26 +add x -14 +eql x w +eql x 0 +mul y 0 +add y 25 +mul y x +add y 1 +mul z y +mul y 0 +add y w +add y 4 +mul y x +add z y +inp w +mul x 0 +add x z +mod x 26 +div z 1 +add x 10 +eql x w +eql x 0 +mul y 0 +add y 25 +mul y x +add y 1 +mul z y +mul y 0 +add y w +add y 13 +mul y x +add z y +inp w +mul x 0 +add x z +mod x 26 +div z 26 +add x -14 +eql x w +eql x 0 +mul y 0 +add y 25 +mul y x +add y 1 +mul z y +mul y 0 +add y w +add y 4 +mul y x +add z y +inp w +mul x 0 +add x z +mod x 26 +div z 26 +add x -5 +eql x w +eql x 0 +mul y 0 +add y 25 +mul y x +add y 1 +mul z y +mul y 0 +add y w +add y 14 +mul y x +add z y diff --git a/src/day24.rs b/src/day24.rs new file mode 100644 index 0000000..154cbe5 --- /dev/null +++ b/src/day24.rs @@ -0,0 +1,269 @@ +// Beautiful explanation: https://github.com/dphilipson/advent-of-code-2021/blob/master/src/days/day24.rs + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Register { + X, Y, Z, W +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Operand { + Register(Register), + Number(i32) +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Instruction { + Input(Register), + Add(Register, Operand), + Multiply(Register, Operand), + Divide(Register, Operand), + Modulo(Register, Operand), + Equal(Register, Operand) +} + +#[allow(clippy::upper_case_acronyms)] +pub struct CPU { + rx: i32, + ry: i32, + rz: i32, + rw: i32, + input: Vec +} + +impl CPU { + fn new() -> CPU { + CPU { rx: 0, ry: 0, rz: 0, rw: 0, input: vec![] } + } + + fn reg(&mut self, reg: Register) -> &mut i32 { + match reg { + Register::X => &mut self.rx, + Register::Y => &mut self.ry, + Register::Z => &mut self.rz, + Register::W => &mut self.rw, + } + } + + fn op(&mut self, op: Operand) -> i32 { + match op { + Operand::Register(reg) => *self.reg(reg), + Operand::Number(num) => num, + } + } + + fn input(&mut self, reg: Register, num: i32) { + *self.reg(reg) = num; + } + + fn add(&mut self, reg: Register, op: Operand) { + *self.reg(reg) += self.op(op); + } + + fn multiply(&mut self, reg: Register, op: Operand) { + *self.reg(reg) *= self.op(op); + } + + fn divide(&mut self, reg: Register, op: Operand) { + *self.reg(reg) /= self.op(op); + } + + fn modulo(&mut self, reg: Register, op: Operand) { + *self.reg(reg) %= self.op(op); + } + + fn equal(&mut self, reg: Register, op: Operand) { + let b = self.op(op); + let a = self.reg(reg); + *a = if *a == b { 1 } else { 0 }; + } + + fn run(&mut self, inst: &Instruction) { + use Instruction::*; + match *inst { + Input(a) => { + let b = self.input.pop().expect("Missing input"); + self.input(a, b) + }, + Add(a, b) => self.add(a, b), + Multiply(a, b) => self.multiply(a, b), + Divide(a, b) => self.divide(a, b), + Modulo(a, b) => self.modulo(a, b), + Equal(a, b) => self.equal(a, b), + } + } +} + +fn parse_register(input: &str) -> Register { + match input { + "x" => Register::X, + "y" => Register::Y, + "z" => Register::Z, + "w" => Register::W, + _ => panic!("Unknown variable '{}'", input) + } +} + +fn parse_operand(input: &str) -> Operand { + if let Ok(number) = input.parse::() { + Operand::Number(number) + } else { + Operand::Register(parse_register(input)) + } +} + +pub fn parse_input(input: &str) -> Vec { + let mut instructions = vec![]; + for line in input.lines() { + if line.is_empty() { continue; } + let parts = line.split(' ').collect::>(); + let opcode = *parts.get(0).expect("Missing opcode"); + let instruction = match opcode { + "inp" => { + let op1 = parts.get(1).expect("Missing variable"); + Instruction::Input(parse_register(op1)) + }, + "add" => { + let op1 = parts.get(1).expect("Missing variable"); + let op2 = parts.get(2).expect("Missing operand"); + Instruction::Add(parse_register(op1), parse_operand(op2)) + }, + "mul" => { + let op1 = parts.get(1).expect("Missing variable"); + let op2 = parts.get(2).expect("Missing operand"); + Instruction::Multiply(parse_register(op1), parse_operand(op2)) + } + "div" => { + let op1 = parts.get(1).expect("Missing variable"); + let op2 = parts.get(2).expect("Missing operand"); + Instruction::Divide(parse_register(op1), parse_operand(op2)) + }, + "mod" => { + let op1 = parts.get(1).expect("Missing variable"); + let op2 = parts.get(2).expect("Missing operand"); + Instruction::Modulo(parse_register(op1), parse_operand(op2)) + } + "eql" => { + let op1 = parts.get(1).expect("Missing variable"); + let op2 = parts.get(2).expect("Missing operand"); + Instruction::Equal(parse_register(op1), parse_operand(op2)) + }, + _ => panic!("Unexpected opcode '{}'", opcode) + }; + instructions.push(instruction); + } + instructions +} + +fn check(instructions: &[Instruction], monad: &[u8]) -> bool { + let mut cpu = CPU::new(); + cpu.input = vec![]; + for num in monad { + cpu.input.push(*num as i32); + } + cpu.input.reverse(); + for inst in instructions { + cpu.run(inst); + } + cpu.rz == 0 +} + +fn analyze(instructions: &[Instruction]) -> Vec<(i32, i32, i32)> { + let mut result = vec![]; + for (i, _) in instructions.iter().enumerate() + .filter(|(_, inst)| **inst == Instruction::Input(Register::W)) { + let a = match instructions.get(i+4) { + Some(Instruction::Divide(_, Operand::Number(num))) => *num, + _ => panic!() + }; + + let b = match instructions.get(i+5) { + Some(Instruction::Add(_, Operand::Number(num))) => *num, + _ => panic!() + }; + + let c = match instructions.get(i+15) { + Some(Instruction::Add(_, Operand::Number(num))) => *num, + _ => panic!() + }; + + result.push((a, b, c)); + } + result +} + +fn analyze_requirements(instructions: &[Instruction]) -> Vec<(u8, u8, i8)> { + let mut requirements = vec![]; + let result = analyze(instructions); + let mut stack = vec![]; + for (i, (_, check, offset)) in result.iter().enumerate() { + if *check > 0 { + stack.push((i, offset)) + } else { + let top = stack.pop().unwrap(); + requirements.push((i as u8, top.0 as u8, (*check + top.1) as i8)); + } + } + requirements +} + +fn concat_nums(nums: &[u8]) -> u64 { + let mut answer = 0u64; + for num in nums { + answer *= 10; + answer += *num as u64; + } + answer +} + +pub fn part1(instructions: Vec) -> u64 { + let mut monad = [9u8; 14]; + for (i, j, offset) in analyze_requirements(&instructions) { + if offset > 0 { + monad[j as usize] = (9 - offset) as u8; + } else { + monad[i as usize] = (9 + offset) as u8; + } + } + + assert!(check(&instructions, &monad)); + + concat_nums(&monad) +} + +pub fn part2(instructions: Vec) -> u64 { + let mut monad = [1u8; 14]; + for (i, j, offset) in analyze_requirements(&instructions) { + if offset > 0 { + monad[i as usize] = (1 + offset) as u8; + } else { + monad[j as usize] = (1 - offset) as u8; + } + } + + assert!(check(&instructions, &monad)); + + concat_nums(&monad) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn input_parsing() { + use Instruction::*; + use Register::*; + let instructions = parse_input(&[ + "inp z", + "inp x", + "mul z 3", + "eql z x", + ].join("\n")); + assert_eq!(instructions, vec![ + Input(Z), + Input(X), + Multiply(Z, Operand::Number(3)), + Equal(Z, Operand::Register(X)) + ]) + } +} diff --git a/src/main.rs b/src/main.rs index f84f6ad..ca64256 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ mod day20; mod day21; mod day22; mod day23; +mod day24; mod day3; mod day4; mod day5; @@ -126,6 +127,8 @@ fn run(day: i32, part: i32, input_filename: &str) { "22.2" => println!("{}", day22::part2(day22::parse_input(&contents))), "23.1" => println!("{}", day23::part1(day23::parse_input(&contents))), "23.2" => println!("{}", day23::part2(day23::parse_input(&contents))), + "24.1" => println!("{}", day24::part1(day24::parse_input(&contents))), + "24.2" => println!("{}", day24::part2(day24::parse_input(&contents))), _ => println!("Day {} part {} not found", day, part), } }