Skip to content

Instantly share code, notes, and snippets.

@svantelidman
Last active January 4, 2020 14:38
Show Gist options
  • Save svantelidman/faddd5a613a9bd6a46b4ba58cfddb394 to your computer and use it in GitHub Desktop.
Save svantelidman/faddd5a613a9bd6a46b4ba58cfddb394 to your computer and use it in GitHub Desktop.
/*
To run, put the IntCode-program in a file named prog and the spring droid program
in a file named droid_prog.
It was fairly easy to come up with a spring droid program for part 1 manually:
NOT C J
NOT A T
OR T J
AND D J
WALK
Part 2 took some more trial and error to come up with the following spring droid program:
NOT E T -- Condition A: Never Jump if E and H are holes
NOT H J
AND J T
NOT T T -- T is now true if allowed to jump according to condition A
NOT A J -- Condition C: Never jump if A, B, C are solid ground
NOT J J
AND B J
AND C J
NOT J J -- J is now true if we are allowed to jump according to condition B
AND J T -- T is now true if we are allowed to jump according to both conditions abobe
NOT A J -- Jump always if directly in front of a gap
OR D J -- Never jump if we will land in a hole
AND T J -- Apply the merged conditions from above.
RUN
*/
use std::io::{BufReader, BufRead};
use std::fs::File;
use std::collections::HashMap;
#[derive(Debug)]
struct Machine {
prog_ptr: i64,
relative_base: i64,
program: Vec<i64>,
input: Vec<i64>,
output: Vec<i64>,
state: MachineState
}
#[derive(PartialEq, Debug)]
enum MachineState {
ReadyToRun,
WaitingForInput,
OutputAvailable,
Terminated
}
enum InstructionResult {
Continue,
WaitForInput,
OutputDelivered,
Invalid,
Exit
}
enum ParameterMode {
Position = 0,
Direct = 1,
Relative= 2
}
impl Machine {
fn resume(&mut self) {
loop {
let a = self.run_one_instruction();
match a {
InstructionResult::Continue => { self.state = MachineState::ReadyToRun },
InstructionResult::WaitForInput => { self.state = MachineState::WaitingForInput; break; },
InstructionResult::OutputDelivered => { self.state = MachineState::OutputAvailable; break; },
InstructionResult::Exit => { self.state = MachineState::Terminated; break; },
_ => panic!("Unexpected action."),
}
}
}
fn grow_storage_if_required(&mut self, location: i64) {
if location >= self.program.len() as i64 {
self.program.resize((location + 1) as usize, 0)
}
}
fn get(&mut self, location: i64) -> i64 {
self.grow_storage_if_required(location);
self.program[location as usize]
}
fn put(&mut self, location: i64, value: i64) {
self.grow_storage_if_required(location);
self.program[location as usize] = value;
}
fn next_cell(&mut self) -> i64 {
let ind = self.prog_ptr;
self.prog_ptr += 1;
self.get(ind)
}
fn get_parameter_value(&mut self, mode: i64) -> i64 {
let value = self.next_cell();
if mode == ParameterMode::Position as i64 {
return self.get(value)
} else if mode == ParameterMode::Direct as i64 {
return value
} else if mode == ParameterMode::Relative as i64 {
return self.get(value + self.relative_base)
} else {
panic!("Unknown parameter mode: {}", mode)
}
}
fn get_storage_location(&mut self, mode: i64) -> i64 {
if mode == ParameterMode::Relative as i64 {
self.next_cell() + self.relative_base
} else {
self.next_cell()
}
}
const ADD: i64 = 1;
const MULT: i64 = 2;
const INPUT: i64 = 3;
const OUTPUT: i64 = 4;
const JUMP_TRUE: i64 = 5;
const JUMP_FALSE: i64 = 6;
const LESS_THAN: i64 = 7;
const EQUALS: i64 = 8;
const RELATIVE_BASE_OFFSET: i64 = 9;
const EXIT: i64 = 99;
fn run_one_instruction(&mut self) -> InstructionResult {
let saved_prog_ptr = self.prog_ptr;
let next_instruction_code = self.next_cell();
let next_op_code = next_instruction_code % 100;
let mode_par_1 = (next_instruction_code / 100) % 10;
let mode_par_2 = (next_instruction_code / 1000) % 10;
let mode_par_3 = (next_instruction_code / 10000) % 10;
match next_op_code {
Machine::ADD => {
let t1 = self.get_parameter_value(mode_par_1);
let t2 = self.get_parameter_value(mode_par_2);
let store_at = self.get_storage_location(mode_par_3);
self.put(store_at, t1 + t2);
InstructionResult::Continue
},
Machine::MULT => {
let f1 = self.get_parameter_value(mode_par_1);
let f2 = self.get_parameter_value(mode_par_2);
let store_at = self.get_storage_location(mode_par_3);
self.put(store_at, f1 * f2);
InstructionResult::Continue
},
Machine::INPUT => {
if !self.input.is_empty() {
let store_at = self.get_storage_location(mode_par_1);
let c = self.input.remove(0);
self.put(store_at, c);
InstructionResult::Continue
} else {
self.prog_ptr = saved_prog_ptr;
InstructionResult::WaitForInput
}
},
Machine::JUMP_TRUE => {
let value = self.get_parameter_value(mode_par_1);
let jump_target = self.get_parameter_value(mode_par_2);
if value != 0 {
self.prog_ptr = jump_target
}
InstructionResult::Continue
},
Machine::JUMP_FALSE => {
let value = self.get_parameter_value(mode_par_1);
let jump_target = self.get_parameter_value(mode_par_2);
if value == 0 {
self.prog_ptr = jump_target
}
InstructionResult::Continue
},
Machine::LESS_THAN => {
let p1 = self.get_parameter_value(mode_par_1);
let p2 = self.get_parameter_value(mode_par_2);
let store_at = self.get_storage_location(mode_par_3);
let result = if p1 < p2 { 1 } else { 0 };
self.put(store_at, result);
InstructionResult::Continue
},
Machine::EQUALS => {
let p1 = self.get_parameter_value(mode_par_1);
let p2 = self.get_parameter_value(mode_par_2);
let store_at = self.get_storage_location(mode_par_3);
let result = if p1 == p2 { 1 } else { 0 };
self.put(store_at, result);
InstructionResult::Continue
},
Machine::OUTPUT => {
let v = self.get_parameter_value(mode_par_1);
self.output.push(v);
InstructionResult::OutputDelivered
},
Machine::RELATIVE_BASE_OFFSET => {
let v = self.get_parameter_value(mode_par_1);
self.relative_base += v;
InstructionResult::Continue
},
Machine::EXIT => InstructionResult::Exit,
_ => InstructionResult::Invalid
}
}
}
fn load_program(program_file: &String) -> Vec<i64> {
let mut prog: Vec<i64> = vec![];
let file = File::open(program_file).expect("Could not open program file!");
let reader = BufReader::new(file);
let line = reader.lines().next().expect("Could not read from program file");
let line = line.expect("No string there.");
for s in line.split(",") {
let c = i64::from_str_radix(&s, 10).expect("Could not parse int");
prog.push(c);
}
prog
}
fn load_droid_program(program_file: &String) -> Vec<i64> {
let mut droid_prog: Vec<i64> = vec![];
let file = File::open(program_file).expect("Could not open droid program file!");
let reader = BufReader::new(file);
for line in reader.lines() {
for c in line.expect("No string there.").chars() {
droid_prog.push(c as i64)
}
droid_prog.push(10)
}
droid_prog
}
fn deploy_droid(program: &Vec<i64>, droid_program: &Vec<i64>) {
let mut machine = Machine {
prog_ptr: 0,
relative_base: 0,
program: program.clone(),
input: droid_program.clone(),
output: vec!(),
state: MachineState::ReadyToRun
};
loop {
match machine.state {
MachineState::Terminated => break,
MachineState::OutputAvailable => {
while !machine.output.is_empty() {
let code = machine.output.remove(0);
if code < 128 {
let code = code as u8;
let c = code as char;
print!("{}", c)
} else {
println!("Accumulated damage = {}", code);
}
}
},
MachineState::WaitingForInput => {
panic!("Unexpected input requested")
},
MachineState::ReadyToRun => {},
}
machine.resume()
}
}
fn main() {
let program_file = "prog".to_string();
let droid_program_file = "droid_prog".to_string();
let program: Vec<i64> = load_program(&program_file);
let droid_program: Vec<i64> = load_droid_program(&droid_program_file);
deploy_droid(&program, &droid_program)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment