Files
adventofcode-2019/day05/Day05.rs

296 lines
8.1 KiB
Rust
Raw Normal View History

use std::{
io::{self, Write},
env,
fs,
process,
};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
#[allow(unused_macros)]
macro_rules! todo {
($($tt:tt)*) => { unimplemented!($($tt)*) };
}
fn get_input_string() -> 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 program: Vec<_> = get_input_string()?
.trim()
.split(",")
.filter(|s| !s.is_empty())
.map(|n| n.parse::<isize>().unwrap())
.collect();
part1(&program);
part2(&program);
//pretty_print(&program);
Ok(())
}
/*
fn pretty_print(program: &Vec<isize>) {
let mut pc = 0;
let mut end = false;
while pc < program.len() {
let op = program[pc] % 100;
match op {
1 | 2 => {
println!("{:04} {} {} {}",
program[pc],
program[pc + 1],
program[pc + 2],
program[pc + 3]);
pc += 4;
}
3 | 4 => {
println!("{:04} {}",
program[pc],
program[pc + 1]);
pc += 2;
}
99 => {
println!("{:04}", program[pc]);
end = true;
pc += 1;
}
other => {
if end {
print!("{} ", other);
} else {
println!("{}", other);
}
pc += 1;
}
}
}
println!();
}
*/
fn part1(program: &Vec<isize>) {
println!("Running part 1 - input diagnostic code 1");
run_program(program.clone());
}
fn part2(program: &Vec<isize>) {
println!("Running part 2 - input diagnostic code 5");
run_program(program.clone());
}
fn run_program(mut program: Vec<isize>) -> Vec<isize> {
let mut pc = 0;
let mut read_count = 1;
loop {
let op = Op::from(&program[pc..]);
pc += op.len();
match op {
Op::Add { in1, in2, out, } => {
let v1 = in1.get(&program);
let v2 = in2.get(&program);
program[out] = v1 + v2;
}
Op::Mul { in1, in2, out, } => {
let v1 = in1.get(&program);
let v2 = in2.get(&program);
program[out] = v1 * v2;
}
Op::Read(pos) => {
let mut line = String::new();
let value = loop {
print!("{}> ", read_count);
io::stdout().flush().unwrap();
io::stdin().read_line(&mut line)
.expect("could not read line");
let parsed = line.trim()
.parse::<isize>();
if let Ok(value) = parsed {
break value;
}
};
read_count += 1;
program[pos] = value;
}
Op::Write(pos) => {
let value = pos.get(&program);
println!("{}", value);
}
Op::JumpTrue { test, jump } => {
let value = test.get(&program);
if value != 0 {
let addr = jump.get(&program);
assert!(addr >= 0, "invalid jump address: {}", addr);
pc = addr as usize;
}
}
Op::JumpFalse { test, jump } => {
let value = test.get(&program);
if value == 0 {
let addr = jump.get(&program);
assert!(addr >= 0, "invalid jump address: {}", addr);
pc = addr as usize;
}
}
Op::LessThan { in1, in2, out, } => {
let v1 = in1.get(&program);
let v2 = in2.get(&program);
program[out] = (v1 < v2) as isize;
}
Op::Equals { in1, in2, out, } => {
let v1 = in1.get(&program);
let v2 = in2.get(&program);
program[out] = (v1 == v2) as isize;
}
Op::Halt => {
break;
}
}
}
program
}
#[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),
}
}
}