Skip to content

Instantly share code, notes, and snippets.

@svantelidman
Created December 17, 2019 15:39
Show Gist options
  • Select an option

  • Save svantelidman/af6a4965d15f80bfbd0979323cf4ca7f to your computer and use it in GitHub Desktop.

Select an option

Save svantelidman/af6a4965d15f80bfbd0979323cf4ca7f to your computer and use it in GitHub Desktop.
use std::io::{BufReader, BufRead};
use std::fs::File;
use std::collections::HashMap;
struct Machine {
prog_ptr: i64,
relative_base: i64,
program: Vec<i64>,
input: Vec<i64>,
output: Vec<i64>,
state: MachineState
}
#[derive(PartialEq)]
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 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 machine = Machine {
prog_ptr: 0,
relative_base: 0,
program: program.clone(),
input: vec!(),
output: vec!(),
state: MachineState::ReadyToRun
};
let mut area: HashMap<(i64, i64), char> = HashMap::new();
let mut cx = 0;
let mut cy = 0;
loop {
match machine.state {
MachineState::Terminated => break,
MachineState::OutputAvailable => {
let code = machine.output.remove(0);
if code == 10 {
cx = 0;
cy += 1;
} else {
let c = std::char::from_u32(code as u32).unwrap();
area.insert((cx, cy), c);
cx += 1;
}
},
MachineState::WaitingForInput => {
panic!("Unexpected input requested.")
},
MachineState::ReadyToRun => {},
}
machine.resume()
}
paint_area(&area);
let intersections = find_intersections(&area);
let summed_alignment = intersections.iter().fold(0, |acc, (x, y)| acc + x * y);
println!("Solution part 1 = {}", summed_alignment);
}
fn part_2(program: &Vec<i64>) {
//
// Found by playing around with the full path in the text editor manually.
//
let ascii_input = "A,B,A,B,A,C,B,C,A,C\nR,4,L,10,L,10\nL,8,R,12,R,10,R,4\nL,8,L,8,R,10,R,4\nn\n";
let mut int_input: Vec<i64> = ascii_input.chars().map(|c| c as i64).collect();
let mut program_part_2 = program.clone();
program_part_2[0] = 2;
let mut machine = Machine {
prog_ptr: 0,
relative_base: 0,
program: program_part_2,
input: vec!(),
output: vec!(),
state: MachineState::ReadyToRun
};
let mut last_output = -1;
loop {
match machine.state {
MachineState::Terminated => break,
MachineState::OutputAvailable => {
last_output = machine.output.remove(0);
},
MachineState::WaitingForInput => {
machine.input.push(int_input.remove(0));
},
MachineState::ReadyToRun => {},
}
machine.resume()
}
println!("Solution part 2 = {}", last_output);
}
fn main() {
let program_file = "prog".to_string();
let program: Vec<i64> = load_program(&program_file);
part_1(&program);
part_2(&program);
}
fn find_intersections(area: &HashMap<(i64, i64), char>) -> Vec<(i64, i64)> {
let ((min_x, min_y), (max_x, max_y)) = find_dimensions(&area);
area.iter().filter(
|((x, y), v)|
**v == '#' && *x != min_x && *x != max_x && *y != min_y && *y != max_y &&
*area.get(&(*x + 1, *y)).unwrap() == '#' &&
*area.get(&(*x - 1, *y)).unwrap() == '#' &&
*area.get(&(*x, *y + 1)).unwrap() == '#' &&
*area.get(&(*x, *y - 1)).unwrap() == '#'
).map(|(k, _)| *k).collect()
}
fn find_dimensions(area: &HashMap<(i64, i64), char>) -> ((i64, i64), (i64, i64)) {
let min_x = area.keys().fold(0, |acc, k| std::cmp::min(acc, k.0));
let max_x = area.keys().fold(0, |acc, k| std::cmp::max(acc, k.0));
let min_y = area.keys().fold(0, |acc, k| std::cmp::min(acc, k.1));
let max_y = area.keys().fold(0, |acc, k| std::cmp::max(acc, k.1));
((min_x, min_y), (max_x, max_y))
}
fn paint_area(area: &HashMap<(i64, i64), char>) {
let ((min_x, min_y), (max_x, max_y)) = find_dimensions(&area);
for y in min_y..=max_y {
for x in min_x..=max_x {
print!("{}", area.get(&(x, y)).unwrap())
}
println!();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment