Created
December 8, 2019 23:45
-
-
Save ltriant/2e01fd870fd0e5e41325d8dec4e66f7b to your computer and use it in GitHub Desktop.
Advent of Code 2019 Day 7
This file contains hidden or 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::collections::VecDeque; | |
use itertools::Itertools; | |
#[derive(PartialEq)] | |
enum AddressingMode { | |
Position, | |
Immediate, | |
} | |
impl From<u8> for AddressingMode { | |
fn from(v: u8) -> Self { | |
match v { | |
0 => AddressingMode::Position, | |
1 => AddressingMode::Immediate, | |
_ => unreachable!(), | |
} | |
} | |
} | |
fn digits(num: i64) -> [u8; 4] { | |
[ | |
((num / 10 % 10) as u8 * 10) + (num % 10) as u8, | |
(num / 100 % 10) as u8, | |
(num / 1_000 % 10) as u8, | |
(num / 10_000 % 10) as u8, | |
] | |
} | |
fn get_operands(mem: &Vec<i64>, pc: usize, n: usize) -> Vec<i64> { | |
let opcode = digits(mem[pc]); | |
(1 ..= n) | |
.map(|i| { | |
match AddressingMode::from(opcode[i]) { | |
AddressingMode::Position => mem[mem[pc + i] as usize], | |
AddressingMode::Immediate => mem[pc + i], | |
} | |
} ) | |
.collect::<Vec<_>>() | |
} | |
enum InputState { | |
PhaseSetting, | |
InputSignal, | |
} | |
struct CPU { | |
mem: Vec<i64>, | |
pc: usize, | |
input_state: InputState, | |
phase_setting: i64, | |
} | |
impl CPU { | |
fn new_cpu(mem: Vec<i64>, phase_setting: i64) -> Self { | |
Self { | |
mem: mem, | |
pc: 0, | |
input_state: InputState::PhaseSetting, | |
phase_setting: phase_setting, | |
} | |
} | |
fn run_machine(&mut self, input_signal: i64) -> Option<i64> { | |
loop { | |
let original_op = self.mem[self.pc]; | |
let op = digits(original_op); | |
match op[0] { | |
1 => { | |
let operands = get_operands(&self.mem, self.pc, 2); | |
let dest = self.mem[self.pc + 3] as usize; | |
self.mem[dest] = operands[0] as i64 + operands[1] as i64; | |
println!("{:05} ADD {} {} -> {}", | |
original_op, | |
operands[0], | |
operands[1], | |
dest); | |
self.pc += 4; | |
}, | |
2 => { | |
let operands = get_operands(&self.mem, self.pc, 2); | |
let dest = self.mem[self.pc + 3] as usize; | |
self.mem[dest] = operands[0] as i64 * operands[1] as i64; | |
println!("{:05} MUL {} {} -> {}", | |
original_op, | |
operands[0], | |
operands[1], | |
dest); | |
self.pc += 4; | |
}, | |
3 => { | |
let operand1 = self.mem[self.pc + 1] as usize; | |
match self.input_state { | |
InputState::PhaseSetting => { | |
self.mem[operand1] = self.phase_setting; | |
self.input_state = InputState::InputSignal; | |
}, | |
InputState::InputSignal => { | |
self.mem[operand1] = input_signal; | |
}, | |
} | |
println!("{:05} IN -> {}", original_op, operand1); | |
self.pc += 2; | |
}, | |
4 => { | |
let operand1 = self.mem[self.pc + 1] as usize; | |
println!("{:05} OUT -> {}", original_op, operand1); | |
self.pc += 2; | |
return Some(self.mem[operand1]); | |
//println!("{}", self.mem[operand1]); | |
}, | |
5 => { | |
let operands = get_operands(&self.mem, self.pc, 2); | |
if operands[0] != 0 { | |
self.pc = operands[1] as usize; | |
} else { | |
self.pc += 3; | |
} | |
println!("{:05} JNZ {} -> {}", | |
original_op, | |
operands[0], | |
operands[1]); | |
}, | |
6 => { | |
let operands = get_operands(&self.mem, self.pc, 2); | |
if operands[0] == 0 { | |
self.pc = operands[1] as usize; | |
} else { | |
self.pc += 3; | |
} | |
println!("{:05} JZ {} -> {}", | |
original_op, | |
operands[0], | |
operands[1]); | |
}, | |
7 => { | |
let operands = get_operands(&self.mem, self.pc, 2); | |
let dest = self.mem[self.pc + 3] as usize; | |
if operands[0] < operands[1] { | |
self.mem[dest] = 1; | |
} else { | |
self.mem[dest] = 0; | |
} | |
println!("{:05} JL {} {} -> {}", | |
original_op, | |
operands[0], | |
operands[1], | |
dest); | |
self.pc += 4; | |
}, | |
8 => { | |
let operands = get_operands(&self.mem, self.pc, 2); | |
let dest = self.mem[self.pc + 3] as usize; | |
if operands[0] == operands[1] { | |
self.mem[dest] = 1; | |
} else { | |
self.mem[dest] = 0; | |
} | |
println!("{:05} JEQ {} {} -> {}", | |
original_op, | |
operands[0], | |
operands[1], | |
dest); | |
self.pc += 4; | |
}, | |
99 => { | |
println!("{:05} HLT", original_op); | |
break; | |
}, | |
_ => { | |
println!("got opcode: {} -> {}", op[3], self.mem[self.pc]); | |
break; | |
}, | |
} | |
} | |
return None; | |
} | |
} | |
#[allow(dead_code)] | |
pub fn part1() { | |
let mem = include_str!("day07.input") | |
.lines() | |
.flat_map(|line| line.split(',')) | |
.map(|c| c.parse::<i64>().unwrap()) | |
.collect::<Vec<_>>(); | |
let max_output = (0 .. 5).permutations(5) | |
.map(|inputs| { | |
inputs.iter().fold(0, |input_signal, &phase_setting| { | |
CPU::new_cpu(mem.clone(), phase_setting as i64) | |
.run_machine(input_signal) | |
.unwrap() | |
} ) | |
} ) | |
.max() | |
.unwrap(); | |
println!("Part 1 {}", max_output); | |
} | |
#[allow(dead_code)] | |
pub fn part2() { | |
let mem = include_str!("day07.input") | |
.lines() | |
.flat_map(|line| line.split(',')) | |
.map(|c| c.parse::<i64>().unwrap()) | |
.collect::<Vec<_>>(); | |
let max_output = (5 ..= 9).permutations(5) | |
.map(|inputs| { | |
let mut amps = (0 .. 5).map(|_| mem.clone()) | |
.zip(inputs.iter()) | |
.map(|(mem, &phase_setting)| CPU::new_cpu(mem, phase_setting)) | |
.collect::<VecDeque<_>>(); | |
let mut input = 0; | |
while let Some(mut amp) = amps.pop_front() { | |
if let Some(output) = amp.run_machine(input) { | |
input = output; | |
amps.push_back(amp); | |
} | |
} | |
input | |
} ) | |
.max() | |
.unwrap(); | |
println!("Part 2 {}", max_output); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment