Skip to content

Instantly share code, notes, and snippets.

@svantelidman
Created January 5, 2020 14:45
Show Gist options
  • Save svantelidman/7f0089bebf5f276e6847ea616e43926a to your computer and use it in GitHub Desktop.
Save svantelidman/7f0089bebf5f276e6847ea616e43926a to your computer and use it in GitHub Desktop.
/*
Probably one of the quicker days to solve both parts. Strayed a bit on the network-idle detection
but all else was quite straight forward. Solution could be cleaner with respect to the NAT-handling
though.
*/
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 part_1(program: &Vec<i64>) {
let mut machines: Vec<Machine> = vec!();
let mut packages_in_transit: HashMap<usize, Vec<(i64, i64)>> = HashMap::new();
for address in 0..50 {
packages_in_transit.insert(address, vec!());
machines.push(
Machine {
prog_ptr: 0,
relative_base: 0,
program: program.clone(),
input: vec!(address as i64),
output: vec!(),
state: MachineState::ReadyToRun
}
);
}
let mut latest_nat_package: Option<(i64, i64)> = None;
let mut prev_y_from_nat: Option<i64> = None;
let mut prev_idle: Option<bool> = None;
loop {
let mut machine_index = 0;
for machine in machines.iter_mut() {
match machine.state {
MachineState::Terminated => break,
MachineState::OutputAvailable => {
while machine.output.len() > 2 {
let address = machine.output.remove(0) as usize;
let x = machine.output.remove(0);
let y = machine.output.remove(0);
if address < 50 {
let package_list = packages_in_transit.get_mut(&address).unwrap();
package_list.push((x, y));
} else {
if address == 255 {
latest_nat_package = Some((x, y));
}
}
}
},
MachineState::WaitingForInput => {
let package_list = packages_in_transit.get_mut(&machine_index).unwrap();
if package_list.is_empty() {
machine.input.push(-1)
} else {
while !package_list.is_empty() {
let (x, y) = package_list.remove(0);
machine.input.push(x);
machine.input.push(y);
}
}
},
MachineState::ReadyToRun => {},
}
machine_index += 1;
machine.resume()
}
let is_idle = machines.iter().all(|m| m.input.is_empty() && m.output.is_empty() && m.state == MachineState::WaitingForInput);
if let Some(was_idle) = prev_idle {
if was_idle && is_idle {
if let Some((x, y)) = latest_nat_package {
if let Some(prev) = prev_y_from_nat {
if y == prev {
println!("The first Y-value sent from NAT twice in a row={}", y);
std::process::exit(0)
}
}
let package_list = packages_in_transit.get_mut(&0).unwrap();
package_list.push((x, y));
prev_y_from_nat = Some(y);
} else {
panic!("Idling without NAT-package.")
}
}
}
prev_idle = Some(is_idle);
}
}
fn main() {
let program_file = "prog".to_string();
let program: Vec<i64> = load_program(&program_file);
part_1(&program)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment