mirror of
https://github.com/wezm/advent-of-code.git
synced 2024-12-18 10:19:55 +00:00
Add day 18 part 2 solution
This commit is contained in:
parent
3e43a1c38f
commit
dd4d9a7f6f
2 changed files with 98 additions and 26 deletions
|
@ -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?
|
||||
|
||||
|
|
|
@ -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<char, isize>,
|
||||
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<isize>, recv: Receiver<isize>) {
|
||||
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<isize>, recv: &Receiver<isize>) -> 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::<Vec<Instruction>>();
|
||||
run(Program(program));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue