2
day09/.gitignore
vendored
Normal file
2
day09/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
target/
|
||||||
|
*.txt
|
||||||
6
day09/Cargo.lock
generated
Normal file
6
day09/Cargo.lock
generated
Normal file
@@ -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"
|
||||||
|
|
||||||
9
day09/Cargo.toml
Normal file
9
day09/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "day09"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Alek Ratzloff <alekratz@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
105
day09/src/data.rs
Normal file
105
day09/src/data.rs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
use std::{
|
||||||
|
collections::VecDeque,
|
||||||
|
io::{self, Write},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type Queue = VecDeque<isize>;
|
||||||
|
|
||||||
|
pub trait IntSource {
|
||||||
|
fn next(&mut self) -> Option<isize>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<isize> {
|
||||||
|
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::<isize>();
|
||||||
|
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<isize> {
|
||||||
|
self.pop_front()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IntQueue: IntSink + IntSource {}
|
||||||
|
|
||||||
|
impl<T: IntSink + IntSource> 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<isize> {
|
||||||
|
self.0.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
48
day09/src/main.rs
Normal file
48
day09/src/main.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
mod data;
|
||||||
|
mod vm;
|
||||||
|
|
||||||
|
use std::{env, fs, io, process};
|
||||||
|
|
||||||
|
use crate::data::*;
|
||||||
|
use crate::vm::*;
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||||
|
|
||||||
|
#[allow(unused_macros)]
|
||||||
|
macro_rules! todo {
|
||||||
|
($($tt:tt)*) => { unimplemented!($($tt)*) };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_args() -> io::Result<String> {
|
||||||
|
let args: Vec<String> = 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::<isize>().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(())
|
||||||
|
}
|
||||||
30
day09/src/permute.rs
Normal file
30
day09/src/permute.rs
Normal file
@@ -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<usize>,
|
||||||
|
swaps: Vec<usize>,
|
||||||
|
i: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for Permutations {
|
||||||
|
type Item = Vec<usize>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
245
day09/src/vm.rs
Normal file
245
day09/src/vm.rs
Normal file
@@ -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<isize>,
|
||||||
|
pc: usize,
|
||||||
|
rel_base: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Program {
|
||||||
|
pub fn new(program: Vec<isize>) -> 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user