From 25d1e0d3a9f09ce3b28e8b1e9eba0fae71319a4b Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Mon, 9 Dec 2019 18:38:01 -0500 Subject: [PATCH] Add day09 Signed-off-by: Alek Ratzloff --- day09/.gitignore | 2 + day09/Cargo.lock | 6 ++ day09/Cargo.toml | 9 ++ day09/src/data.rs | 105 +++++++++++++++++++ day09/src/main.rs | 48 +++++++++ day09/src/permute.rs | 30 ++++++ day09/src/vm.rs | 245 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 445 insertions(+) create mode 100644 day09/.gitignore create mode 100644 day09/Cargo.lock create mode 100644 day09/Cargo.toml create mode 100644 day09/src/data.rs create mode 100644 day09/src/main.rs create mode 100644 day09/src/permute.rs create mode 100644 day09/src/vm.rs diff --git a/day09/.gitignore b/day09/.gitignore new file mode 100644 index 0000000..871d5ba --- /dev/null +++ b/day09/.gitignore @@ -0,0 +1,2 @@ +target/ +*.txt diff --git a/day09/Cargo.lock b/day09/Cargo.lock new file mode 100644 index 0000000..4e4359f --- /dev/null +++ b/day09/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "day09" +version = "0.1.0" + diff --git a/day09/Cargo.toml b/day09/Cargo.toml new file mode 100644 index 0000000..acff802 --- /dev/null +++ b/day09/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "day09" +version = "0.1.0" +authors = ["Alek Ratzloff "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/day09/src/data.rs b/day09/src/data.rs new file mode 100644 index 0000000..5707d5c --- /dev/null +++ b/day09/src/data.rs @@ -0,0 +1,105 @@ +use std::{ + collections::VecDeque, + io::{self, Write}, +}; + +pub type Queue = VecDeque; + +pub trait IntSource { + fn next(&mut self) -> Option; +} + +pub trait IntSink { + fn feed(&mut self, value: isize); +} + +pub struct StdinSource<'s> { + in_count: usize, + prompt: &'s str, +} + +impl<'s> StdinSource<'s> { + pub fn new(prompt: &'s str) -> Self { + StdinSource { in_count: 0, prompt, } + } +} + +impl IntSource for StdinSource<'_> { + fn next(&mut self) -> Option { + let mut line = String::new(); + self.in_count += 1; + loop { + print!("{}{} ", self.in_count, self.prompt); + io::stdout().flush().unwrap(); + io::stdin() + .read_line(&mut line) + .expect("could not read line"); + if line.is_empty() { + break None; + } + let parsed = line.trim().parse::(); + if let Ok(value) = parsed { + break Some(value); + } + } + } +} + +pub struct StdoutSink; + +impl IntSink for StdoutSink { + fn feed(&mut self, value: isize) { + println!("{}", value); + } +} + +impl IntSink for Queue { + fn feed(&mut self, int: isize) { + self.push_back(int); + } +} + +impl IntSource for Queue { + fn next(&mut self) -> Option { + self.pop_front() + } +} + +pub trait IntQueue: IntSink + IntSource {} + +impl IntQueue for T {} + +pub struct IoPair<'io, So, Si>(&'io mut So, &'io mut Si) +where + So: IntSource + 'static, + Si: IntSink + 'static; + +impl<'io, So, Si> IoPair<'io, So, Si> +where + So: IntSource + 'static, + Si: IntSink + 'static, +{ + pub fn new(source: &'io mut So, sink: &'io mut Si) -> Self { + IoPair(source, sink) + } +} + +impl<'io, So, Si> IntSink for IoPair<'io, So, Si> +where + So: IntSource + 'static, + Si: IntSink + 'static, +{ + fn feed(&mut self, value: isize) { + self.1.feed(value); + } +} + +impl<'io, So, Si> IntSource for IoPair<'io, So, Si> +where + So: IntSource + 'static, + Si: IntSink + 'static, +{ + fn next(&mut self) -> Option { + self.0.next() + } +} diff --git a/day09/src/main.rs b/day09/src/main.rs new file mode 100644 index 0000000..2cd070a --- /dev/null +++ b/day09/src/main.rs @@ -0,0 +1,48 @@ +mod data; +mod vm; + +use std::{env, fs, io, process}; + +use crate::data::*; +use crate::vm::*; + +type Result = std::result::Result>; + +#[allow(unused_macros)] +macro_rules! todo { + ($($tt:tt)*) => { unimplemented!($($tt)*) }; +} + +fn parse_args() -> io::Result { + let args: Vec = env::args().collect(); + if args.len() <= 1 { + println!( + "usage: {} input.txt", + args.get(0).map(String::as_str).unwrap_or("Day05") + ); + process::exit(1); + } else { + fs::read_to_string(&args[1]) + } +} + +fn main() -> Result<()> { + let input_string = parse_args()?; + let code: Vec<_> = input_string + .trim() + .split(",") + .filter(|s| !s.is_empty()) + .map(|n| n.parse::().unwrap()) + .collect(); + + let mut program = Program::new(code.clone()); + let mut stdin_source = StdinSource::new(">"); + let mut stdout_sink = StdoutSink; + + let mut io_pair = IoPair::new(&mut stdin_source, &mut stdout_sink); + let halt_result = program.run(&mut io_pair); + + assert_eq!(halt_result, HaltResult::Halt); + + Ok(()) +} diff --git a/day09/src/permute.rs b/day09/src/permute.rs new file mode 100644 index 0000000..faaa8df --- /dev/null +++ b/day09/src/permute.rs @@ -0,0 +1,30 @@ +// stolen from rosettacode + +pub fn permutations(size: usize) -> Permutations { + Permutations { idxs: (0..size).collect(), swaps: vec![0; size], i: 0 } +} + +pub struct Permutations { + idxs: Vec, + swaps: Vec, + i: usize, +} + +impl Iterator for Permutations { + type Item = Vec; + + fn next(&mut self) -> Option { + if self.i > 0 { + loop { + if self.i >= self.swaps.len() { return None; } + if self.swaps[self.i] < self.i { break; } + self.swaps[self.i] = 0; + self.i += 1; + } + self.idxs.swap(self.i, (self.i & 1) * self.swaps[self.i]); + self.swaps[self.i] += 1; + } + self.i = 1; + Some(self.idxs.clone()) + } +} diff --git a/day09/src/vm.rs b/day09/src/vm.rs new file mode 100644 index 0000000..6c9b589 --- /dev/null +++ b/day09/src/vm.rs @@ -0,0 +1,245 @@ +use crate::data::*; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HaltResult { + Halt, + WaitRead, +} + +#[derive(Debug, Clone)] +pub struct Program { + program: Vec, + pc: usize, + rel_base: isize, +} + +impl Program { + pub fn new(program: Vec) -> Self { + Program { program, pc: 0, rel_base: 0, } + } + + fn ensure_addr(&mut self, addr: usize) { + if self.program.len() <= addr { + let new_size = ((addr as f64) * 1.5) as usize; + self.program.resize_with(new_size, Default::default); + } + } + + fn get(&mut self, param: Param) -> isize { + match param { + Param::Pos(p) => { + self.ensure_addr(p); + self.program[p] + }, + Param::Imm(v) => v, + Param::Rel(r) => { + let addr = self.rel_base + r; + assert!(addr >= 0, "invalid relative address"); + let addr = addr as usize; + self.ensure_addr(addr); + self.program[addr] + } + } + } + + fn get_addr(&mut self, param: Param) -> usize { + match param { + Param::Pos(p) => p, + Param::Imm(_) => panic!("illegal addressing mode for target address"), + Param::Rel(r) => { + let addr = self.rel_base + r; + assert!(addr >= 0, "invalid relative address"); + addr as usize + } + } + } + + fn set(&mut self, addr: usize, what: isize) { + self.ensure_addr(addr); + self.program[addr] = what; + } + + pub fn run(&mut self, value_queue: &mut dyn IntQueue) -> HaltResult { + loop { + let op = Op::from(&self.program[self.pc..]); + let mut next_pc = self.pc + op.len(); + match op { + Op::Add { in1, in2, out } => { + let v1 = self.get(in1); + let v2 = self.get(in2); + let out = self.get_addr(out); + self.set(out, v1 + v2); + } + Op::Mul { in1, in2, out } => { + let v1 = self.get(in1); + let v2 = self.get(in2); + let out = self.get_addr(out); + self.set(out, v1 * v2); + } + Op::Read(pos) => { + if let Some(value) = value_queue.next() { + let addr = self.get_addr(pos); + self.set(addr, value); + } else { + // PC is not incremented, so this should do the read again + break HaltResult::WaitRead; + } + } + Op::Write(pos) => { + let value = self.get(pos); + value_queue.feed(value); + } + Op::JumpTrue { test, jump } => { + let value = self.get(test); + if value != 0 { + let addr = self.get(jump); + assert!(addr >= 0, "invalid jump address: {}", addr); + next_pc = addr as usize; + } + } + Op::JumpFalse { test, jump } => { + let value = self.get(test); + if value == 0 { + let addr = self.get(jump); + assert!(addr >= 0, "invalid jump address: {}", addr); + next_pc = addr as usize; + } + } + Op::LessThan { in1, in2, out } => { + let v1 = self.get(in1); + let v2 = self.get(in2); + let out = self.get_addr(out); + self.set(out, (v1 < v2) as isize); + } + Op::Equals { in1, in2, out } => { + let v1 = self.get(in1); + let v2 = self.get(in2); + let out = self.get_addr(out); + self.set(out, (v1 == v2) as isize); + } + Op::SetRel(rel) => { + self.rel_base += self.get(rel); + } + Op::Halt => { + self.pc = next_pc; + break HaltResult::Halt; + } + } + self.pc = next_pc; + } + } +} + +#[derive(Debug, Clone, Copy)] +enum Param { + Pos(usize), + Imm(isize), + Rel(isize), +} + +fn param_mode(op: usize, param: u32) -> usize { + let mode = op / 100; + (mode / (10usize.pow(param))) % 10 +} + +#[test] +fn test_param_mode() { + assert_eq!(param_mode(1100, 0), 1); + assert_eq!(param_mode(2100, 1), 2); + assert_eq!(param_mode(2200, 0), 2); + assert_eq!(param_mode(1000, 0), 0); + assert_eq!(param_mode(1100, 0), 1); +} + +fn make_param(op: usize, param: u32, value: isize) -> Param { + let mode = param_mode(op, param); + match mode { + 0 => { + assert!( + value >= 0, + "invalid position parameter for mode 0 op={} param={} value={}", + op, + param, + value + ); + Param::Pos(value as usize) + } + 1 => Param::Imm(value), + 2 => Param::Rel(value), + _ => panic!("invalid mode {} for op={} param={}", mode, op, param), + } +} + +#[derive(Debug, Clone, Copy)] +enum Op { + Add { in1: Param, in2: Param, out: Param }, + Mul { in1: Param, in2: Param, out: Param }, + Read(Param), + Write(Param), + JumpTrue { test: Param, jump: Param }, + JumpFalse { test: Param, jump: Param }, + LessThan { in1: Param, in2: Param, out: Param }, + Equals { in1: Param, in2: Param, out: Param }, + SetRel(Param), + Halt, +} + +impl Op { + fn len(&self) -> usize { + match self { + Op::Add { .. } | Op::Mul { .. } | Op::LessThan { .. } | Op::Equals { .. } => 4, + Op::JumpTrue { .. } | Op::JumpFalse { .. } => 3, + Op::Read(_) | Op::Write(_) | Op::SetRel(_) => 2, + Op::Halt => 1, + } + } +} + +impl From<&'_ [isize]> for Op { + fn from(other: &'_ [isize]) -> Self { + let op = other[0] as usize; + match op % 100 { + 1 => { + Op::Add { + in1: make_param(op, 0, other[1]), + in2: make_param(op, 1, other[2]), + out: make_param(op, 2, other[3]), + } + } + 2 => { + Op::Mul { + in1: make_param(op, 0, other[1]), + in2: make_param(op, 1, other[2]), + out: make_param(op, 2, other[3]), + } + } + 3 => Op::Read(make_param(op, 0, other[1])), + 4 => Op::Write(make_param(op, 0, other[1])), + 5 => Op::JumpTrue { + test: make_param(op, 0, other[1]), + jump: make_param(op, 1, other[2]), + }, + 6 => Op::JumpFalse { + test: make_param(op, 0, other[1]), + jump: make_param(op, 1, other[2]), + }, + 7 => { + Op::LessThan { + in1: make_param(op, 0, other[1]), + in2: make_param(op, 1, other[2]), + out: make_param(op, 2, other[3]), + } + } + 8 => { + Op::Equals { + in1: make_param(op, 0, other[1]), + in2: make_param(op, 1, other[2]), + out: make_param(op, 2, other[3]), + } + } + 9 => Op::SetRel(make_param(op, 0, other[1])), + 99 => Op::Halt, + op => panic!("invalid op: {}", op), + } + } +}