-
-
Save Gordin508/2d44024d7c9f6b9bf2fe201802c8c019 to your computer and use it in GitHub Desktop.
Advent of Code 2019 - Day 02 // Intcode Computer
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
pub struct IntcodeVM { | |
memory: Vec<usize>, | |
ip: usize | |
} | |
impl IntcodeVM { | |
pub fn new(state: Vec<usize>) -> IntcodeVM { | |
IntcodeVM { memory: state, ip: 0 } | |
} | |
pub fn readmem(&self, address: usize) -> usize { | |
self.memory[address] | |
} | |
pub fn run(&mut self) { | |
loop { | |
let opcode = self.memory[self.ip]; | |
if opcode == 1 || opcode == 2 { | |
let src1 = self.memory[self.ip + 1]; | |
let src2 = self.memory[self.ip + 2]; | |
let dest = self.memory[self.ip + 3]; | |
self.memory[dest] = match opcode { | |
1 => self.memory[src1] + self.memory[src2], | |
2 => self.memory[src1] * self.memory[src2], | |
_ => panic!("Error in program logic") | |
}; | |
self.ip += 4; | |
} else if opcode == 99 { | |
self.ip += 1; | |
return; | |
} | |
} | |
} | |
} | |
fn part1(lines: &Vec<&str>) -> Option<i64> { | |
assert_eq!(1, lines.len()); | |
let mut input_seq = lines[0].split(",") | |
.map(|n| n.parse::<usize>().unwrap()) | |
.collect::<Vec<_>>(); | |
input_seq[1] = 12; | |
input_seq[2] = 2; | |
let mut vm = IntcodeVM::new(input_seq); | |
vm.run(); | |
Some(vm.readmem(0) as i64) | |
} | |
fn part2(lines: &Vec<&str>) -> Option<i64> { | |
assert_eq!(1, lines.len()); | |
const TARGET: usize = 19690720; | |
let input_seq = lines[0].split(",") | |
.map(|n| n.parse::<usize>().unwrap()) | |
.collect::<Vec<_>>(); | |
// helper function to simulate the VM on a given input | |
let run_vm_on_input = |noun, verb| -> usize { | |
let mut initialstate = input_seq.clone(); | |
initialstate[1] = noun; | |
initialstate[2] = verb; | |
let mut vm = IntcodeVM::new(initialstate); | |
vm.run(); | |
vm.readmem(0) | |
}; | |
// assuming the machine always realizes a linear function, we got 3 unknowns | |
// y = noun_fac * noun + verb_fac * verb + offset | |
let offset = run_vm_on_input(0, 0); | |
let noun_fac = run_vm_on_input(1, 0) - offset; | |
let verb_fac = run_vm_on_input(0, 1) - offset; | |
// Assuming the VM always realizes a linear function, | |
// the answer may not be unique. | |
// We minimize the sum (noun + verb) | |
let diff = TARGET - offset; | |
let noun = if noun_fac >= verb_fac {diff / noun_fac} else {(diff % verb_fac) / noun_fac}; | |
let verb = if noun_fac >= verb_fac {(diff % noun_fac) / verb_fac} else {diff / verb_fac}; | |
assert_eq!(run_vm_on_input(noun, verb), TARGET); | |
Some((100 * noun + verb) as i64) | |
} | |
fn main() { | |
use std::fs; | |
use std::env; | |
use std::time::Instant; | |
let args: Vec<String> = env::args().collect(); | |
let infile = args.get(1).unwrap_or_else(|| { | |
println!("Usage: {} <puzzle input>", args[0]); | |
std::process::exit(1); | |
}); | |
let contents = fs::read_to_string(infile) | |
.expect("Could not read in file"); | |
let lines: Vec<&str> = contents.lines().collect(); | |
// execute part 1 and part 2, print their results if they exist | |
// later parts may follow, so we loop over the part functions | |
let parts = [part1, part2]; | |
for (index, part) in parts.iter().enumerate() { | |
let partstart = Instant::now(); | |
let result = part(&lines); | |
match result { | |
Some(result) => println!("Part {}: {}\t({:?})", index+1, result, partstart.elapsed()), | |
None => println!("Part {}: No result", index+1), | |
} | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
#[test] | |
fn test_intcodevm() { | |
let state = vec![1,1,1,4,99,5,6,0,99]; | |
let mut vm = IntcodeVM::new(state); | |
vm.run(); | |
assert_eq!([30,1,1,4,2,5,6,0,99].as_slice(), vm.memory.as_slice()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Your solution to part 2 is very cool! Great Work!