Skip to content

Instantly share code, notes, and snippets.

@svantelidman
Created January 6, 2020 13:05
Show Gist options
  • Save svantelidman/85c2478718609824b765cbe3a609b430 to your computer and use it in GitHub Desktop.
Save svantelidman/85c2478718609824b765cbe3a609b430 to your computer and use it in GitHub Desktop.
/*
Part 1 was solved by first exploring the internals of Santa's ship manually then
creating a command file / parsing to collect all items and move to the security
checkpoint. Then a random number of items between 3 and 6 are picked up and
tried if they are the right combination until we find the right one. Seemed simpler
solution which would be quick enough given the relatively few number of items compared to
doing a more proper search. It will typically take 5-20 seconds to find the right combination.
Part 2 will have to wait until days 16, 18, and 22 are completed, if ever.
*/
use std::io::{BufReader, BufRead};
use std::fs::File;
extern crate random_integer;
#[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 input_string(&mut self, str: String) {
str.chars().for_each(|c| self.input.push((c as u8) as i64))
}
fn input_i64(&mut self, code: i64) {
self.input.push(code)
}
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_script(script_file: &String) -> Vec<String> {
let mut droid_script: Vec<String> = vec![];
let file = File::open(script_file).expect("Could not open droid program file!");
let reader = BufReader::new(file);
for line in reader.lines() {
droid_script.push(line.unwrap());
}
droid_script
}
fn random_item_permutation(min_items: u8, max_items: u8) -> Vec<String> {
let mut item_combo: Vec<String> = vec!();
let mut items: Vec<String> = vec!(
"cake".to_string(), "prime number".to_string(), "mutex".to_string(),
"dehydrated water".to_string(), "coin".to_string(), "manifold".to_string(),
"candy cane".to_string(), "fuel cell".to_string());
let n_items = random_integer::random_u8(min_items, max_items);
for _ in 0..n_items {
let pick_item = random_integer::random_u8(0, (items.len() - 1) as u8) as usize;
item_combo.push(items.remove(pick_item))
}
item_combo
}
fn part_1(program: &Vec<i64>, droid_script: &Vec<String>) {
let mut droid_script = droid_script.clone();
let mut machine = Machine {
prog_ptr: 0,
relative_base: 0,
program: program.clone(),
input: vec!(),
output: vec!(),
state: MachineState::ReadyToRun
};
let weight_adjusted = false;
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 {
panic!("Unexpected output.")
}
}
},
MachineState::WaitingForInput => {
if droid_script.is_empty() {
if !weight_adjusted {
machine.input_string("drop cake\n".to_string());
machine.input_string("drop prime number\n".to_string());
machine.input_string("drop mutex\n".to_string());
machine.input_string("drop dehydrated water\n".to_string());
machine.input_string("drop coin\n".to_string());
machine.input_string("drop manifold\n".to_string());
machine.input_string("drop candy cane\n".to_string());
machine.input_string("drop fuel cell\n".to_string());
let test_items = random_item_permutation(3, 6);
println!("Try with: {:?}", test_items);
for item in test_items {
machine.input_string("take ".to_string());
machine.input_string(item);
machine.input_i64(10);
}
machine.input_string("west\n".to_string());
} else {
let mut command = String::new();
match std::io::stdin().read_line(&mut command) {
Ok(_) => {
machine.input_string(command);
}
Err(error) => println!("error: {}", error),
}
}
} else {
let command = droid_script.remove(0);
machine.input_string(command);
machine.input_i64(10); // Linefeed needed also
}
},
MachineState::ReadyToRun => {},
}
machine.resume()
}
}
fn main() {
let program = "prog".to_string();
let program = load_program(&program);
let droid_script = "droid_script".to_string();
let droid_script = load_droid_script(&droid_script);
part_1(&program, &droid_script)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment