2017-12-23 08:45:08 +00:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io::{BufRead, BufReader};
|
|
|
|
use std::str::FromStr;
|
|
|
|
use std::collections::HashMap;
|
2017-12-23 11:30:10 +00:00
|
|
|
use std::sync::mpsc::{channel, Sender, Receiver, RecvTimeoutError};
|
|
|
|
use std::thread;
|
|
|
|
use std::sync::Arc;
|
|
|
|
use std::time::Duration;
|
2017-12-23 08:45:08 +00:00
|
|
|
|
|
|
|
type Register = char;
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum Operand {
|
|
|
|
Register(Register),
|
|
|
|
Immediate(isize),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Operand {
|
|
|
|
fn new(register_or_value: &str) -> Self {
|
|
|
|
if let Ok(value) = isize::from_str(register_or_value) {
|
|
|
|
Operand::Immediate(value)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Operand::Register(register_or_value.chars().next().unwrap())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn value(&self, machine: &Machine) -> isize {
|
|
|
|
match *self {
|
|
|
|
Operand::Register(ref reg) => machine.registers.get(reg).map(|val| *val).unwrap_or(0),
|
|
|
|
Operand::Immediate(val) => val,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum Instruction {
|
|
|
|
Snd(Operand),
|
|
|
|
Set(Operand, Operand),
|
|
|
|
Add(Operand, Operand),
|
|
|
|
Mul(Operand, Operand),
|
|
|
|
Mod(Operand, Operand),
|
|
|
|
Rcv(Operand),
|
|
|
|
Jgz(Operand, Operand),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Instruction {
|
|
|
|
fn new(line: &str) -> Self {
|
|
|
|
use Instruction::*;
|
|
|
|
|
|
|
|
let asm = line.split_whitespace().collect::<Vec<&str>>();
|
|
|
|
let instruction = asm[0];
|
|
|
|
|
|
|
|
match instruction {
|
|
|
|
"snd" => Snd(Operand::new(asm[1])),
|
|
|
|
"set" => Set(Operand::new(asm[1]), Operand::new(asm[2])),
|
|
|
|
"add" => Add(Operand::new(asm[1]), Operand::new(asm[2])),
|
|
|
|
"mul" => Mul(Operand::new(asm[1]), Operand::new(asm[2])),
|
|
|
|
"mod" => Mod(Operand::new(asm[1]), Operand::new(asm[2])),
|
|
|
|
"rcv" => Rcv(Operand::new(asm[1])),
|
|
|
|
"jgz" => Jgz(Operand::new(asm[1]), Operand::new(asm[2])),
|
|
|
|
_ => panic!("bad instruction")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Program(Vec<Instruction>);
|
|
|
|
|
|
|
|
impl Program {
|
|
|
|
fn load(path: &str) -> Program {
|
|
|
|
let file = File::open(path).expect("unable to open input file");
|
|
|
|
let reader = BufReader::new(file);
|
|
|
|
|
|
|
|
let program = reader.lines()
|
|
|
|
.map(|line| Instruction::new(line.as_ref().unwrap()))
|
|
|
|
.collect::<Vec<Instruction>>();
|
|
|
|
Program(program)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct Machine {
|
2017-12-23 11:30:10 +00:00
|
|
|
pid: usize,
|
2017-12-23 08:45:08 +00:00
|
|
|
registers: HashMap<char, isize>,
|
|
|
|
pc: isize,
|
2017-12-23 11:30:10 +00:00
|
|
|
send_count: usize,
|
2017-12-23 08:45:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Machine {
|
2017-12-23 11:30:10 +00:00
|
|
|
pub fn new(pid: usize) -> Machine {
|
|
|
|
let mut registers = HashMap::new();
|
|
|
|
registers.insert('p', pid as isize);
|
|
|
|
|
2017-12-23 08:45:08 +00:00
|
|
|
Machine {
|
2017-12-23 11:30:10 +00:00
|
|
|
pid,
|
|
|
|
registers,
|
2017-12-23 08:45:08 +00:00
|
|
|
pc: 0,
|
2017-12-23 11:30:10 +00:00
|
|
|
send_count: 0,
|
2017-12-23 08:45:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-23 11:30:10 +00:00
|
|
|
pub fn run(&mut self, program: &Program, send: Sender<isize>, recv: Receiver<isize>) {
|
2017-12-23 08:45:08 +00:00
|
|
|
loop {
|
|
|
|
if self.pc < 0 || self.pc >= program.0.len() as isize {
|
2017-12-23 11:30:10 +00:00
|
|
|
println!("pid {}: send_count = {}", self.pid, self.send_count);
|
2017-12-23 08:45:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
let instruction = &program.0[self.pc as usize];
|
2017-12-23 11:30:10 +00:00
|
|
|
// println!("pid {}: {:?}", self.pid, instruction);
|
|
|
|
if let Err(_err) = self.execute(instruction, &send, &recv) {
|
|
|
|
println!("pid {}: send_count = {}", self.pid, self.send_count);
|
2017-12-23 08:45:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get(&self, register: char) -> isize {
|
|
|
|
self.registers.get(®ister).map(|val| *val).unwrap_or(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set(&mut self, register: char, value: isize) {
|
|
|
|
self.registers.insert(register, value);
|
|
|
|
}
|
|
|
|
|
2017-12-23 11:30:10 +00:00
|
|
|
fn execute(&mut self, instruction: &Instruction, send: &Sender<isize>, recv: &Receiver<isize>) -> Result<(), RecvTimeoutError> {
|
2017-12-23 08:45:08 +00:00
|
|
|
use Instruction::*;
|
|
|
|
|
|
|
|
match *instruction {
|
|
|
|
Set(Operand::Register(reg), ref val) => {
|
|
|
|
let value = val.value(self);
|
|
|
|
self.set(reg, value);
|
|
|
|
}
|
|
|
|
Add(Operand::Register(reg), ref val) => {
|
|
|
|
let reg_value = self.get(reg);
|
|
|
|
let value = val.value(self);
|
|
|
|
self.set(reg, reg_value + value);
|
|
|
|
}
|
|
|
|
Mul(Operand::Register(reg), ref val) => {
|
|
|
|
let reg_value = self.get(reg);
|
|
|
|
let value = val.value(self);
|
|
|
|
self.set(reg, reg_value * value);
|
|
|
|
}
|
|
|
|
Mod(Operand::Register(reg), ref val) => {
|
|
|
|
let reg_value = self.get(reg);
|
|
|
|
let value = val.value(self);
|
|
|
|
self.set(reg, reg_value % value);
|
|
|
|
}
|
2017-12-23 11:30:10 +00:00
|
|
|
Snd(ref op) => {
|
2017-12-23 08:45:08 +00:00
|
|
|
let value = op.value(self);
|
2017-12-23 11:30:10 +00:00
|
|
|
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);
|
2017-12-23 08:45:08 +00:00
|
|
|
}
|
|
|
|
Jgz(_, _) => { /* handled below */ }
|
|
|
|
_ => panic!("bad argument")
|
|
|
|
};
|
|
|
|
|
|
|
|
// Update PC
|
|
|
|
match *instruction {
|
|
|
|
Jgz(ref x, ref offset) => {
|
|
|
|
let x_value = x.value(self);
|
|
|
|
let offset_value = offset.value(self);
|
|
|
|
|
2017-12-23 11:30:10 +00:00
|
|
|
if x_value > 0 {
|
2017-12-23 08:45:08 +00:00
|
|
|
self.pc += offset_value;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
self.pc += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => self.pc += 1
|
|
|
|
};
|
2017-12-23 11:30:10 +00:00
|
|
|
|
|
|
|
Ok(())
|
2017-12-23 08:45:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-23 11:30:10 +00:00
|
|
|
fn run(program: Program) {
|
|
|
|
let program = Arc::new(program);
|
|
|
|
let mut machine0 = Machine::new(0);
|
|
|
|
let mut machine1 = Machine::new(1);
|
|
|
|
|
|
|
|
let (send0, recv1) = channel();
|
|
|
|
let (send1, recv0) = channel();
|
|
|
|
|
|
|
|
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);
|
|
|
|
});
|
2017-12-23 08:45:08 +00:00
|
|
|
|
2017-12-23 11:30:10 +00:00
|
|
|
thread0.join().unwrap();
|
|
|
|
thread1.join().unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
run(Program::load("input"));
|
|
|
|
}
|
2017-12-23 08:45:08 +00:00
|
|
|
|
2017-12-23 11:30:10 +00:00
|
|
|
#[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));
|
2017-12-23 08:45:08 +00:00
|
|
|
}
|