2
day07/.gitignore
vendored
Normal file
2
day07/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
target/
|
||||
*.txt
|
||||
6
day07/Cargo.lock
generated
Normal file
6
day07/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 = "day07"
|
||||
version = "0.1.0"
|
||||
|
||||
9
day07/Cargo.toml
Normal file
9
day07/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "day07"
|
||||
version = "0.1.0"
|
||||
authors = ["Alek Ratzloff <alekratz@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
#permutation = "0.2.5"
|
||||
#permutate = "0.3.2"
|
||||
91
day07/src/data.rs
Normal file
91
day07/src/data.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
153
day07/src/main.rs
Normal file
153
day07/src/main.rs
Normal file
@@ -0,0 +1,153 @@
|
||||
mod data;
|
||||
mod permute;
|
||||
mod vm;
|
||||
|
||||
use std::{cell::RefCell, collections::VecDeque, env, fs, io, process};
|
||||
|
||||
use crate::data::*;
|
||||
use crate::vm::*;
|
||||
use permute::*;
|
||||
|
||||
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, String)> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() <= 2 || !(&["part1", "part2"]).contains(&args[1].as_str()) {
|
||||
println!(
|
||||
"usage: {} [part1|part2] input.txt",
|
||||
args.get(0).map(String::as_str).unwrap_or("Day05")
|
||||
);
|
||||
process::exit(1);
|
||||
} else {
|
||||
Ok((args[1].to_string(), fs::read_to_string(&args[2])?))
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let (part, input_string) = parse_args()?;
|
||||
let program: Vec<_> = input_string
|
||||
.trim()
|
||||
.split(",")
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|n| n.parse::<isize>().unwrap())
|
||||
.collect();
|
||||
|
||||
if part == "part1" {
|
||||
part1(&program);
|
||||
} else if part == "part2" {
|
||||
part2(&program);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn part1(program: &Vec<isize>) {
|
||||
let (value, seq) = permutations(5)
|
||||
.map(|seq| {
|
||||
let value = run_input_seq(seq.as_slice(), program);
|
||||
(value, seq)
|
||||
})
|
||||
.max_by_key(|(v, _)| *v)
|
||||
.unwrap();
|
||||
|
||||
println!("Part 1: Max value = {} with sequence = {:?}", value, seq);
|
||||
}
|
||||
|
||||
fn part2(program: &Vec<isize>) {
|
||||
let (value, seq) = permutations(5)
|
||||
.map(|seq| {
|
||||
let seq: Vec<_> = seq.into_iter().map(|v| v + 5).collect();
|
||||
let value = run_input_feedback_seq(seq.as_slice(), program);
|
||||
(value, seq)
|
||||
})
|
||||
.max_by_key(|(v, _)| *v)
|
||||
.unwrap();
|
||||
//let seq = &[9, 8, 7, 6, 5];
|
||||
//let seq = &[9, 7, 8, 5, 6];
|
||||
//let value = run_input_feedback_seq(seq, program);
|
||||
println!("Part 2: Max value = {} with sequence = {:?}", value, seq);
|
||||
}
|
||||
|
||||
fn run_input_seq(seq: &[usize], program: &Vec<isize>) -> isize {
|
||||
let mut result = 0;
|
||||
for n in seq {
|
||||
let mut program = Program::new(program.clone());
|
||||
let mut io_feed = VecDeque::default();
|
||||
|
||||
io_feed.feed(*n as isize);
|
||||
io_feed.feed(result);
|
||||
let program_result = program.run(&mut io_feed);
|
||||
assert_eq!(
|
||||
program_result,
|
||||
HaltResult::Halt,
|
||||
"program halted waiting for input"
|
||||
);
|
||||
result = io_feed.pop_front().expect("no output");
|
||||
assert!(io_feed.is_empty(), "non-empty output feed: {:?}", io_feed);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn run_input_feedback_seq(seq: &[usize], program: &Vec<isize>) -> isize {
|
||||
let seq_idx: Vec<usize> = seq.iter().copied().map(|n| n - 5).collect();
|
||||
|
||||
let mut programs = vec![Program::new(program.clone()); seq.len()];
|
||||
let mut halts = vec![false; seq.len()];
|
||||
let pipes: Vec<RefCell<VecDeque<isize>>> = vec![
|
||||
RefCell::new(VecDeque::<isize>::new()); seq.len()
|
||||
];
|
||||
|
||||
let first_pipe = seq_idx[0];
|
||||
|
||||
// Set up initial pipe values that get fed exactly once
|
||||
for (i, pipe) in pipes.iter().enumerate() {
|
||||
let mut pipe = pipe.borrow_mut();
|
||||
let init = i + 5;
|
||||
pipe.feed(init as isize);
|
||||
}
|
||||
|
||||
{
|
||||
// feed the first program with a value
|
||||
let mut pipe = pipes[first_pipe].borrow_mut();
|
||||
pipe.feed(0);
|
||||
}
|
||||
|
||||
//println!("{:?}", seq);
|
||||
while !halts.iter().all(|h| *h) {
|
||||
for (i, n) in seq.iter().copied().enumerate() {
|
||||
let in_idx = seq_idx[i];
|
||||
assert_eq!(in_idx, n - 5);
|
||||
|
||||
if halts[in_idx] {
|
||||
continue;
|
||||
}
|
||||
|
||||
let out_seq_idx = (i + 1) % seq.len();
|
||||
let out_idx = seq_idx[out_seq_idx];
|
||||
//println!("in: {}, out: {}", seq[in_seq_idx], n);
|
||||
|
||||
let mut in_pipe = pipes[in_idx].borrow_mut();
|
||||
let mut out_pipe = pipes[out_idx].borrow_mut();
|
||||
|
||||
let mut io_pair = IoPair::new(&mut *in_pipe, &mut *out_pipe);
|
||||
|
||||
let halt_result = programs[in_idx].run(&mut io_pair);
|
||||
match halt_result {
|
||||
HaltResult::Halt => {
|
||||
halts[in_idx] = true;
|
||||
},
|
||||
HaltResult::WaitRead => { continue; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
//println!("{:#?}", pipes);
|
||||
let mut pipe = pipes[first_pipe].borrow_mut();
|
||||
pipe.next().expect("no output")
|
||||
}
|
||||
}
|
||||
30
day07/src/permute.rs
Normal file
30
day07/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())
|
||||
}
|
||||
}
|
||||
225
day07/src/vm.rs
Normal file
225
day07/src/vm.rs
Normal file
@@ -0,0 +1,225 @@
|
||||
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,
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn new(program: Vec<isize>) -> Self {
|
||||
Program { program, pc: 0 }
|
||||
}
|
||||
|
||||
fn get(&self, param: Param) -> isize {
|
||||
param.get(&self.program)
|
||||
}
|
||||
|
||||
fn set(&mut self, addr: usize, what: isize) {
|
||||
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);
|
||||
self.set(out, v1 + v2);
|
||||
}
|
||||
Op::Mul { in1, in2, out } => {
|
||||
let v1 = self.get(in1);
|
||||
let v2 = self.get(in2);
|
||||
self.set(out, v1 * v2);
|
||||
}
|
||||
Op::Read(pos) => {
|
||||
if let Some(value) = value_queue.next() {
|
||||
self.set(pos, 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);
|
||||
|
||||
self.set(out, (v1 < v2) as isize);
|
||||
}
|
||||
Op::Equals { in1, in2, out } => {
|
||||
let v1 = self.get(in1);
|
||||
let v2 = self.get(in2);
|
||||
|
||||
self.set(out, (v1 == v2) as isize);
|
||||
}
|
||||
Op::Halt => {
|
||||
self.pc = next_pc;
|
||||
break HaltResult::Halt;
|
||||
}
|
||||
}
|
||||
self.pc = next_pc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Param {
|
||||
Pos(usize),
|
||||
Imm(isize),
|
||||
}
|
||||
|
||||
impl Param {
|
||||
fn get(&self, program: &Vec<isize>) -> isize {
|
||||
match self {
|
||||
Param::Pos(p) => program[*p],
|
||||
Param::Imm(v) => *v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Op {
|
||||
Add { in1: Param, in2: Param, out: usize },
|
||||
Mul { in1: Param, in2: Param, out: usize },
|
||||
Read(usize),
|
||||
Write(Param),
|
||||
JumpTrue { test: Param, jump: Param },
|
||||
JumpFalse { test: Param, jump: Param },
|
||||
LessThan { in1: Param, in2: Param, out: usize },
|
||||
Equals { in1: Param, in2: Param, out: usize },
|
||||
Halt,
|
||||
}
|
||||
|
||||
fn param_mode(op: usize, param: u32) -> usize {
|
||||
let mode = op / 100;
|
||||
(mode / (10usize.pow(param))) & 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),
|
||||
_ => panic!("invalid mode {} for op={} param={}", mode, op, param),
|
||||
}
|
||||
}
|
||||
|
||||
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(_) => 2,
|
||||
Op::Halt => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'_ [isize]> for Op {
|
||||
fn from(other: &'_ [isize]) -> Self {
|
||||
let op = other[0] as usize;
|
||||
match op % 100 {
|
||||
1 => {
|
||||
assert!(
|
||||
other[3] >= 0,
|
||||
"invalid output parameter for op={} output={}",
|
||||
op,
|
||||
other[3]
|
||||
);
|
||||
Op::Add {
|
||||
in1: make_param(op, 0, other[1]),
|
||||
in2: make_param(op, 1, other[2]),
|
||||
out: other[3] as usize,
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
assert!(
|
||||
other[3] >= 0,
|
||||
"invalid output parameter for op={} output={}",
|
||||
op,
|
||||
other[3]
|
||||
);
|
||||
Op::Mul {
|
||||
in1: make_param(op, 0, other[1]),
|
||||
in2: make_param(op, 1, other[2]),
|
||||
out: other[3] as usize,
|
||||
}
|
||||
}
|
||||
3 => Op::Read(other[1] as usize),
|
||||
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 => {
|
||||
assert!(
|
||||
other[3] >= 0,
|
||||
"invalid output parameter for op={} output={}",
|
||||
op,
|
||||
other[3]
|
||||
);
|
||||
Op::LessThan {
|
||||
in1: make_param(op, 0, other[1]),
|
||||
in2: make_param(op, 1, other[2]),
|
||||
out: other[3] as usize,
|
||||
}
|
||||
}
|
||||
8 => {
|
||||
assert!(
|
||||
other[3] >= 0,
|
||||
"invalid output parameter for op={} output={}",
|
||||
op,
|
||||
other[3]
|
||||
);
|
||||
Op::Equals {
|
||||
in1: make_param(op, 0, other[1]),
|
||||
in2: make_param(op, 1, other[2]),
|
||||
out: other[3] as usize,
|
||||
}
|
||||
}
|
||||
99 => Op::Halt,
|
||||
op => panic!("invalid op: {}", op),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user