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), } } }