Last active
December 7, 2019 15:32
-
-
Save svantelidman/072fb1c8c01f98d9b36627f2a36edf2d 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
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