advent-of-code/2019/07/src/main.rs

213 lines
6.6 KiB
Rust
Raw Normal View History

2019-12-07 06:25:53 +01:00
use itertools::Itertools;
use std::io;
use std::io::BufRead;
2019-12-07 08:21:27 +01:00
use std::ops::Range;
2019-12-07 06:25:53 +01:00
#[derive(Debug)]
enum Operation {
2019-12-07 07:22:19 +01:00
Add { x: i64, y: i64, addr: usize },
Multiply { x: i64, y: i64, addr: usize },
Input { value: i64, addr: usize },
Output { value: i64 },
JumpIfTrue { value: i64, addr: i64 },
JumpIfFalse { value: i64, addr: i64 },
LessThan { first: i64, second: i64, addr: i64 },
Equals { first: i64, second: i64, addr: i64 },
2019-12-07 06:25:53 +01:00
}
#[derive(Debug)]
enum Mode {
Immediate,
Position,
}
enum ParameterPosition {
First,
Second,
}
2019-12-07 08:21:27 +01:00
struct Machine {
pos: i64,
tape: Vec<i64>,
params: Vec<i64>,
}
2019-12-07 06:25:53 +01:00
impl Into<usize> for ParameterPosition {
fn into(self) -> usize {
match self {
ParameterPosition::First => 2,
ParameterPosition::Second => 3,
}
}
}
impl Into<Mode> for &char {
fn into(self) -> Mode {
match self {
'0' => Mode::Position,
'1' => Mode::Immediate,
_ => unreachable!(),
}
}
}
2019-12-07 07:22:19 +01:00
fn get_next(input: &[i64], pos: &mut i64, mode: Mode) -> i64 {
2019-12-07 06:25:53 +01:00
let value = input[*pos as usize];
let next = match mode {
Mode::Position => input[value as usize],
Mode::Immediate => value,
};
*pos += 1;
next
}
fn get_mode(raw_opcode: &[char], pos: ParameterPosition) -> Mode {
raw_opcode.get::<usize>(pos.into()).unwrap_or(&'0').into()
}
2019-12-07 08:21:27 +01:00
#[rustfmt::skip]
fn next_operation(machine: &mut Machine) -> Option<Operation> {
let next = get_next(&machine.tape, &mut machine.pos, Mode::Immediate);
2019-12-07 06:25:53 +01:00
let mut raw_opcode: Vec<_> = next.to_string().chars().collect();
raw_opcode.reverse();
match raw_opcode[0] {
'1' => Some(Operation::Add {
2019-12-07 08:21:27 +01:00
x: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)),
y: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::Second)),
addr: get_next(&machine.tape, &mut machine.pos, Mode::Immediate) as usize,
2019-12-07 06:25:53 +01:00
}),
'2' => Some(Operation::Multiply {
2019-12-07 08:21:27 +01:00
x: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)),
y: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::Second)),
addr: get_next(&machine.tape, &mut machine.pos, Mode::Immediate) as usize,
2019-12-07 06:25:53 +01:00
}),
'3' => Some(Operation::Input {
2019-12-07 08:21:27 +01:00
value: machine.params.pop().unwrap(),
addr: get_next(&machine.tape, &mut machine.pos, Mode::Immediate) as usize,
2019-12-07 06:25:53 +01:00
}),
'4' => Some(Operation::Output {
2019-12-07 08:21:27 +01:00
value: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)),
2019-12-07 06:25:53 +01:00
}),
'5' => Some(Operation::JumpIfTrue {
2019-12-07 08:21:27 +01:00
value: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)),
addr: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::Second)),
2019-12-07 06:25:53 +01:00
}),
'6' => Some(Operation::JumpIfFalse {
2019-12-07 08:21:27 +01:00
value: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)),
addr: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::Second)),
2019-12-07 06:25:53 +01:00
}),
'7' => Some(Operation::LessThan {
2019-12-07 08:21:27 +01:00
first: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)),
second: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::Second)),
addr: get_next(&machine.tape, &mut machine.pos, Mode::Immediate),
2019-12-07 06:25:53 +01:00
}),
'8' => Some(Operation::Equals {
2019-12-07 08:21:27 +01:00
first: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)),
second: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::Second)),
addr: get_next(&machine.tape, &mut machine.pos, Mode::Immediate),
2019-12-07 06:25:53 +01:00
}),
'9' => None,
_ => unreachable!(),
}
}
2019-12-07 07:22:19 +01:00
fn execute(op: Operation, input: &mut Vec<i64>, pos: &mut i64) -> Option<i64> {
2019-12-07 06:25:53 +01:00
match op {
Operation::Add { x, y, addr } => {
input[addr as usize] = x + y;
None
}
Operation::Multiply { x, y, addr } => {
input[addr as usize] = x * y;
None
}
Operation::Input { value, addr } => {
input[addr] = value;
None
}
Operation::Output { value } => Some(value),
Operation::JumpIfTrue { value, addr } => {
if value != 0 {
*pos = addr
}
None
}
Operation::JumpIfFalse { value, addr } => {
if value == 0 {
*pos = addr
}
None
}
Operation::LessThan {
first,
second,
addr,
} => {
2019-12-07 07:22:19 +01:00
input[addr as usize] = (first < second) as i64;
2019-12-07 06:25:53 +01:00
None
}
Operation::Equals {
first,
second,
addr,
} => {
2019-12-07 07:22:19 +01:00
input[addr as usize] = (first == second) as i64;
2019-12-07 06:25:53 +01:00
None
}
}
}
2019-12-07 08:21:27 +01:00
fn find_max(range: Range<i64>, input: &Vec<i64>) -> i64 {
range
2019-12-07 08:02:10 +01:00
.permutations(5)
2019-12-07 08:29:19 +01:00
.scan(0, |&mut mut acc, amps| {
2019-12-07 08:21:27 +01:00
let mut machines: Vec<_> = amps
.into_iter()
.map(|amp| Machine {
tape: input.clone(),
pos: 0,
params: vec![amp],
})
.collect();
2019-12-07 08:02:10 +01:00
for state in (0..5).cycle() {
2019-12-07 08:21:27 +01:00
let mut machine = machines.get_mut(state).unwrap();
2019-12-07 08:29:19 +01:00
machine.params.insert(0, acc);
2019-12-07 08:21:27 +01:00
match execute_machine(&mut machine) {
2019-12-07 08:29:19 +01:00
Err(output) => acc = output,
2019-12-07 08:02:10 +01:00
Ok(_) => break,
}
}
2019-12-07 08:29:19 +01:00
Some(acc)
2019-12-07 08:02:10 +01:00
})
.max()
2019-12-07 08:21:27 +01:00
.unwrap()
2019-12-07 08:02:10 +01:00
}
2019-12-07 08:21:27 +01:00
fn execute_machine(machine: &mut Machine) -> Result<i64, i64> {
2019-12-07 08:02:10 +01:00
loop {
2019-12-07 08:21:27 +01:00
match next_operation(machine) {
2019-12-07 08:02:10 +01:00
Some(op) => {
2019-12-07 08:21:27 +01:00
if let Some(o) = execute(op, &mut machine.tape, &mut machine.pos) {
2019-12-07 08:02:10 +01:00
return Err(o);
2019-12-07 07:22:19 +01:00
}
}
2019-12-07 08:21:27 +01:00
None => return Ok(machine.params.pop().unwrap()),
2019-12-07 07:22:19 +01:00
}
2019-12-07 08:02:10 +01:00
}
2019-12-07 06:25:53 +01:00
}
2019-12-07 08:21:27 +01:00
pub fn main() {
2019-12-07 08:29:19 +01:00
let input: Vec<_> = io::stdin()
2019-12-07 08:21:27 +01:00
.lock()
.lines()
.next()
.unwrap()
.unwrap()
.split(',')
.map(|n| n.parse().unwrap())
.collect();
println!("Part 1: {}", find_max(0..5, &input));
println!("Part 2: {}", find_max(5..10, &input));
}