diff --git a/day05/Day05.rs b/day05/Day05.rs new file mode 100644 index 0000000..afee160 --- /dev/null +++ b/day05/Day05.rs @@ -0,0 +1,295 @@ +use std::{ + io::{self, Write}, + env, + fs, + process, +}; + +type Result = std::result::Result>; + +#[allow(unused_macros)] +macro_rules! todo { + ($($tt:tt)*) => { unimplemented!($($tt)*) }; +} + +fn get_input_string() -> 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 program: Vec<_> = get_input_string()? + .trim() + .split(",") + .filter(|s| !s.is_empty()) + .map(|n| n.parse::().unwrap()) + .collect(); + + part1(&program); + part2(&program); + //pretty_print(&program); + Ok(()) +} + +/* +fn pretty_print(program: &Vec) { + 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) { + println!("Running part 1 - input diagnostic code 1"); + run_program(program.clone()); +} + +fn part2(program: &Vec) { + println!("Running part 2 - input diagnostic code 5"); + run_program(program.clone()); +} + +fn run_program(mut program: Vec) -> Vec { + 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::(); + 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 { + 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), + } + } +} diff --git a/day05/Makefile b/day05/Makefile new file mode 100644 index 0000000..1e73eb8 --- /dev/null +++ b/day05/Makefile @@ -0,0 +1,11 @@ +TARGET=Day05 + +.PHONY: all +all: $(TARGET) + +$(TARGET): $(TARGET).rs + rustc $< + +.PHONY: clean +clean: + rm $(TARGET) -f