use std::io::{self, BufRead}; use std::env; fn read_program() -> Vec { let stdin = io::stdin(); stdin.lock().split(b',').map(|v| { let s = String::from_utf8(v.unwrap()).unwrap(); let u:i32 = s.trim_end().parse().unwrap(); u as i32 }).collect() } #[derive(Debug)] enum Arithm { Add, Mul, LessThan, Equals, } #[derive(Debug)] enum IO { In, Out, } #[derive(Debug)] enum Jmp { JmpTrue, JmpFalse, } #[derive(Debug, PartialEq)] enum ParameterMode { Position, Immediate, } fn arithm(op:Arithm, ip:usize, modes:Vec, memory:&mut Vec) { if modes[2] == ParameterMode::Immediate { panic!("Instruction can not store in immediate mode parameter. (IP: {})", ip); } let params:Vec = (0..=2).map(|n| match &modes[n] { ParameterMode::Position => memory[ip + n + 1] as usize, ParameterMode::Immediate => ip + n + 1, }).collect(); let left = params[0]; let right = params[1]; let result = params[2]; match op { Arithm::Add => memory[result] = memory[left] + memory[right], Arithm::Mul => memory[result] = memory[left] * memory[right], Arithm::LessThan => memory[result] = if memory[left] < memory[right] { 1 } else { 0 }, Arithm::Equals => memory[result] = if memory[left] == memory[right] { 1 } else { 0 }, } } fn jmp(op:Jmp, ip:usize, modes:Vec, memory:&mut Vec) -> usize { let params:Vec = (0..=2).map(|n| match &modes[n] { ParameterMode::Position => memory[ip + n + 1] as usize, ParameterMode::Immediate => ip + n + 1, }).collect(); let value = params[0]; let address = params[1]; match op { Jmp::JmpTrue => (if memory[value] != 0 { memory[address] as usize } else { ip + 3 }), Jmp::JmpFalse => (if memory[value] == 0 { memory[address] as usize } else { ip + 3 }), } } fn io(op:IO, ip:usize, modes:Vec, memory:&mut Vec, keyboard:i32) { match op { IO::In => { let address = match &modes[0] { ParameterMode::Position => memory[ip + 1] as usize, ParameterMode::Immediate => panic!("Cannot store input in immediate mode parameter. (IP: {})", ip), }; memory[address] = keyboard; }, IO::Out => { println!("Output: {}", match &modes[0] { ParameterMode::Position => memory[memory[ip + 1] as usize], ParameterMode::Immediate => memory[ip + 1], }); }, } } fn intcode(mut memory:Vec, keyboard:i32) { let mut ip = 0; while ip < memory.len() { let op = memory[ip] % 100; let modes:Vec = (2..=4).map(|e| memory[ip] / 10i32.pow(e) % 10) .map(|n| match n { 0 => ParameterMode::Position, 1 => ParameterMode::Immediate, p => panic!("Invalid parameter mode {} at IP: {}", p, ip), }).collect(); match op { 1 => { arithm(Arithm::Add, ip, modes, &mut memory); ip += 4 }, 2 => { arithm(Arithm::Mul, ip, modes, &mut memory); ip += 4 }, 7 => { arithm(Arithm::LessThan, ip, modes, &mut memory); ip += 4 }, 8 => { arithm(Arithm::Equals, ip, modes, &mut memory); ip += 4 }, 3 => { io(IO::In, ip, modes, &mut memory, keyboard); ip += 2 }, 4 => { io(IO::Out, ip, modes, &mut memory, keyboard); ip += 2 }, 5 => { ip = jmp(Jmp::JmpTrue, ip, modes, &mut memory); }, 6 => { ip = jmp(Jmp::JmpFalse, ip, modes, &mut memory); }, 99 => break, op => panic!("Invalid operation {} at IP: {}", op, ip), } } } fn first_puzzle(memory:Vec, input:i32) { println!("Attemping to solve first puzzle. Using input: {}.", input); intcode(memory, input); } fn second_puzzle(memory:Vec, input:i32) { println!("Attemping to solve second puzzle. Using input: {}.", input); intcode(memory, input); } fn main() { let memory = read_program(); let input = env::args().nth(1); match input { Some(i) => { println!("Attemping to run program using input: {}.", i); intcode(memory, i.parse().unwrap()); }, None => { first_puzzle(memory.clone(), 1); second_puzzle(memory, 5); } } }