Last active
January 4, 2020 14:38
-
-
Save svantelidman/faddd5a613a9bd6a46b4ba58cfddb394 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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