Skip to content

Instantly share code, notes, and snippets.

@svantelidman
Last active December 7, 2019 15:32
Show Gist options
  • Save svantelidman/072fb1c8c01f98d9b36627f2a36edf2d to your computer and use it in GitHub Desktop.
Save svantelidman/072fb1c8c01f98d9b36627f2a36edf2d to your computer and use it in GitHub Desktop.
use std::io::{BufReader, BufRead};
use std::env;
use std::fs::File;
use std::collections::HashSet;
fn main() {
let args: Vec<String> = env::args().collect();
let program_file = &args[1];
let program: Vec<i32> = load_program(program_file);
let all_conf = generate_configurations(5..10);
let connections = vec!((0, 1), (1, 2), (2, 3), (3, 4), (4, 0));
let all_signals: Vec<i32> = all_conf.iter().map(|conf| run_piped_machines(&program, &conf, 0, &connections)).collect();
let max_signal = all_signals.iter().fold(std::i32::MIN, |acc, x| if acc > *x {acc} else {*x});
println!("Max signal: {}", max_signal);
}
fn initialize_machines(program: &Vec<i32>, conf: &Vec<i32>) -> Vec<Machine> {
let mut machines: Vec<Machine> = vec!();
for phase_conf in conf {
machines.push(
Machine{
prog_ptr: 0,
program: program.clone(),
input: vec!(*phase_conf),
output: vec!(),
state: MachineState::ReadyToRun
}
)
}
machines
}
fn run_piped_machines(program: &Vec<i32>, phase_conf: &Vec<i32>, first_input: i32, connections: &Vec<(usize, usize)>) -> i32 {
let mut machines = initialize_machines(program, phase_conf);
machines[0].input.push(first_input);
loop {
shift_buffers(&mut machines, connections);
for ind in 0..machines.len() {
if machines[ind].state == MachineState::ReadyToRun || (machines[ind].state == MachineState::WaitingForInput && machines[ind].input.len() > 0) {
machines[ind].resume()
}
}
if machines.iter().all(|m| m.state == MachineState::Terminated) {
break;
}
}
machines[4].output[0]
}
fn shift_buffers(machines: &mut Vec<Machine>, connections: &Vec<(usize, usize)>) {
for (out, inp) in connections {
while !machines[*out].output.is_empty() {
let o = machines[*out].output.remove(0);
machines[*inp].input.push(o)
}
}
}
struct Machine {
prog_ptr: usize,
program: Vec<i32>,
input: Vec<i32>,
output: Vec<i32>,
state: MachineState
}
#[derive(PartialEq)]
enum MachineState {
ReadyToRun,
WaitingForInput,
Terminated
}
enum InstructionResult{
Continue,
WaitForInput,
Invalid,
Exit
}
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::Exit => { self.state = MachineState::Terminated; break; },
_ => panic!("Unexpected action."),
}
}
}
fn next_cell(&mut self) -> i32 {
let ind = self.prog_ptr;
self.prog_ptr += 1;
self.program[ind]
}
fn get_parameter_value(&mut self, mode: i32) -> i32 {
let value = self.next_cell();
if mode == 0 {
return self.program[value as usize]
} else if mode == 1 {
return value
} else {
panic!("Unknown parameter mode: {}", mode)
}
}
const ADD: i32 = 1;
const MULT: i32 = 2;
const INPUT: i32 = 3;
const OUTPUT: i32 = 4;
const JUMP_TRUE: i32 = 5;
const JUMP_FALSE: i32 = 6;
const LESS_THAN: i32 = 7;
const EQUALS: i32 = 8;
const EXIT: i32 = 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;
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.next_cell() as usize;
self.program[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.next_cell() as usize;
self.program[store_at] = f1 * f2;
InstructionResult::Continue
},
Machine::INPUT => {
if !self.input.is_empty() {
let store_at = self.next_cell() as usize;
let c = self.input.remove(0);
self.program[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 as usize
}
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 as usize
}
InstructionResult::Continue
},
Machine::LESS_THAN => {
let p1 = self.get_parameter_value(mode_par_1);
let p2 = self.get_parameter_value(mode_par_2);
let p3 = self.next_cell();
let result = if p1 < p2 { 1 } else { 0 };
self.program[p3 as usize] = result;
InstructionResult::Continue
},
Machine::EQUALS => {
let p1 = self.get_parameter_value(mode_par_1);
let p2 = self.get_parameter_value(mode_par_2);
let p3 = self.next_cell();
let result = if p1 == p2 { 1 } else { 0 };
self.program[p3 as usize] = result;
InstructionResult::Continue
},
Machine::OUTPUT => {
let v = self.get_parameter_value(mode_par_1);
self.output.push(v);
InstructionResult::Continue
},
Machine::EXIT => InstructionResult::Exit,
_ => InstructionResult::Invalid
}
}
}
fn load_program(program_file: &String) -> Vec<i32> {
let mut prog: Vec<i32> = 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 stdin");
let line = line.expect("No string there.");
for s in line.split(",") {
let c = i32::from_str_radix(&s, 10).expect("Could not parse int");
prog.push(c);
}
prog
}
fn generate_configurations(conf_range: std::ops::Range<i32> ) -> Vec<Vec<i32>> {
let mut conf: Vec<Vec<i32>> = vec!();
for c1 in (&conf_range).clone() {
for c2 in (&conf_range).clone() {
for c3 in (&conf_range).clone() {
for c4 in (&conf_range).clone() {
for c5 in (&conf_range).clone() {
let v = vec!(c1, c2, c3, c4, c5);
let mut s = HashSet::new();
for c in &v {
s.insert(c);
}
if s.len() == v.len() {
conf.push(v)
}
}
}
}
}
}
conf
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment