332 lines
11 KiB
Rust
332 lines
11 KiB
Rust
use std::collections::HashMap;
|
|
use std::path::Path;
|
|
|
|
pub type RAM = HashMap<usize, i128>;
|
|
|
|
#[allow(dead_code)]
|
|
pub fn run() {
|
|
let input = std::fs::read_to_string("input/day05.txt").unwrap();
|
|
let ram: RAM = input
|
|
.split(",")
|
|
.enumerate()
|
|
.map(|(i, s)| (i, s.parse::<i128>().unwrap()))
|
|
.collect();
|
|
task1(ram.clone());
|
|
task2(ram.clone());
|
|
}
|
|
|
|
pub fn load_ram<P: AsRef<Path>>(path: P) -> RAM {
|
|
let input = std::fs::read_to_string(path).unwrap();
|
|
input
|
|
.split(",")
|
|
.enumerate()
|
|
.map(|(i, s)| (i, s.parse::<i128>().unwrap()))
|
|
.collect()
|
|
}
|
|
|
|
pub struct IntCodeComputer {
|
|
input_storage: Vec<i128>,
|
|
output_storage: Vec<i128>,
|
|
pc: usize,
|
|
ram: RAM,
|
|
relative_base_offset: i128,
|
|
}
|
|
|
|
#[derive(PartialEq, Debug)]
|
|
enum OpCode {
|
|
Add,
|
|
Multiply,
|
|
Input,
|
|
Output,
|
|
JumpIfTrue,
|
|
JumpIfFalse,
|
|
LessThan,
|
|
Equals,
|
|
AdjustRelBase,
|
|
Terminate,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum OpParam {
|
|
Immediate(i128),
|
|
Memory(usize),
|
|
Relative(i128),
|
|
}
|
|
|
|
impl OpParam {
|
|
fn from(val: i128, type_number: i128) -> Self {
|
|
match type_number {
|
|
0 => OpParam::Memory(val as usize),
|
|
1 => OpParam::Immediate(val),
|
|
2 => OpParam::Relative(val),
|
|
_ => unreachable!("unexpected parameter"),
|
|
}
|
|
}
|
|
|
|
fn retrieve(&self, memory: &RAM, relative_base_offset: i128) -> i128 {
|
|
match self {
|
|
OpParam::Immediate(v) => *v,
|
|
OpParam::Memory(i) => memory.get_default(*i),
|
|
OpParam::Relative(i) => memory.get_default((relative_base_offset + *i) as usize),
|
|
}
|
|
}
|
|
|
|
fn store(&self, memory: &mut RAM, value: i128, relative_base_offset: i128) {
|
|
match self {
|
|
OpParam::Immediate(_) => panic!("cannot save to immediate position"),
|
|
OpParam::Memory(i) => {
|
|
memory.insert(*i, value);
|
|
}
|
|
OpParam::Relative(i) => {
|
|
memory.insert((relative_base_offset + *i) as usize, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Op {
|
|
opcode: OpCode,
|
|
params: Vec<OpParam>,
|
|
}
|
|
|
|
impl Op {
|
|
fn from(pc: usize, memory: &RAM) -> Self {
|
|
//opcode: 123xx -> 1, 2, 3: parameter type for parameters 1-3, xx = actual op code
|
|
let mut opcode = memory.get_default(pc);
|
|
let p_type1 = opcode / 10_000;
|
|
opcode %= 10_000;
|
|
let p_type2 = opcode / 1000;
|
|
opcode %= 1000;
|
|
let p_type3 = opcode / 100;
|
|
opcode %= 100;
|
|
match opcode {
|
|
1 => Op {
|
|
opcode: OpCode::Add,
|
|
params: vec![
|
|
OpParam::from(memory.get_default(pc + 1), p_type3),
|
|
OpParam::from(memory.get_default(pc + 2), p_type2),
|
|
OpParam::from(memory.get_default(pc + 3), p_type1),
|
|
],
|
|
},
|
|
2 => Op {
|
|
opcode: OpCode::Multiply,
|
|
params: vec![
|
|
OpParam::from(memory.get_default(pc + 1), p_type3),
|
|
OpParam::from(memory.get_default(pc + 2), p_type2),
|
|
OpParam::from(memory.get_default(pc + 3), p_type1),
|
|
],
|
|
},
|
|
3 => Op {
|
|
opcode: OpCode::Input,
|
|
params: vec![OpParam::from(memory.get_default(pc + 1), p_type3)],
|
|
},
|
|
4 => Op {
|
|
opcode: OpCode::Output,
|
|
params: vec![OpParam::from(memory.get_default(pc + 1), p_type3)],
|
|
},
|
|
5 => Op {
|
|
opcode: OpCode::JumpIfTrue,
|
|
params: vec![
|
|
OpParam::from(memory.get_default(pc + 1), p_type3),
|
|
OpParam::from(memory.get_default(pc + 2), p_type2),
|
|
],
|
|
},
|
|
6 => Op {
|
|
opcode: OpCode::JumpIfFalse,
|
|
params: vec![
|
|
OpParam::from(memory.get_default(pc + 1), p_type3),
|
|
OpParam::from(memory.get_default(pc + 2), p_type2),
|
|
],
|
|
},
|
|
7 => Op {
|
|
opcode: OpCode::LessThan,
|
|
params: vec![
|
|
OpParam::from(memory.get_default(pc + 1), p_type3),
|
|
OpParam::from(memory.get_default(pc + 2), p_type2),
|
|
OpParam::from(memory.get_default(pc + 3), p_type1),
|
|
],
|
|
},
|
|
8 => Op {
|
|
opcode: OpCode::Equals,
|
|
params: vec![
|
|
OpParam::from(memory.get_default(pc + 1), p_type3),
|
|
OpParam::from(memory.get_default(pc + 2), p_type2),
|
|
OpParam::from(memory.get_default(pc + 3), p_type1),
|
|
],
|
|
},
|
|
9 => Op {
|
|
opcode: OpCode::AdjustRelBase,
|
|
params: vec![OpParam::from(memory.get_default(pc + 1), p_type3)],
|
|
},
|
|
99 => Op {
|
|
opcode: OpCode::Terminate,
|
|
params: vec![],
|
|
},
|
|
_ => unreachable!("unknown op code {}", opcode),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl IntCodeComputer {
|
|
pub fn new(input: Vec<i128>, memory: RAM) -> Self {
|
|
IntCodeComputer {
|
|
input_storage: input,
|
|
output_storage: Vec::new(),
|
|
pc: 0,
|
|
ram: memory,
|
|
relative_base_offset: 0,
|
|
}
|
|
}
|
|
|
|
pub fn run_single(&mut self, input: i128) -> i128 {
|
|
self.set_input(&[input]);
|
|
self.clear_output();
|
|
self.run_until_input_empty();
|
|
*self.get_output().first().unwrap()
|
|
}
|
|
|
|
pub fn run_until_end(&mut self) {
|
|
if !self.run_until_input_empty() {
|
|
panic!("There wasn't enough input given to run until the program halted");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* True means the program terminated, false means it is just waiting for more input
|
|
*/
|
|
pub fn run_until_input_empty(&mut self) -> bool {
|
|
let mut op = Op::from(self.pc, &self.ram);
|
|
let mut inputs = self.input_storage.iter();
|
|
while op.opcode != OpCode::Terminate {
|
|
match op.opcode {
|
|
OpCode::Add => {
|
|
let r = op.params[0].retrieve(&self.ram, self.relative_base_offset)
|
|
+ op.params[1].retrieve(&self.ram, self.relative_base_offset);
|
|
op.params[2].store(&mut self.ram, r, self.relative_base_offset);
|
|
self.pc += 4;
|
|
}
|
|
OpCode::Multiply => {
|
|
let r = op.params[0].retrieve(&self.ram, self.relative_base_offset)
|
|
* op.params[1].retrieve(&self.ram, self.relative_base_offset);
|
|
op.params[2].store(&mut self.ram, r, self.relative_base_offset);
|
|
self.pc += 4;
|
|
}
|
|
OpCode::Input => {
|
|
if let Some(v) = inputs.next() {
|
|
op.params[0].store(&mut self.ram, *v, self.relative_base_offset);
|
|
self.pc += 2;
|
|
} else {
|
|
self.input_storage.clear();
|
|
return false;
|
|
}
|
|
}
|
|
OpCode::Output => {
|
|
self.output_storage
|
|
.push(op.params[0].retrieve(&self.ram, self.relative_base_offset));
|
|
self.pc += 2;
|
|
}
|
|
OpCode::JumpIfTrue => {
|
|
if op.params[0].retrieve(&self.ram, self.relative_base_offset) > 0 {
|
|
self.pc =
|
|
op.params[1].retrieve(&self.ram, self.relative_base_offset) as usize;
|
|
} else {
|
|
self.pc += 3;
|
|
}
|
|
}
|
|
OpCode::JumpIfFalse => {
|
|
if op.params[0].retrieve(&self.ram, self.relative_base_offset) == 0 {
|
|
self.pc =
|
|
op.params[1].retrieve(&self.ram, self.relative_base_offset) as usize;
|
|
} else {
|
|
self.pc += 3;
|
|
}
|
|
}
|
|
OpCode::LessThan => {
|
|
let r = if op.params[0].retrieve(&self.ram, self.relative_base_offset)
|
|
< op.params[1].retrieve(&self.ram, self.relative_base_offset)
|
|
{
|
|
1
|
|
} else {
|
|
0
|
|
};
|
|
op.params[2].store(&mut self.ram, r, self.relative_base_offset);
|
|
self.pc += 4;
|
|
}
|
|
OpCode::Equals => {
|
|
let r = if op.params[0].retrieve(&self.ram, self.relative_base_offset)
|
|
== op.params[1].retrieve(&self.ram, self.relative_base_offset)
|
|
{
|
|
1
|
|
} else {
|
|
0
|
|
};
|
|
op.params[2].store(&mut self.ram, r, self.relative_base_offset);
|
|
self.pc += 4;
|
|
}
|
|
OpCode::AdjustRelBase => {
|
|
self.relative_base_offset +=
|
|
op.params[0].retrieve(&self.ram, self.relative_base_offset);
|
|
self.pc += 2;
|
|
}
|
|
OpCode::Terminate => {}
|
|
}
|
|
op = Op::from(self.pc, &self.ram);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
pub fn set_input(&mut self, input: &[i128]) {
|
|
self.input_storage.clear();
|
|
for x in input {
|
|
self.input_storage.push(*x);
|
|
}
|
|
}
|
|
|
|
pub fn add_input(&mut self, input: &[i128]) {
|
|
for x in input {
|
|
self.input_storage.push(*x);
|
|
}
|
|
}
|
|
|
|
pub fn get_input(&self) -> &[i128] {
|
|
self.input_storage.as_slice()
|
|
}
|
|
|
|
pub fn clear_output(&mut self) {
|
|
self.output_storage.clear();
|
|
}
|
|
|
|
pub fn clear_n_output(&mut self, n: usize) {
|
|
self.output_storage = Vec::from(self.output_storage.split_at(n).1);
|
|
}
|
|
|
|
pub fn get_output(&self) -> &[i128] {
|
|
self.output_storage.as_slice()
|
|
}
|
|
}
|
|
|
|
fn task1(ram: RAM) {
|
|
let mut computer = IntCodeComputer::new(vec![1], ram);
|
|
computer.run_until_end();
|
|
println!("{:?}", computer.get_output());
|
|
println!("Task 1: {}", computer.get_output().last().unwrap());
|
|
}
|
|
|
|
fn task2(ram: RAM) {
|
|
let mut computer = IntCodeComputer::new(vec![5], ram);
|
|
computer.run_until_end();
|
|
println!("{:?}", computer.get_output());
|
|
println!("Task 2: {}", computer.get_output().last().unwrap());
|
|
}
|
|
|
|
trait Easy {
|
|
fn get_default(&self, index: usize) -> i128;
|
|
}
|
|
|
|
impl Easy for RAM {
|
|
fn get_default(&self, index: usize) -> i128 {
|
|
**self.get(&index).get_or_insert(&0)
|
|
}
|
|
}
|