Skip to content

Instantly share code, notes, and snippets.

@Gordin508
Forked from icub3d/main.rs
Last active January 25, 2024 03:54
Show Gist options
  • Save Gordin508/2d44024d7c9f6b9bf2fe201802c8c019 to your computer and use it in GitHub Desktop.
Save Gordin508/2d44024d7c9f6b9bf2fe201802c8c019 to your computer and use it in GitHub Desktop.
Advent of Code 2019 - Day 02 // Intcode Computer
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());
}
}
@icub3d
Copy link

icub3d commented Jan 25, 2024

Your solution to part 2 is very cool! Great Work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment