Last active
December 6, 2019 07:19
-
-
Save hellow554/6d2f8945725743cb283952ac89f698cb to your computer and use it in GitHub Desktop.
day05
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
use intcode::IntCode; | |
const INPUT: &str = include_str!("../input"); | |
fn parse(s: &str) -> Vec<i32> { | |
s.split(',').map(|x| x.parse().unwrap()).collect() | |
} | |
fn main() { | |
let input = parse(INPUT); | |
let mut ic = IntCode::new(&input); | |
ic.provide_input(1); | |
ic.run(); | |
println!("A: {}", ic.output()); | |
let mut ic = IntCode::new(input); | |
ic.provide_input(5); | |
ic.run(); | |
println!("B: {}", ic.output()); | |
} | |
macro_rules! test { | |
($name:ident - $code:expr, $expected:expr) => { | |
#[test] | |
fn $name() { | |
let mut ic = IntCode::new($code); | |
ic.run(); | |
assert_eq!(ic.memory(), $expected); | |
} | |
}; | |
($name:ident - $code:expr, $($input:expr => $expected:expr),+ $(,)?) => { | |
#[test] | |
fn $name() { | |
$( | |
let mut ic = IntCode::new($code); | |
ic.provide_input($input); | |
ic.run(); | |
assert_eq!(format!("{}", $expected), ic.output(), "Input {}, output {}, expected {}", $input, ic.output(), $expected); | |
)* | |
} | |
} | |
} | |
test!(ta - &[1002, 4, 3, 4, 33], &[1002, 4, 3, 4, 99]); | |
test!(tb_pos_equal - &[3, 9, 8, 9, 10, 9, 4, 9, 99, -1, 8], | |
8 => 1, 7 => 0, 0 => 0, 9 => 0); | |
test!(tb_pos_less - &[3, 9, 7, 9, 10, 9, 4, 9, 99, -1, 8], | |
8 => 0, 7 => 1, 0 => 1, 9 => 0); | |
test!(tb_imm_equal - &[3, 3, 1108, -1, 8, 3, 4, 3, 99], | |
8 => 1, 7 => 0, 0 => 0, 9 => 0); | |
test!(tb_imm_less - &[3, 3, 1107, -1, 8, 3, 4, 3, 99], | |
8 => 0, 7 => 1, 0 => 1, 9 => 0); | |
test!(tb_pos_jump_input - &[3, 12, 6, 12, 15, 1, 13, 14, 13, 4, 13, 99, -1, 0, 1, 9], | |
0 => 0, 1 => 1, 8 => 1); | |
test!(tb_imm_jump_input - &[3, 3, 1105, -1, 9, 1101, 0, 0, 12, 4, 12, 99, 1], | |
0 => 0, 1 => 1, 8 => 1); | |
test!( | |
tb_big | |
- &[ | |
3, 21, 1008, 21, 8, 20, 1005, 20, 22, 107, 8, 21, 20, 1006, 20, 31, 1106, 0, 36, 98, 0, 0, 1002, 21, 125, | |
20, 4, 20, 1105, 1, 46, 104, 999, 1105, 1, 46, 1101, 1000, 1, 20, 4, 20, 1105, 1, 46, 98, 99, | |
][..], | |
0 => 999, | |
1 => 999, | |
2 => 999, | |
3 => 999, | |
4 => 999, | |
5 => 999, | |
6 => 999, | |
7 => 999, | |
8 => 1000, | |
9 => 1001, | |
10 => 1001, | |
); |
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
use std::borrow::Borrow; | |
use std::collections::VecDeque; | |
use std::convert::TryFrom; | |
use itertools::Itertools; | |
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | |
pub enum Mode { | |
Parameter, | |
Immediate, | |
} | |
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | |
// #[non_exhaustive] | |
pub enum Opcode { | |
Add(Mode, Mode, Mode), | |
Mul(Mode, Mode, Mode), | |
Read(Mode), | |
Write(Mode), | |
JumpTrue(Mode, Mode), | |
JumpFalse(Mode, Mode), | |
Less(Mode, Mode, Mode), | |
Equal(Mode, Mode, Mode), | |
Break, | |
} | |
use Opcode::*; // never do this in production, kids! | |
impl Opcode { | |
fn parse(instruction: i32) -> Option<Self> { | |
assert!(instruction >= 0); | |
let op = format!("{:05}", instruction); | |
let (mode3, mode2, mode1) = op[..3] | |
.chars() | |
.map(|c| { | |
if c == '0' { | |
Mode::Parameter | |
} else if c == '1' { | |
Mode::Immediate | |
} else { | |
unreachable!("Mode is neither `1` nor `0`: {}", instruction) | |
} | |
}) | |
.tuples() | |
.next()?; | |
Some(match op[3..].parse().ok()? { | |
1 => Add(mode1, mode2, mode3), | |
2 => Mul(mode1, mode2, mode3), | |
3 => Read(mode1), | |
4 => Write(mode1), | |
5 => JumpTrue(mode1, mode2), | |
6 => JumpFalse(mode1, mode2), | |
7 => Less(mode1, mode2, mode3), | |
8 => Equal(mode1, mode2, mode3), | |
99 => Break, | |
_ => None?, | |
}) | |
} | |
fn instruction_length(self) -> usize { | |
match self { | |
Add(_, _, _) | | |
Mul(_, _, _) | | |
Less(_, _, _) | | |
Equal(_, _, _) => 4, | |
Read(_) | | |
Write(_) => 2, | |
JumpTrue(_, _) | | |
JumpFalse(_, _) => 3, | |
Break => 1, | |
} | |
} | |
} | |
#[derive(Debug, Clone)] | |
pub struct IntCode { | |
code: Vec<i32>, | |
ip: usize, | |
fifo: VecDeque<i32>, | |
} | |
impl IntCode { | |
pub fn new<I: IntoIterator<Item = B>, B: Borrow<i32>>(data: I) -> Self { | |
Self { | |
code: data.into_iter().map(|d| *d.borrow()).collect(), | |
ip: 0, | |
fifo: VecDeque::new(), | |
} | |
} | |
pub fn provide_input(&mut self, value: i32) { | |
self.fifo.push_back(value); | |
} | |
pub fn step(&mut self) -> Opcode { | |
let opc = Opcode::parse(self.code[self.ip]).unwrap(); | |
let mut advance_ip = opc.instruction_length(); | |
match opc { | |
Add(m1, m2, m3) => *self.get_mut(3, m3) = self.get(1, m1) + self.get(2, m2), | |
Mul(m1, m2, m3) => *self.get_mut(3, m3) = self.get(1, m1) * self.get(2, m2), | |
Read(m1) => *self.get_mut(1, m1) = self.fifo.pop_front().unwrap(), | |
Write(m1) => self.fifo.push_back(self.get(1, m1)), | |
JumpTrue(m1, m2) => { | |
if self.get(1, m1) != 0 { | |
self.ip = usize::try_from(self.get(2, m2)).unwrap(); | |
advance_ip = 0; | |
} | |
} | |
JumpFalse(m1, m2) => { | |
if self.get(1, m1) == 0 { | |
self.ip = usize::try_from(self.get(2, m2)).unwrap(); | |
advance_ip = 0; | |
} | |
} | |
Less(m1, m2, m3) => *self.get_mut(3, m3) = (self.get(1, m1) < self.get(2, m2)).into(), | |
Equal(m1, m2, m3) => *self.get_mut(3, m3) = (self.get(1, m1) == self.get(2, m2)).into(), | |
Break => (), | |
} | |
self.ip += advance_ip; | |
opc | |
} | |
pub fn run(&mut self) { | |
loop { | |
if let Opcode::Break = self.step() { | |
break; | |
} | |
} | |
} | |
fn real_pos(&self, offset: isize) -> usize { | |
usize::try_from(isize::try_from(self.ip).unwrap() + offset).unwrap() | |
} | |
fn get(&self, offset: isize, mode: Mode) -> i32 { | |
let real_pos = self.real_pos(offset); | |
match mode { | |
Mode::Parameter => self.code[usize::try_from(self.code[real_pos]).unwrap()], | |
Mode::Immediate => self.code[real_pos], | |
} | |
} | |
fn get_mut(&mut self, offset: isize, mode: Mode) -> &mut i32 { | |
assert_eq!( | |
mode, | |
Mode::Parameter, | |
"`get_mut` doesn't work with immediate mode" | |
); | |
let tmp = self.code[self.real_pos(offset)]; | |
&mut self.code[usize::try_from(tmp).unwrap()] | |
} | |
pub fn memory(&self) -> &[i32] { | |
self.code.as_slice() | |
} | |
pub fn memory_mut(&mut self) -> &mut [i32] { | |
self.code.as_mut_slice() | |
} | |
pub fn output(&self) -> String { | |
self.fifo.iter().map(ToString::to_string).collect() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment