2019-12-08 01:23:49 +00:00
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::collections::VecDeque;
|
2019-12-09 05:54:32 +00:00
|
|
|
use std::convert::TryFrom;
|
2019-12-08 01:23:49 +00:00
|
|
|
use std::rc::Rc;
|
|
|
|
|
2019-12-09 05:54:32 +00:00
|
|
|
type Address = i64;
|
2019-12-07 02:45:54 +00:00
|
|
|
|
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
|
|
enum Instruction {
|
2019-12-09 05:54:32 +00:00
|
|
|
Add(Mode, Mode, Mode),
|
|
|
|
Multiply(Mode, Mode, Mode),
|
|
|
|
Input(Mode),
|
2019-12-07 02:45:54 +00:00
|
|
|
Output(Mode),
|
2019-12-07 03:44:38 +00:00
|
|
|
JumpIfTrue(Mode, Mode),
|
|
|
|
JumpIfFalse(Mode, Mode),
|
2019-12-09 05:54:32 +00:00
|
|
|
LessThan(Mode, Mode, Mode),
|
|
|
|
Equals(Mode, Mode, Mode),
|
|
|
|
AdjustRelativeBase(Mode),
|
2019-12-07 02:45:54 +00:00
|
|
|
Halt,
|
|
|
|
}
|
|
|
|
|
2019-12-09 05:54:32 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
2019-12-07 02:45:54 +00:00
|
|
|
enum Mode {
|
|
|
|
Immediate,
|
|
|
|
Address,
|
2019-12-09 05:54:32 +00:00
|
|
|
Relative,
|
2019-12-07 02:45:54 +00:00
|
|
|
}
|
|
|
|
|
2019-12-08 01:23:49 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Pipe {
|
2019-12-09 05:54:32 +00:00
|
|
|
queue: VecDeque<i64>,
|
|
|
|
last: Option<i64>,
|
2019-12-08 01:23:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub enum ComputeResult {
|
|
|
|
Halted,
|
|
|
|
NeedsInput,
|
|
|
|
}
|
|
|
|
|
2019-12-07 23:07:15 +00:00
|
|
|
pub struct Computer<I: Input, O: Output> {
|
2019-12-08 01:23:49 +00:00
|
|
|
name: char,
|
2019-12-09 05:54:32 +00:00
|
|
|
ip: i64,
|
|
|
|
memory: Vec<i64>,
|
2019-12-07 23:07:15 +00:00
|
|
|
input: I,
|
|
|
|
output: O,
|
2019-12-09 05:54:32 +00:00
|
|
|
relative_base: i64,
|
2019-12-07 23:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub trait Input {
|
2019-12-09 05:54:32 +00:00
|
|
|
fn read(&mut self) -> Option<i64>;
|
2019-12-07 23:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub trait Output {
|
2019-12-09 05:54:32 +00:00
|
|
|
fn write(&mut self, value: i64);
|
2019-12-07 23:07:15 +00:00
|
|
|
|
2019-12-09 05:54:32 +00:00
|
|
|
fn last_value(&self) -> i64;
|
2019-12-07 23:07:15 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 05:54:32 +00:00
|
|
|
impl Input for Vec<i64> {
|
|
|
|
fn read(&mut self) -> Option<i64> {
|
2019-12-07 23:07:15 +00:00
|
|
|
self.pop()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-09 05:54:32 +00:00
|
|
|
impl Output for Vec<i64> {
|
|
|
|
fn write(&mut self, value: i64) {
|
2019-12-07 23:07:15 +00:00
|
|
|
self.push(value)
|
|
|
|
}
|
|
|
|
|
2019-12-09 05:54:32 +00:00
|
|
|
fn last_value(&self) -> i64 {
|
2019-12-07 23:07:15 +00:00
|
|
|
*self.last().unwrap()
|
|
|
|
}
|
2019-12-07 02:45:54 +00:00
|
|
|
}
|
|
|
|
|
2019-12-08 01:23:49 +00:00
|
|
|
impl Input for Rc<RefCell<Pipe>> {
|
2019-12-09 05:54:32 +00:00
|
|
|
fn read(&mut self) -> Option<i64> {
|
2019-12-08 01:23:49 +00:00
|
|
|
dbg!(self.borrow_mut().queue.pop_front())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Output for Rc<RefCell<Pipe>> {
|
2019-12-09 05:54:32 +00:00
|
|
|
fn write(&mut self, value: i64) {
|
2019-12-08 01:23:49 +00:00
|
|
|
let mut pipe = self.borrow_mut();
|
|
|
|
pipe.last = Some(value);
|
|
|
|
pipe.queue.push_back(value);
|
|
|
|
}
|
|
|
|
|
2019-12-09 05:54:32 +00:00
|
|
|
fn last_value(&self) -> i64 {
|
2019-12-08 01:23:49 +00:00
|
|
|
self.borrow().last.unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-09 05:54:32 +00:00
|
|
|
fn decode(mut instruction: i64) -> Instruction {
|
2019-12-07 02:45:54 +00:00
|
|
|
let opcode = divmod(&mut instruction, 100);
|
|
|
|
|
|
|
|
match opcode {
|
|
|
|
1 => {
|
|
|
|
// let mode3 = divmod(&mut instruction, 10);
|
|
|
|
// Parameters that an instruction writes to will never be in immediate mode.
|
|
|
|
Instruction::Add(
|
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
2019-12-09 05:54:32 +00:00
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
2019-12-07 02:45:54 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
2 => Instruction::Multiply(
|
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
2019-12-09 05:54:32 +00:00
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
2019-12-07 02:45:54 +00:00
|
|
|
),
|
2019-12-09 05:54:32 +00:00
|
|
|
3 => Instruction::Input(Mode::from(divmod(&mut instruction, 10))),
|
2019-12-07 02:45:54 +00:00
|
|
|
4 => Instruction::Output(Mode::from(divmod(&mut instruction, 10))),
|
2019-12-07 03:44:38 +00:00
|
|
|
5 => Instruction::JumpIfTrue(
|
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
|
|
|
),
|
|
|
|
6 => Instruction::JumpIfFalse(
|
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
|
|
|
),
|
|
|
|
7 => Instruction::LessThan(
|
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
2019-12-09 05:54:32 +00:00
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
2019-12-07 03:44:38 +00:00
|
|
|
),
|
|
|
|
8 => Instruction::Equals(
|
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
2019-12-09 05:54:32 +00:00
|
|
|
Mode::from(divmod(&mut instruction, 10)),
|
2019-12-07 03:44:38 +00:00
|
|
|
),
|
2019-12-09 05:54:32 +00:00
|
|
|
9 => Instruction::AdjustRelativeBase(Mode::from(divmod(&mut instruction, 10))),
|
2019-12-07 02:45:54 +00:00
|
|
|
99 => Instruction::Halt,
|
|
|
|
_ => panic!("Invalid opcode: {}", opcode),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-08 01:23:49 +00:00
|
|
|
impl<I, O> Computer<I, O>
|
|
|
|
where
|
|
|
|
I: Input,
|
|
|
|
O: Output,
|
|
|
|
{
|
2019-12-09 05:54:32 +00:00
|
|
|
pub fn new(name: char, mut memory: Vec<i64>, input: I, output: O) -> Self {
|
|
|
|
// HACK
|
|
|
|
let mut buffer = vec![0; 64 * 1024 * 1024];
|
|
|
|
memory.append(&mut buffer);
|
2019-12-07 06:36:42 +00:00
|
|
|
Computer {
|
2019-12-08 01:23:49 +00:00
|
|
|
name,
|
|
|
|
ip: 0,
|
2019-12-07 06:36:42 +00:00
|
|
|
memory,
|
|
|
|
input,
|
2019-12-07 23:07:15 +00:00
|
|
|
output,
|
2019-12-09 05:54:32 +00:00
|
|
|
relative_base: 0,
|
2019-12-07 06:36:42 +00:00
|
|
|
}
|
2019-12-07 02:45:54 +00:00
|
|
|
}
|
|
|
|
|
2019-12-08 01:23:49 +00:00
|
|
|
pub fn name(&self) -> char {
|
|
|
|
self.name
|
|
|
|
}
|
|
|
|
|
2019-12-09 05:54:32 +00:00
|
|
|
fn read(&self, value: i64, mode: Mode) -> i64 {
|
2019-12-07 02:45:54 +00:00
|
|
|
match mode {
|
2019-12-07 06:36:42 +00:00
|
|
|
Mode::Immediate => self.memory[value as usize],
|
|
|
|
Mode::Address => self.memory[self.memory[value as usize] as usize],
|
2019-12-09 05:54:32 +00:00
|
|
|
// The address a relative mode parameter refers to is itself plus the current relative base.
|
|
|
|
Mode::Relative => {
|
|
|
|
self.memory
|
|
|
|
[usize::try_from(self.relative_base + self.memory[value as usize]).unwrap()]
|
|
|
|
}
|
2019-12-07 02:45:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-09 05:54:32 +00:00
|
|
|
fn write(&mut self, address: Address, value: i64, mode: Mode) {
|
|
|
|
match mode {
|
|
|
|
Mode::Immediate => panic!("attempt to write with immediate mode"),
|
|
|
|
Mode::Address => self.memory[address as usize] = value,
|
|
|
|
Mode::Relative => {
|
|
|
|
self.memory[usize::try_from(self.relative_base + address).unwrap()] = value
|
|
|
|
}
|
|
|
|
}
|
2019-12-07 02:45:54 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 05:54:32 +00:00
|
|
|
pub fn run(&mut self, noun: Option<i64>, verb: Option<i64>) -> ComputeResult {
|
2019-12-08 01:23:49 +00:00
|
|
|
println!("{}: resume ip = {}", self.name, self.ip);
|
2019-12-07 02:58:11 +00:00
|
|
|
if let Some(noun) = noun {
|
2019-12-09 05:54:32 +00:00
|
|
|
self.write(1, noun, Mode::Address);
|
2019-12-07 02:58:11 +00:00
|
|
|
}
|
|
|
|
if let Some(verb) = verb {
|
2019-12-09 05:54:32 +00:00
|
|
|
self.write(2, verb, Mode::Address);
|
2019-12-07 02:58:11 +00:00
|
|
|
}
|
2019-12-07 02:45:54 +00:00
|
|
|
|
|
|
|
loop {
|
2019-12-08 01:23:49 +00:00
|
|
|
match decode(self.read(self.ip, Mode::Immediate)) {
|
2019-12-09 05:54:32 +00:00
|
|
|
Instruction::Add(mode1, mode2, write_mode) => {
|
2019-12-08 01:23:49 +00:00
|
|
|
let result = self.read(self.ip + 1, mode1) + self.read(self.ip + 2, mode2);
|
2019-12-09 05:54:32 +00:00
|
|
|
self.write(self.read(self.ip + 3, Mode::Immediate), result, write_mode);
|
2019-12-08 01:23:49 +00:00
|
|
|
self.ip += 4;
|
2019-12-07 02:45:54 +00:00
|
|
|
}
|
2019-12-09 05:54:32 +00:00
|
|
|
Instruction::Multiply(mode1, mode2, write_mode) => {
|
2019-12-08 01:23:49 +00:00
|
|
|
let result = self.read(self.ip + 1, mode1) * self.read(self.ip + 2, mode2);
|
2019-12-09 05:54:32 +00:00
|
|
|
self.write(self.read(self.ip + 3, Mode::Immediate), result, write_mode);
|
2019-12-08 01:23:49 +00:00
|
|
|
self.ip += 4;
|
2019-12-07 02:45:54 +00:00
|
|
|
}
|
2019-12-09 05:54:32 +00:00
|
|
|
Instruction::Input(write_mode) => match self.input.read() {
|
2019-12-08 01:23:49 +00:00
|
|
|
Some(value) => {
|
2019-12-09 05:54:32 +00:00
|
|
|
self.write(self.read(self.ip + 1, Mode::Immediate), value, write_mode);
|
2019-12-08 01:23:49 +00:00
|
|
|
self.ip += 2;
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
// println!("{}: pause ip = {}", self.name, self.ip);
|
|
|
|
return ComputeResult::NeedsInput;
|
|
|
|
}
|
|
|
|
},
|
2019-12-07 02:45:54 +00:00
|
|
|
Instruction::Output(mode) => {
|
2019-12-08 01:23:49 +00:00
|
|
|
let value = self.read(self.ip + 1, mode);
|
|
|
|
println!("{}: output {}", self.name, value);
|
|
|
|
self.output.write(value);
|
|
|
|
self.ip += 2;
|
2019-12-07 02:45:54 +00:00
|
|
|
}
|
2019-12-07 03:44:38 +00:00
|
|
|
Instruction::JumpIfTrue(mode1, mode2) => {
|
2019-12-08 01:23:49 +00:00
|
|
|
if self.read(self.ip + 1, mode1) != 0 {
|
|
|
|
self.ip = self.read(self.ip + 2, mode2);
|
2019-12-07 03:44:38 +00:00
|
|
|
} else {
|
2019-12-08 01:23:49 +00:00
|
|
|
self.ip += 3;
|
2019-12-07 03:44:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Instruction::JumpIfFalse(mode1, mode2) => {
|
2019-12-08 01:23:49 +00:00
|
|
|
if self.read(self.ip + 1, mode1) == 0 {
|
|
|
|
self.ip = self.read(self.ip + 2, mode2);
|
2019-12-07 03:44:38 +00:00
|
|
|
} else {
|
2019-12-08 01:23:49 +00:00
|
|
|
self.ip += 3;
|
2019-12-07 03:44:38 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-09 05:54:32 +00:00
|
|
|
Instruction::LessThan(mode1, mode2, write_mode) => {
|
2019-12-08 01:23:49 +00:00
|
|
|
if self.read(self.ip + 1, mode1) < self.read(self.ip + 2, mode2) {
|
2019-12-09 05:54:32 +00:00
|
|
|
self.write(self.read(self.ip + 3, Mode::Immediate), 1, write_mode);
|
2019-12-07 03:44:38 +00:00
|
|
|
} else {
|
2019-12-09 05:54:32 +00:00
|
|
|
self.write(self.read(self.ip + 3, Mode::Immediate), 0, write_mode);
|
2019-12-07 03:44:38 +00:00
|
|
|
}
|
2019-12-08 01:23:49 +00:00
|
|
|
self.ip += 4;
|
2019-12-07 03:44:38 +00:00
|
|
|
}
|
2019-12-09 05:54:32 +00:00
|
|
|
Instruction::Equals(mode1, mode2, write_mode) => {
|
2019-12-08 01:23:49 +00:00
|
|
|
if self.read(self.ip + 1, mode1) == self.read(self.ip + 2, mode2) {
|
2019-12-09 05:54:32 +00:00
|
|
|
self.write(self.read(self.ip + 3, Mode::Immediate), 1, write_mode);
|
2019-12-07 03:44:38 +00:00
|
|
|
} else {
|
2019-12-09 05:54:32 +00:00
|
|
|
self.write(self.read(self.ip + 3, Mode::Immediate), 0, write_mode);
|
2019-12-07 03:44:38 +00:00
|
|
|
}
|
2019-12-08 01:23:49 +00:00
|
|
|
self.ip += 4;
|
2019-12-07 03:44:38 +00:00
|
|
|
}
|
2019-12-09 05:54:32 +00:00
|
|
|
Instruction::AdjustRelativeBase(mode) => {
|
|
|
|
let base = self.read(self.ip + 1, mode);
|
|
|
|
self.relative_base += base;
|
|
|
|
self.relative_base;
|
|
|
|
self.ip += 2;
|
|
|
|
}
|
2019-12-08 01:23:49 +00:00
|
|
|
Instruction::Halt => return ComputeResult::Halted,
|
2019-12-07 02:45:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-07 06:36:42 +00:00
|
|
|
|
2019-12-09 05:54:32 +00:00
|
|
|
pub fn output(&self) -> i64 {
|
2019-12-07 23:07:15 +00:00
|
|
|
self.output.last_value()
|
2019-12-07 06:36:42 +00:00
|
|
|
}
|
2019-12-07 02:45:54 +00:00
|
|
|
}
|
|
|
|
|
2019-12-08 01:23:49 +00:00
|
|
|
impl Pipe {
|
2019-12-09 05:54:32 +00:00
|
|
|
pub fn new(queue: VecDeque<i64>) -> Self {
|
2019-12-08 01:23:49 +00:00
|
|
|
Pipe { queue, last: None }
|
|
|
|
}
|
|
|
|
|
2019-12-09 05:54:32 +00:00
|
|
|
pub fn push_front(&mut self, value: i64) {
|
2019-12-08 01:23:49 +00:00
|
|
|
self.queue.push_front(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-09 05:54:32 +00:00
|
|
|
fn divmod(value: &mut i64, divisor: i64) -> i64 {
|
2019-12-07 02:45:54 +00:00
|
|
|
let res = *value % divisor;
|
|
|
|
*value /= divisor;
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
2019-12-09 05:54:32 +00:00
|
|
|
impl From<i64> for Mode {
|
|
|
|
fn from(mode: i64) -> Self {
|
2019-12-07 02:45:54 +00:00
|
|
|
match mode {
|
|
|
|
0 => Mode::Address,
|
|
|
|
1 => Mode::Immediate,
|
2019-12-09 05:54:32 +00:00
|
|
|
2 => Mode::Relative,
|
2019-12-07 02:45:54 +00:00
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use crate::input;
|
|
|
|
use std::fs;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_decode() {
|
|
|
|
assert_eq!(
|
|
|
|
decode(1002),
|
2019-12-09 05:54:32 +00:00
|
|
|
Instruction::Multiply(Mode::Address, Mode::Immediate, Mode::Address)
|
2019-12-07 02:45:54 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-12-07 02:58:11 +00:00
|
|
|
#[test]
|
2019-12-07 02:45:54 +00:00
|
|
|
fn test_day2() {
|
|
|
|
let input = fs::read_to_string("input/day2.txt").unwrap();
|
2019-12-08 01:23:49 +00:00
|
|
|
let data = input::read_separated_line(',', &input).unwrap();
|
|
|
|
let mut program = Computer::new('2', data, vec![], vec![]);
|
2019-12-07 02:45:54 +00:00
|
|
|
|
|
|
|
// Check that day2 still works wirh run through this implementation
|
2019-12-07 02:58:11 +00:00
|
|
|
program.run(Some(12), Some(2));
|
|
|
|
assert_eq!(program.read(0, Mode::Immediate), 4138658);
|
2019-12-07 02:45:54 +00:00
|
|
|
}
|
|
|
|
}
|