From a7ceac9f3698e9a79bd2b8e090ecc23105dc6155 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Wed, 12 May 2021 19:46:48 +0100 Subject: [PATCH] Day 18 --- Cargo.lock | 4 + Cargo.toml | 1 + day_18/Cargo.toml | 9 + day_18/input.txt | 41 ++++ day_18/src/main.rs | 474 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 529 insertions(+) create mode 100644 day_18/Cargo.toml create mode 100644 day_18/input.txt create mode 100644 day_18/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 34f394b..307ad09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,6 +16,10 @@ version = "0.1.0" name = "day_16" version = "0.1.0" +[[package]] +name = "day_18" +version = "0.1.0" + [[package]] name = "day_2" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 645f01d..16fc473 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,5 +11,6 @@ members = [ "day_12", "day_15", "day_16", + "day_18", "day_23", ] diff --git a/day_18/Cargo.toml b/day_18/Cargo.toml new file mode 100644 index 0000000..134f242 --- /dev/null +++ b/day_18/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "day_18" +version = "0.1.0" +authors = ["Smaug123 "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/day_18/input.txt b/day_18/input.txt new file mode 100644 index 0000000..dd5f7cc --- /dev/null +++ b/day_18/input.txt @@ -0,0 +1,41 @@ +set i 31 +set a 1 +mul p 17 +jgz p p +mul a 2 +add i -1 +jgz i -2 +add a -1 +set i 127 +set p 618 +mul p 8505 +mod p a +mul p 129749 +add p 12345 +mod p a +set b p +mod b 10000 +snd b +add i -1 +jgz i -9 +jgz a 3 +rcv b +jgz b -1 +set f 0 +set i 126 +rcv a +rcv b +set p a +mul p -1 +add p b +jgz p 4 +snd a +set a b +jgz 1 3 +snd b +set f 1 +add i -1 +jgz i -11 +snd a +jgz f -16 +jgz a -19 diff --git a/day_18/src/main.rs b/day_18/src/main.rs new file mode 100644 index 0000000..93b28a0 --- /dev/null +++ b/day_18/src/main.rs @@ -0,0 +1,474 @@ +enum Instruction { + Set(char, i64), + SetTo(char, char), + Add(char, i64), + AddTo(char, char), + Mul(char, i64), + MulBy(char, char), + Mod(char, i64), + ModBy(char, char), + JgzRegVal(char, i64), + JgzValVal(i64, i64), + JgzRegReg(char, char), + JgzValReg(i64, char), + Snd(char), + SndExact(i64), + Rcv(char), +} + +fn parse(s: &str) -> Instruction { + let mut s = s.split_whitespace(); + match s.next().unwrap() { + "add" => { + let register = { + let mut chars = s.next().unwrap().chars(); + let result = chars.next().unwrap(); + match chars.next() { + Some(i) => panic!("Expected a register, got another char: {}", i), + None => result, + } + }; + let last = s.next().unwrap(); + match s.next() { + Some(i) => panic!("Expected no more tokens, got {}", i), + None => match last.parse::() { + Ok(i) => Instruction::Add(register, i), + Err(_) => { + let mut chars = last.chars(); + let target = chars.next().unwrap(); + match chars.next() { + Some(i) => panic!( + "Expected only a one-length register, got another char: {}", + i + ), + None => Instruction::AddTo(register, target), + } + } + }, + } + } + "set" => { + let register = { + let mut chars = s.next().unwrap().chars(); + let result = chars.next().unwrap(); + match chars.next() { + Some(i) => panic!("Expected a register, got another char: {}", i), + None => result, + } + }; + let last = s.next().unwrap(); + match s.next() { + Some(i) => panic!("Expected no more tokens, got {}", i), + None => match last.parse::() { + Ok(i) => Instruction::Set(register, i), + Err(_) => { + let mut chars = last.chars(); + let target = chars.next().unwrap(); + match chars.next() { + Some(i) => panic!( + "Expected only a one-length register, got another char: {}", + i + ), + None => Instruction::SetTo(register, target), + } + } + }, + } + } + "mul" => { + let register = { + let mut chars = s.next().unwrap().chars(); + let result = chars.next().unwrap(); + match chars.next() { + Some(i) => panic!("Expected a register, got another char: {}", i), + None => result, + } + }; + let last = s.next().unwrap(); + match s.next() { + Some(i) => panic!("Expected no more tokens, got {}", i), + None => match last.parse::() { + Ok(i) => Instruction::Mul(register, i), + Err(_) => { + let mut chars = last.chars(); + let target = chars.next().unwrap(); + match chars.next() { + Some(i) => panic!( + "Expected only a one-length register, got another char: {}", + i + ), + None => Instruction::MulBy(register, target), + } + } + }, + } + } + "mod" => { + let register = { + let mut chars = s.next().unwrap().chars(); + let result = chars.next().unwrap(); + match chars.next() { + Some(i) => panic!("Expected a register, got another char: {}", i), + None => result, + } + }; + let last = s.next().unwrap(); + match s.next() { + Some(i) => panic!("Expected no more tokens, got {}", i), + None => match last.parse::() { + Ok(i) => Instruction::Mod(register, i), + Err(_) => { + let mut chars = last.chars(); + let target = chars.next().unwrap(); + match chars.next() { + Some(i) => panic!( + "Expected only a one-length register, got another char: {}", + i + ), + None => Instruction::ModBy(register, target), + } + } + }, + } + } + "jgz" => { + let register = s.next().unwrap(); + let last = s.next().unwrap(); + if let Some(i) = s.next() { + panic!("Expected no more tokens, got {}", i); + } + match register.parse::() { + Ok(register) => match last.parse() { + Ok(last) => Instruction::JgzValVal(register, last), + Err(_) => { + let mut chars = last.chars(); + let last = chars.next().unwrap(); + if let Some(i) = chars.next() { + panic!( + "Expected a register name for last token, got another char: {}", + i + ); + } + Instruction::JgzValReg(register, last) + } + }, + Err(_) => { + let mut chars = register.chars(); + let result = chars.next().unwrap(); + if let Some(i) = chars.next() { + panic!("Expected a register, got another char: {}", i); + } + match last.parse::() { + Ok(last) => Instruction::JgzRegVal(result, last), + Err(_) => { + let mut chars = last.chars(); + let last = chars.next().unwrap(); + if let Some(i) = chars.next() { + panic!("Expected a register name for last, got extra: {}", i); + } + Instruction::JgzRegReg(result, last) + } + } + } + } + } + "snd" => { + let register = s.next().unwrap(); + if let Some(i) = s.next() { + panic!("Expected no more tokens, got {}", i); + } + match register.parse::() { + Ok(i) => Instruction::SndExact(i), + Err(_) => { + let mut chars = register.chars(); + let register = chars.next().unwrap(); + if let Some(i) = chars.next() { + panic!("Expected a single register, got another character: {}", i); + } + Instruction::Snd(register) + } + } + } + "rcv" => { + let register = s.next().unwrap(); + if let Some(i) = s.next() { + panic!("Expected no more tokens, got {}", i); + } + let mut chars = register.chars(); + let register = chars.next().unwrap(); + if let Some(i) = chars.next() { + panic!("Expected a single register, got another character: {}", i); + } + Instruction::Rcv(register) + } + instr => panic!("Unrecognised instruction: {}", instr), + } +} + +struct Machine<'a> { + program: &'a [Instruction], + pc: usize, + registers: &'a mut Vec, +} + +enum MachineState { + Send(i64), + Blocked, + Received(i64), +} + +struct Queue { + elts: Vec, + ptr: usize, +} + +fn new_queue() -> Queue { + Queue { + elts: vec![], + ptr: 1, + } +} + +// Basically an iterator, really. +fn advance<'a>(m: &mut Machine<'a>, receive_queue: &mut Queue) -> Option { + loop { + match m.program[m.pc] { + Instruction::Add(register, value) => { + m.registers[(register as u8 - b'a') as usize] += value; + m.pc += 1; + } + Instruction::AddTo(register, other) => { + m.registers[(register as u8 - b'a') as usize] += + m.registers[(other as u8 - b'a') as usize]; + m.pc += 1; + } + Instruction::Mul(register, value) => { + m.registers[(register as u8 - b'a') as usize] *= value; + m.pc += 1; + } + Instruction::MulBy(register, other) => { + m.registers[(register as u8 - b'a') as usize] *= + m.registers[(other as u8 - b'a') as usize]; + m.pc += 1; + } + Instruction::Mod(register, value) => { + m.registers[(register as u8 - b'a') as usize] %= value; + m.pc += 1; + } + Instruction::ModBy(register, other) => { + m.registers[(register as u8 - b'a') as usize] %= + m.registers[(other as u8 - b'a') as usize]; + m.pc += 1; + } + Instruction::Set(register, value) => { + m.registers[(register as u8 - b'a') as usize] = value; + m.pc += 1; + } + Instruction::SetTo(register, other) => { + m.registers[(register as u8 - b'a') as usize] = + m.registers[(other as u8 - b'a') as usize]; + m.pc += 1; + } + Instruction::JgzRegReg(register, offset) => { + if m.registers[(register as u8 - b'a') as usize] > 0 { + let dest = m.pc as i64 + m.registers[(offset as u8 - b'a') as usize]; + if dest >= m.program.len() as i64 || dest < 0 { + return None; + } else { + m.pc = dest as usize; + } + } else { + m.pc += 1; + } + } + Instruction::JgzValReg(cmp, offset) => { + if cmp > 0 { + let dest = m.pc as i64 + m.registers[(offset as u8 - b'a') as usize]; + if dest >= m.program.len() as i64 || dest < 0 { + return None; + } else { + m.pc = dest as usize; + } + } else { + m.pc += 1; + } + } + Instruction::JgzRegVal(register, value) => { + if m.registers[(register as u8 - b'a') as usize] > 0 { + let dest = m.pc as i64 + value; + if dest >= m.program.len() as i64 || dest < 0 { + return None; + } else { + m.pc = dest as usize; + } + } else { + m.pc += 1; + } + } + Instruction::JgzValVal(cmp, value) => { + if cmp > 0 { + let dest = m.pc as i64 + value; + if dest < 0 { + return None; + } else { + m.pc = dest as usize; + } + } else { + m.pc += 1; + } + } + Instruction::Snd(value) => { + m.pc += 1; + return Some(MachineState::Send( + m.registers[(value as u8 - b'a') as usize], + )); + } + Instruction::SndExact(value) => { + m.pc += 1; + return Some(MachineState::Send(value)); + } + Instruction::Rcv(value) => { + if receive_queue.ptr > receive_queue.elts.len() { + return Some(MachineState::Blocked); + } else { + let received = receive_queue.elts[receive_queue.ptr - 1]; + m.registers[(value as u8 - b'a') as usize] = received; + receive_queue.ptr += 1; + m.pc += 1; + return Some(MachineState::Received(received)); + } + } + } + if m.pc >= m.program.len() { + return None; + } + } +} + +fn input() -> Vec { + let input = include_str!("../input.txt"); + input.lines().map(|l| parse(l)).collect() +} + +fn part_1(instructions: &[Instruction]) -> i64 { + let mut machine = Machine { + pc: 0, + program: instructions, + registers: &mut vec![0; 26], + }; + let mut queue_1 = new_queue(); + while let Some(state) = advance(&mut machine, &mut queue_1) { + match state { + MachineState::Blocked => { + panic!("Shouldn't have deadlocked."); + } + MachineState::Send(i) => { + queue_1.elts.push(i); + } + MachineState::Received(_) => { + return *queue_1.elts.last().unwrap(); + } + } + } + panic!("Expected to send a value"); +} + +fn part_2(instructions: &[Instruction]) -> usize { + let mut machine0 = Machine { + pc: 0, + program: instructions, + registers: &mut vec![0; 26], + }; + let mut machine1 = Machine { + pc: 0, + program: instructions, + registers: &mut vec![0; 26], + }; + machine1.registers[(b'p' - b'a') as usize] = 1; + + let mut machine_0_done = false; + let mut queue_0 = new_queue(); + let mut queue_1 = new_queue(); + + // We need the number of times machine 1 sent a value; + // that is, the final length of queue 0. + 'machine1: while let Some(state) = advance(&mut machine1, &mut queue_1) { + match state { + MachineState::Blocked => { + let mut may_deadlock = true; + if machine_0_done { + // Deadlock! + return queue_0.elts.len(); + } else { + while let Some(state) = advance(&mut machine0, &mut queue_0) { + match state { + MachineState::Blocked => { + if may_deadlock { + return queue_0.elts.len(); + } else { + // Suspend machine 0. + continue 'machine1; + } + } + MachineState::Send(i) => { + // We no longer risk deadlock, because machine 1 has input again. + may_deadlock = false; + queue_1.elts.push(i); + } + MachineState::Received(_) => {} + } + } + machine_0_done = true; + } + } + MachineState::Send(i) => { + queue_0.elts.push(i); + } + MachineState::Received(_) => {} + } + } + queue_0.elts.len() +} + +fn main() { + let input = input(); + println!("part 1 => {}", part_1(&input)); + println!("part 2 => {}", part_2(&input)); +} + +#[cfg(test)] +mod tests { + use super::*; + + fn test_input() -> Vec<&'static str> { + vec![ + "set a 1", "add a 2", "mul a a", "mod a 5", "snd a", "set a 0", "rcv a", "jgz a -1", + "set a 1", "jgz a -2", + ] + } + + #[test] + fn part1_known() { + let input: Vec = test_input().iter().map(|i| parse(i)).collect(); + assert_eq!(part_1(&input), 4); + } + + fn test_input_2() -> Vec<&'static str> { + vec![ + "snd 1", "snd 2", "snd p", "rcv a", "rcv b", "rcv c", "rcv d", + ] + } + + #[test] + fn part2_known() { + let input: Vec = test_input_2().iter().map(|i| parse(i)).collect(); + assert_eq!(part_2(&input), 3) + } + + #[test] + fn test_day_18() { + let input = input(); + assert_eq!(part_1(&input), 3423); + assert_eq!(part_2(&input), 7493); + } +}