From dd4d9a7f6f0c6a96ea067ce563f4f26d7bca61f4 Mon Sep 17 00:00:00 2001 From: Wesley Moore Date: Sat, 23 Dec 2017 22:30:10 +1100 Subject: [PATCH] Add day 18 part 2 solution --- 2017/day/18/problem.txt | 29 +++++++++++++ 2017/day/18/src/main.rs | 95 ++++++++++++++++++++++++++++++----------- 2 files changed, 98 insertions(+), 26 deletions(-) diff --git a/2017/day/18/problem.txt b/2017/day/18/problem.txt index 4b6eebe..c7dc976 100644 --- a/2017/day/18/problem.txt +++ b/2017/day/18/problem.txt @@ -40,3 +40,32 @@ At the time the recover operation is executed, the frequency of the last sound p What is the value of the recovered frequency (the value of the most recently played sound) the first time a rcv instruction is executed with a non-zero value? +--- Part Two --- + +As you congratulate yourself for a job well done, you notice that the documentation has been on the back of the tablet this entire time. While you actually got most of the instructions correct, there are a few key differences. This assembly code isn't about sound at all - it's meant to be run twice at the same time. + +Each running copy of the program has its own set of registers and follows the code independently - in fact, the programs don't even necessarily run at the same speed. To coordinate, they use the send (snd) and receive (rcv) instructions: + + snd X sends the value of X to the other program. These values wait in a queue until that program is ready to receive them. Each program has its own message queue, so a program can never receive a message it sent. + rcv X receives the next value and stores it in register X. If no values are in the queue, the program waits for a value to be sent to it. Programs do not continue to the next instruction until they have received a value. Values are received in the order they are sent. + +Each program also has its own program ID (one 0 and the other 1); the register p should begin with this value. + +For example: + +snd 1 +snd 2 +snd p +rcv a +rcv b +rcv c +rcv d + +Both programs begin by sending three values to the other. Program 0 sends 1, 2, 0; program 1 sends 1, 2, 1. Then, each program receives a value (both 1) and stores it in a, receives another value (both 2) and stores it in b, and then each receives the program ID of the other program (program 0 receives 1; program 1 receives 0) and stores it in c. Each program now sees a different value in its own copy of register c. + +Finally, both programs try to rcv a fourth time, but no data is waiting for either of them, and they reach a deadlock. When this happens, both programs terminate. + +It should be noted that it would be equally valid for the programs to run at different speeds; for example, program 0 might have sent all three values and then stopped at the first rcv before program 1 executed even its first instruction. + +Once both of your programs have terminated (regardless of what caused them to do so), how many times did program 1 send a value? + diff --git a/2017/day/18/src/main.rs b/2017/day/18/src/main.rs index 08c6c04..31f12a8 100644 --- a/2017/day/18/src/main.rs +++ b/2017/day/18/src/main.rs @@ -2,6 +2,10 @@ use std::fs::File; use std::io::{BufRead, BufReader}; use std::str::FromStr; use std::collections::HashMap; +use std::sync::mpsc::{channel, Sender, Receiver, RecvTimeoutError}; +use std::thread; +use std::sync::Arc; +use std::time::Duration; type Register = char; @@ -78,32 +82,36 @@ impl Program { // TODO: Machine executes programs and instructions #[derive(Debug)] struct Machine { + pid: usize, registers: HashMap, pc: isize, - last_played: isize, - last_received: isize, + send_count: usize, } impl Machine { - pub fn new() -> Machine { + pub fn new(pid: usize) -> Machine { + let mut registers = HashMap::new(); + registers.insert('p', pid as isize); + Machine { - registers: HashMap::new(), + pid, + registers, pc: 0, - last_played: 0, - last_received: 0, + send_count: 0, } } - pub fn run(&mut self, program: &Program) { + pub fn run(&mut self, program: &Program, send: Sender, recv: Receiver) { loop { if self.pc < 0 || self.pc >= program.0.len() as isize { + println!("pid {}: send_count = {}", self.pid, self.send_count); break; } let instruction = &program.0[self.pc as usize]; - self.execute(instruction); - - if self.last_received != 0 { + // println!("pid {}: {:?}", self.pid, instruction); + if let Err(_err) = self.execute(instruction, &send, &recv) { + println!("pid {}: send_count = {}", self.pid, self.send_count); break; } } @@ -117,15 +125,10 @@ impl Machine { self.registers.insert(register, value); } - fn execute(&mut self, instruction: &Instruction) { + fn execute(&mut self, instruction: &Instruction, send: &Sender, recv: &Receiver) -> Result<(), RecvTimeoutError> { use Instruction::*; - // TODO; Increment pc - match *instruction { - Snd(ref op) => { - self.last_played = op.value(self); - } Set(Operand::Register(reg), ref val) => { let value = val.value(self); self.set(reg, value); @@ -145,11 +148,16 @@ impl Machine { let value = val.value(self); self.set(reg, reg_value % value); } - Rcv(ref op) => { + Snd(ref op) => { let value = op.value(self); - if value != 0 { - self.last_received = self.last_played; - } + send.send(value).expect("error sending"); + self.send_count += 1; + // println!("pid {}: sent {}", self.pid, value); + } + Rcv(Operand::Register(reg)) => { + let value = recv.recv_timeout(Duration::from_secs(5))?; + // println!("pid {}: recv {}", self.pid, value); + self.set(reg, value); } Jgz(_, _) => { /* handled below */ } _ => panic!("bad argument") @@ -161,7 +169,7 @@ impl Machine { let x_value = x.value(self); let offset_value = offset.value(self); - if x_value != 0 { + if x_value > 0 { self.pc += offset_value; } else { @@ -170,14 +178,49 @@ impl Machine { } _ => self.pc += 1 }; + + Ok(()) } } -fn main() { - let program = Program::load("input"); - let mut machine = Machine::new(); +fn run(program: Program) { + let program = Arc::new(program); + let mut machine0 = Machine::new(0); + let mut machine1 = Machine::new(1); - machine.run(&program); + let (send0, recv1) = channel(); + let (send1, recv0) = channel(); - println!("{:?}", machine); + let program0 = Arc::clone(&program); + let thread0 = thread::spawn(move || { + machine0.run(&program0, send0, recv0); + }); + + let program1 = Arc::clone(&program); + let thread1 = thread::spawn(move || { + machine1.run(&program1, send1, recv1); + }); + + thread0.join().unwrap(); + thread1.join().unwrap(); +} + +fn main() { + run(Program::load("input")); +} + +#[test] +fn test_part_two() { + let input = r"snd 1 +snd 2 +snd p +rcv a +rcv b +rcv c +rcv d"; + + let program = input.lines() + .map(|line| Instruction::new(line.as_ref())) + .collect::>(); + run(Program(program)); }