Skip to content

Instantly share code, notes, and snippets.

@hellow554
Last active December 6, 2019 07:19
Show Gist options
  • Save hellow554/6d2f8945725743cb283952ac89f698cb to your computer and use it in GitHub Desktop.
Save hellow554/6d2f8945725743cb283952ac89f698cb to your computer and use it in GitHub Desktop.
day05
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,
);
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