Refactor intcode into separate module
This commit is contained in:
parent
3a3283bf72
commit
56dbed3418
|
@ -8,3 +8,4 @@ edition = "2018"
|
|||
|
||||
[dependencies]
|
||||
itertools = "0.8.2"
|
||||
intcode = { path = "../intcode" }
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
3,8,1001,8,10,8,105,1,0,0,21,30,51,76,101,118,199,280,361,442,99999,3,9,102,5,9,9,4,9,99,3,9,102,4,9,9,1001,9,3,9,102,2,9,9,101,2,9,9,4,9,99,3,9,1002,9,3,9,1001,9,4,9,102,5,9,9,101,3,9,9,1002,9,3,9,4,9,99,3,9,101,5,9,9,102,4,9,9,1001,9,3,9,1002,9,2,9,101,4,9,9,4,9,99,3,9,1002,9,2,9,1001,9,3,9,102,5,9,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,99,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,99,3,9,1001,9,1,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,99,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,99,3,9,102,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,1001,9,2,9,4,9,99
|
|
@ -1,212 +1,7 @@
|
|||
use itertools::Itertools;
|
||||
use std::io;
|
||||
use std::io::BufRead;
|
||||
use std::ops::Range;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Operation {
|
||||
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 },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Mode {
|
||||
Immediate,
|
||||
Position,
|
||||
}
|
||||
|
||||
enum ParameterPosition {
|
||||
First,
|
||||
Second,
|
||||
}
|
||||
|
||||
struct Machine {
|
||||
pos: i64,
|
||||
tape: Vec<i64>,
|
||||
params: Vec<i64>,
|
||||
}
|
||||
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_next(input: &[i64], pos: &mut i64, mode: Mode) -> i64 {
|
||||
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()
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn next_operation(machine: &mut Machine) -> Option<Operation> {
|
||||
let next = get_next(&machine.tape, &mut machine.pos, Mode::Immediate);
|
||||
let mut raw_opcode: Vec<_> = next.to_string().chars().collect();
|
||||
raw_opcode.reverse();
|
||||
match raw_opcode[0] {
|
||||
'1' => Some(Operation::Add {
|
||||
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,
|
||||
}),
|
||||
'2' => Some(Operation::Multiply {
|
||||
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,
|
||||
}),
|
||||
'3' => Some(Operation::Input {
|
||||
value: machine.params.pop().unwrap(),
|
||||
addr: get_next(&machine.tape, &mut machine.pos, Mode::Immediate) as usize,
|
||||
}),
|
||||
'4' => Some(Operation::Output {
|
||||
value: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)),
|
||||
}),
|
||||
'5' => Some(Operation::JumpIfTrue {
|
||||
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)),
|
||||
}),
|
||||
'6' => Some(Operation::JumpIfFalse {
|
||||
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)),
|
||||
}),
|
||||
'7' => Some(Operation::LessThan {
|
||||
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),
|
||||
}),
|
||||
'8' => Some(Operation::Equals {
|
||||
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),
|
||||
}),
|
||||
'9' => None,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn execute(op: Operation, input: &mut Vec<i64>, pos: &mut i64) -> Option<i64> {
|
||||
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,
|
||||
} => {
|
||||
input[addr as usize] = (first < second) as i64;
|
||||
None
|
||||
}
|
||||
Operation::Equals {
|
||||
first,
|
||||
second,
|
||||
addr,
|
||||
} => {
|
||||
input[addr as usize] = (first == second) as i64;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_max(range: Range<i64>, input: &Vec<i64>) -> i64 {
|
||||
range
|
||||
.permutations(5)
|
||||
.scan(0, |&mut mut acc, amps| {
|
||||
let mut machines: Vec<_> = amps
|
||||
.into_iter()
|
||||
.map(|amp| Machine {
|
||||
tape: input.clone(),
|
||||
pos: 0,
|
||||
params: vec![amp],
|
||||
})
|
||||
.collect();
|
||||
for state in (0..5).cycle() {
|
||||
let mut machine = machines.get_mut(state).unwrap();
|
||||
machine.params.insert(0, acc);
|
||||
match execute_machine(&mut machine) {
|
||||
Err(output) => acc = output,
|
||||
Ok(_) => break,
|
||||
}
|
||||
}
|
||||
Some(acc)
|
||||
})
|
||||
.max()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn execute_machine(machine: &mut Machine) -> Result<i64, i64> {
|
||||
loop {
|
||||
match next_operation(machine) {
|
||||
Some(op) => {
|
||||
if let Some(o) = execute(op, &mut machine.tape, &mut machine.pos) {
|
||||
return Err(o);
|
||||
}
|
||||
}
|
||||
None => return Ok(machine.params.pop().unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
use intcode::*;
|
||||
|
||||
pub fn main() {
|
||||
let input: Vec<_> = io::stdin()
|
||||
.lock()
|
||||
.lines()
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.split(',')
|
||||
.map(|n| n.parse().unwrap())
|
||||
.collect();
|
||||
|
||||
let input = read_input();
|
||||
println!("Part 1: {}", find_max(0..5, &input));
|
||||
println!("Part 2: {}", find_max(5..10, &input));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "intcode"
|
||||
version = "0.1.0"
|
||||
authors = ["kageru <kageru@encode.moe>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
itertools = "0.8.2"
|
|
@ -0,0 +1,204 @@
|
|||
use itertools::Itertools;
|
||||
use std::io::{self, BufRead};
|
||||
use std::ops::Range;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Operation {
|
||||
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 },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Mode {
|
||||
Immediate,
|
||||
Position,
|
||||
}
|
||||
|
||||
enum ParameterPosition {
|
||||
First,
|
||||
Second,
|
||||
}
|
||||
|
||||
struct Machine {
|
||||
pos: i64,
|
||||
tape: Vec<i64>,
|
||||
params: Vec<i64>,
|
||||
}
|
||||
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_next(input: &[i64], pos: &mut i64, mode: Mode) -> i64 {
|
||||
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()
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn decode_next(machine: &mut Machine) -> Option<Operation> {
|
||||
let next = get_next(&machine.tape, &mut machine.pos, Mode::Immediate);
|
||||
let mut raw_opcode: Vec<_> = next.to_string().chars().collect();
|
||||
raw_opcode.reverse();
|
||||
match raw_opcode[0] {
|
||||
'1' => Some(Operation::Add {
|
||||
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,
|
||||
}),
|
||||
'2' => Some(Operation::Multiply {
|
||||
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,
|
||||
}),
|
||||
'3' => Some(Operation::Input {
|
||||
value: machine.params.pop().unwrap(),
|
||||
addr: get_next(&machine.tape, &mut machine.pos, Mode::Immediate) as usize,
|
||||
}),
|
||||
'4' => Some(Operation::Output {
|
||||
value: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)),
|
||||
}),
|
||||
'5' => Some(Operation::JumpIfTrue {
|
||||
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)),
|
||||
}),
|
||||
'6' => Some(Operation::JumpIfFalse {
|
||||
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)),
|
||||
}),
|
||||
'7' => Some(Operation::LessThan {
|
||||
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),
|
||||
}),
|
||||
'8' => Some(Operation::Equals {
|
||||
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),
|
||||
}),
|
||||
'9' => None,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn execute(op: Operation, input: &mut Vec<i64>, pos: &mut i64) -> Option<i64> {
|
||||
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,
|
||||
} => {
|
||||
input[addr as usize] = (first < second) as i64;
|
||||
None
|
||||
}
|
||||
Operation::Equals {
|
||||
first,
|
||||
second,
|
||||
addr,
|
||||
} => {
|
||||
input[addr as usize] = (first == second) as i64;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_max(range: Range<i64>, input: &Vec<i64>) -> i64 {
|
||||
range
|
||||
.permutations(5)
|
||||
.scan(0, |&mut mut acc, amps| {
|
||||
let mut machines: Vec<_> = amps
|
||||
.into_iter()
|
||||
.map(|amp| Machine {
|
||||
tape: input.clone(),
|
||||
pos: 0,
|
||||
params: vec![amp],
|
||||
})
|
||||
.collect();
|
||||
for state in (0..5).cycle() {
|
||||
let mut machine = machines.get_mut(state).unwrap();
|
||||
machine.params.insert(0, acc);
|
||||
match execute_machine(&mut machine) {
|
||||
Err(output) => acc = output,
|
||||
Ok(_) => break,
|
||||
}
|
||||
}
|
||||
Some(acc)
|
||||
})
|
||||
.max()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn execute_machine(machine: &mut Machine) -> Result<i64, i64> {
|
||||
loop {
|
||||
match decode_next(machine) {
|
||||
Some(op) => {
|
||||
if let Some(o) = execute(op, &mut machine.tape, &mut machine.pos) {
|
||||
return Err(o);
|
||||
}
|
||||
}
|
||||
None => return Ok(machine.params.pop().unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn read_input() -> Vec<i64> {
|
||||
io::stdin().lock().lines().next().unwrap().unwrap().split(',').map(|n| n.parse().unwrap()).collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
|
@ -0,0 +1,49 @@
|
|||
use super::*;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn parse_test_input(raw: &str) -> Vec<i64> {
|
||||
raw.split(",").map(|x| x.parse().unwrap()).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_max() {
|
||||
assert_eq!(
|
||||
find_max(
|
||||
0..5,
|
||||
&parse_test_input("3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0")
|
||||
),
|
||||
43210
|
||||
);
|
||||
assert_eq!(
|
||||
find_max(
|
||||
0..5,
|
||||
&parse_test_input("3,23,3,24,1002,24,10,24,1002,23,-1,23,101,5,23,23,1,24,23,23,4,23,99,0,0")
|
||||
),
|
||||
54321
|
||||
);
|
||||
assert_eq!(
|
||||
find_max(
|
||||
0..5,
|
||||
&parse_test_input("3,31,3,32,1002,32,10,32,1001,31,-2,31,1007,31,0,33,1002,33,7,33,1,33,31,31,1,32,31,31,4,31,99,0,0,0")
|
||||
),
|
||||
65210
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_max_with_loops() {
|
||||
assert_eq!(
|
||||
find_max(
|
||||
5..10,
|
||||
&parse_test_input("3,26,1001,26,-4,26,3,27,1002,27,2,27,1,27,26,27,4,27,1001,28,-1,28,1005,28,6,99,0,0,5")
|
||||
),
|
||||
139629729
|
||||
);
|
||||
assert_eq!(
|
||||
find_max(
|
||||
5..10,
|
||||
&parse_test_input("3,52,1001,52,-5,52,3,53,1,52,56,54,1007,54,5,55,1005,55,26,1001,54,-5,54,1105,1,12,1,53,54,53,1008,54,0,55,1001,55,1,55,2,53,55,53,4,53,1001,56,-1,56,1005,56,6,99,0,0,0,0,10")
|
||||
),
|
||||
18216
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user