diff --git a/day25/Cargo.lock b/day25/Cargo.lock new file mode 100644 index 0000000..6eb9cdb --- /dev/null +++ b/day25/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "day25" +version = "0.1.0" diff --git a/day25/Cargo.toml b/day25/Cargo.toml new file mode 100644 index 0000000..b830a9d --- /dev/null +++ b/day25/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day25" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/day25/input.txt b/day25/input.txt new file mode 100644 index 0000000..a9734a4 --- /dev/null +++ b/day25/input.txt @@ -0,0 +1,30 @@ +cpy a d +cpy 7 c +cpy 362 b +inc d +dec b +jnz b -2 +dec c +jnz c -5 +cpy d a +jnz 0 0 +cpy a b +cpy 0 a +cpy 2 c +jnz b 2 +jnz 1 6 +dec b +dec c +jnz c -4 +inc a +jnz 1 -7 +cpy 2 b +jnz c 2 +jnz 1 4 +dec b +dec c +jnz 1 -4 +jnz 0 0 +out b +jnz a -19 +jnz 1 -21 diff --git a/day25/src/main.rs b/day25/src/main.rs new file mode 100644 index 0000000..4d0f42d --- /dev/null +++ b/day25/src/main.rs @@ -0,0 +1,220 @@ +const FILENAME: &str = "input.txt"; + +fn main() { + let program: Vec<_> = std::fs::read_to_string(FILENAME) + .unwrap() + .lines() + .map(Instruction::parse) + .collect(); + + part1(program.clone()); +} + +fn part1(program: Vec) { + let mut a = 0; + let mut output = vec![]; + loop { + output.clear(); + + let mut registers = Registers::new(); + registers.set('a', &Value::Numeric(a)); + + let success = run_program(&mut program.clone(), &mut registers, &mut output); + + if success { + println!("{a}"); + break; + } + + a += 1; + } +} + +struct Registers { + registers: [isize; 4], +} + +impl Registers { + fn set(&mut self, register: char, value: &Value) { + let address = Self::get_address(register); + match value { + Value::Register(from_register) => { + let from_address = Self::get_address(*from_register); + self.registers[address] = self.registers[from_address]; + } + Value::Numeric(value) => self.registers[address] = *value, + } + } + + fn inc(&mut self, register: char) { + let address = Self::get_address(register); + self.registers[address] += 1; + } + + fn dec(&mut self, register: char) { + let address = Self::get_address(register); + self.registers[address] -= 1; + } + + fn is_nonzero(&self, value: &Value) -> bool { + match value { + Value::Register(register) => { + let address = Self::get_address(*register); + self.registers[address] != 0 + } + Value::Numeric(value) => *value != 0, + } + } + + fn get_address(register: char) -> usize { + match register { + 'a' => 0, + 'b' => 1, + 'c' => 2, + 'd' => 3, + _ => panic!("unknown register: {register}"), + } + } + + fn new() -> Self { + Self { registers: [0; 4] } + } +} + +#[derive(Clone, Debug)] +enum Instruction { + Cpy(Value, Value), + Inc(Value), + Dec(Value), + Jnz(Value, Value), + Tgl(Value), + Out(Value), +} + +#[derive(Clone, Copy, Debug)] +enum Value { + Register(char), + Numeric(isize), +} + +impl Value { + fn parse(s: &str) -> Self { + let first_char = s.chars().next().unwrap(); + if first_char.is_ascii_digit() || first_char == '-' { + Self::Numeric(s.parse().unwrap()) + } else { + Self::Register(first_char) + } + } +} + +impl Instruction { + fn parse(s: &str) -> Self { + let words: Vec<_> = s.split_whitespace().collect(); + match words[0] { + "cpy" => Self::Cpy(Value::parse(words[1]), Value::parse(words[2])), + "inc" => Self::Inc(Value::parse(words[1])), + "dec" => Self::Dec(Value::parse(words[1])), + "jnz" => Self::Jnz(Value::parse(words[1]), Value::parse(words[2])), + "tgl" => Self::Tgl(Value::parse(words[1])), + "out" => Self::Out(Value::parse(words[1])), + _ => panic!("unknown pattern: {s}"), + } + } +} + +fn run_program( + program: &mut [Instruction], + registers: &mut Registers, + output: &mut Vec, +) -> bool { + let mut pointer = 0; + + while let Some(instruction) = program.get(pointer) { + if output.len() == 100 { + return output + .iter() + .zip([0, 1].into_iter().cycle()) + .all(|(a, b)| *a == b); + } + + match instruction { + Instruction::Cpy(source, value) => { + match value { + Value::Register(r) => { + registers.set(*r, source); + } + Value::Numeric(_) => (), + } + pointer += 1; + } + Instruction::Inc(value) => { + match value { + Value::Register(r) => { + registers.inc(*r); + } + Value::Numeric(_) => (), + } + pointer += 1; + } + Instruction::Dec(value) => { + match value { + Value::Register(r) => { + registers.dec(*r); + } + Value::Numeric(_) => (), + } + pointer += 1; + } + Instruction::Jnz(register, value) => { + if registers.is_nonzero(register) { + match value { + Value::Register(r) => { + let val = registers.registers[Registers::get_address(*r)]; + pointer = ((pointer as isize) + val) as usize; + } + Value::Numeric(n) => { + pointer = ((pointer as isize) + *n) as usize; + } + } + } else { + pointer += 1; + } + } + Instruction::Tgl(value) => { + let val = match value { + Value::Register(r) => registers.registers[Registers::get_address(*r)], + Value::Numeric(n) => *n, + }; + let instruction = match program.get_mut((pointer as isize + val) as usize) { + Some(instruction) => instruction, + None => { + pointer += 1; + continue; + } + }; + *instruction = match instruction { + Instruction::Cpy(a, b) => Instruction::Jnz(*a, *b), + Instruction::Inc(a) => Instruction::Dec(*a), + Instruction::Dec(a) => Instruction::Inc(*a), + Instruction::Jnz(a, b) => Instruction::Cpy(*a, *b), + Instruction::Tgl(a) => Instruction::Inc(*a), + Instruction::Out(a) => Instruction::Inc(*a), + }; + pointer += 1; + } + Instruction::Out(value) => { + match value { + Value::Register(r) => { + output.push(registers.registers[Registers::get_address(*r)] as usize); + } + Value::Numeric(n) => { + output.push(*n as usize); + } + }; + pointer += 1; + } + } + } + false +}