summaryrefslogtreecommitdiff
path: root/2019/rust/day09/src
diff options
context:
space:
mode:
Diffstat (limited to '2019/rust/day09/src')
-rw-r--r--2019/rust/day09/src/main.rs353
1 files changed, 353 insertions, 0 deletions
diff --git a/2019/rust/day09/src/main.rs b/2019/rust/day09/src/main.rs
new file mode 100644
index 0000000..ce7e236
--- /dev/null
+++ b/2019/rust/day09/src/main.rs
@@ -0,0 +1,353 @@
+use std::env;
+use std::io::{self, BufRead, BufReader};
+use std::fs::File;
+
+type BusWidth = i128;
+type IOType = i128;
+type IOData = Vec<IOType>;
+
+fn read_program(filename: &str) -> Vec<BusWidth> {
+ let f = File::open(filename).expect(filename);
+ let f = BufReader::new(f);
+
+ f.lines()
+ .filter(|line| match line {
+ Ok(s) => {
+ let b = s.as_bytes();
+ if b.len() == 0 || b.len() > 0 && b[0] == b'#' { false } else { true }
+ },
+ _ => true,
+ })
+ .map(|line| match line {
+ Ok(line) => {
+ let data:Vec<BusWidth> = line.split(',').map(|e| { e.trim_start().trim_end() })
+ .filter(|v| match v.parse::<BusWidth>() { Ok(_) => true, _ => {
+ eprintln!("Could not parse: {:?}", v);
+ false
+ },
+ })
+ .map(|r| r.parse().unwrap())
+ .collect();
+
+ data
+ },
+ Err(_) => panic!("Could not parse {}.", filename),
+ }).flatten().collect()
+}
+
+#[derive(Clone, Debug)]
+struct Memory {
+ mem: Vec<BusWidth>,
+}
+
+impl Memory {
+ fn new() -> Memory {
+ Memory { mem: Vec::new(), }
+ }
+
+ fn init(&mut self, mem:Vec<BusWidth>) {
+ self.mem = mem;
+ }
+
+ fn read(&self, address: usize) -> BusWidth {
+ match self.mem.get(address) {
+ Some(value) => *value,
+ None => 0,
+ }
+ }
+
+ fn write(&mut self, address: usize, value: BusWidth) {
+ if self.len() < address {
+ self.mem.resize(address*2, 0);
+ }
+ self.mem[address] = value;
+ }
+
+ fn len(&self) -> usize {
+ self.mem.len()
+ }
+}
+
+#[derive(Debug)]
+struct Registers {
+ ip: usize,
+ base: usize,
+}
+
+#[derive(Debug)]
+enum Arithm {
+ Add,
+ Mul,
+ LessThan,
+ Equals,
+}
+
+#[derive(Debug)]
+enum IO {
+ In,
+ Out,
+}
+
+#[derive(Debug)]
+enum Jmp {
+ JmpTrue,
+ JmpFalse,
+}
+
+#[derive(Debug)]
+enum RegOp {
+ BaseMod,
+}
+
+#[derive(Debug, PartialEq)]
+enum ParameterMode {
+ Position,
+ Immediate,
+ Relative,
+}
+
+#[derive(Debug, PartialEq)]
+enum DataDirection {
+ Read,
+ Write,
+}
+
+#[derive(PartialEq)]
+enum IOMethod {
+ StdIO,
+ Variables,
+}
+
+struct Intcode {
+ r: Registers,
+ io_method: IOMethod,
+ input: IOData,
+ output: IOData,
+}
+
+impl Intcode {
+ fn new() -> Intcode {
+ Intcode {
+ r: Registers { ip: 0, base: 0, },
+ io_method: IOMethod::Variables,
+ input: vec![],
+ output: vec![],
+ }
+ }
+
+ fn param_to_argument(&self, mode: &ParameterMode, mem: &Memory, offset: usize,
+ dir: &DataDirection) -> usize
+ {
+ match mode {
+ ParameterMode::Position => {
+ if mem.read(offset) < 0 {
+ panic!("Attempt to address negative memory at IP: {}");
+ }
+ mem.read(offset) as usize
+ },
+ ParameterMode::Immediate => {
+ if *dir == DataDirection::Write {
+ panic!("Immediate parameter mode at IP: {}", self.r.ip);
+ }
+ offset
+ },
+ ParameterMode::Relative => {
+ if (mem.read(offset) + self.r.base as BusWidth) < 0 {
+ panic!("Attempt to address negative memory at IP: {}");
+ }
+ (mem.read(offset) + self.r.base as BusWidth) as usize
+ },
+ }
+ }
+
+ fn arithm(&self, op: Arithm, modes: Vec<ParameterMode>, memory: &mut Memory) {
+ let parameter_directions =
+ [DataDirection::Read, DataDirection::Read, DataDirection::Write];
+ let params:Vec<usize> = (0..parameter_directions.len())
+ .zip(parameter_directions.iter())
+ .map(|(p, d)| {
+ self.param_to_argument(&modes[p], memory, self.r.ip + 1 + p, d)
+ }).collect();
+ let left = params[0];
+ let right = params[1];
+ let result = params[2];
+
+ match op {
+ Arithm::Add => memory.write(result, memory.read(left) + memory.read(right)),
+ Arithm::Mul => memory.write(result, memory.read(left) * memory.read(right)),
+ Arithm::LessThan => memory.write(result, if memory.read(left) < memory.read(right) {
+ 1 } else { 0 }),
+ Arithm::Equals => memory.write(result, if memory.read(left) == memory.read(right) {
+ 1 } else { 0 }),
+ }
+ }
+
+ fn jmp(&mut self, op: Jmp, modes: Vec<ParameterMode>, memory: &mut Memory)
+ {
+ let parameter_directions = [DataDirection::Read, DataDirection::Read];
+ let params:Vec<usize> = (0..parameter_directions.len())
+ .zip(parameter_directions.iter())
+ .map(|(p, d)| {
+ self.param_to_argument(&modes[p], memory, self.r.ip + 1 + p, d)
+ }).collect();
+ let value = params[0];
+ let address = params[1];
+
+ self.r.ip = match op {
+ Jmp::JmpTrue => (if memory.read(value) != 0 {
+ memory.read(address) as usize } else { self.r.ip + 3 }),
+ Jmp::JmpFalse => (if memory.read(value) == 0 {
+ memory.read(address) as usize } else { self.r.ip + 3 }),
+ };
+
+ }
+
+ fn regop(&mut self, op: RegOp, modes: Vec<ParameterMode>, memory: &mut Memory)
+ {
+ let parameter_directions = [DataDirection::Read];
+ let params:Vec<usize> = (0..parameter_directions.len())
+ .zip(parameter_directions.iter())
+ .map(|(p, d)| {
+ self.param_to_argument(&modes[p], memory, self.r.ip + 1 + p, d)
+ }).collect();
+ let value = memory.read(params[0]);
+
+ match op {
+ RegOp::BaseMod => {
+ if value < 0 {
+ self.r.base -= (-1 * value) as usize;
+ } else {
+ self.r.base += value as usize;
+ }
+ },
+ };
+ }
+
+ fn io_in(&mut self, _op: IO, modes: Vec<ParameterMode>, memory:&mut Memory)
+ {
+ let parameter_directions = [DataDirection::Write];
+ let params:Vec<usize> = (0..parameter_directions.len())
+ .zip(parameter_directions.iter())
+ .map(|(p, d)| {
+ self.param_to_argument(&modes[p], memory, self.r.ip + 1 + p, d)
+ }).collect();
+ let address = params[0];
+ let stdin = io::stdin();
+
+ match &self.io_method {
+ IOMethod::StdIO => {
+ let mut value: Option<BusWidth> = None;
+ while {
+ stdin.lock().lines().next().map(|s| {
+ match s {
+ Ok(v) => {
+ let parsed_string = v.trim_end().trim_start().parse::<BusWidth>();
+ match parsed_string {
+ Ok(v) => { value = Some(v); value },
+ _ => None,
+ };
+ value
+ },
+ _ => { None },
+ }
+ });
+ if value == None {
+ eprintln!("Could not parse input, please retry.");
+ true
+ } else {
+ false
+ }
+ } {}
+ match value {
+ Some(v) => memory.write(address, v),
+ _ => {},
+ };
+ },
+ IOMethod::Variables => {
+ memory.write(address, self.input.remove(0) as BusWidth);
+ },
+ }
+ }
+
+ fn io_out(&mut self, _op: IO, modes: Vec<ParameterMode>, memory:&mut Memory )
+ {
+ let parameter_directions = [DataDirection::Read];
+ let params:Vec<usize> = (0..parameter_directions.len())
+ .zip(parameter_directions.iter())
+ .map(|(p, d)| {
+ self.param_to_argument(&modes[p], memory, self.r.ip + 1 + p, d)
+ }).collect();
+ let address = params[0];
+
+ let o = memory.read(address);
+ println!("{}", o);
+ match &self.io_method {
+ IOMethod::Variables => self.output.push(o as IOType),
+ _ => {},
+ }
+ }
+
+ fn intcode(&mut self, mut memory:Memory)
+ {
+ while self.r.ip < memory.len() {
+ let op = memory.read(self.r.ip) % 100;
+ let modes:Vec<ParameterMode> = (2..=4)
+ .map(|e| memory.read(self.r.ip) / 10i32.pow(e) as BusWidth % 10)
+ .map(|n| match n {
+ 0 => ParameterMode::Position,
+ 1 => ParameterMode::Immediate,
+ 2 => ParameterMode::Relative,
+ p => panic!("Invalid parameter mode {} at IP: {}", p, self.r.ip),
+ }).collect();
+
+ match op {
+ 1 => { self.arithm(Arithm::Add, modes, &mut memory); self.r.ip += 4 },
+ 2 => { self.arithm(Arithm::Mul, modes, &mut memory); self.r.ip += 4 },
+ 7 => { self.arithm(Arithm::LessThan, modes, &mut memory); self.r.ip += 4 },
+ 8 => { self.arithm(Arithm::Equals, modes, &mut memory); self.r.ip += 4 },
+ 3 => { self.io_in( IO::In, modes, &mut memory); self.r.ip += 2 },
+ 4 => { self.io_out(IO::Out, modes, &mut memory); self.r.ip += 2 },
+ 5 => { self.jmp( Jmp::JmpTrue, modes, &mut memory); },
+ 6 => { self.jmp( Jmp::JmpFalse, modes, &mut memory); },
+ 9 => { self.regop(RegOp::BaseMod, modes, &mut memory); self.r.ip += 2},
+ 99 => break,
+ op => panic!("Invalid operation {} at IP: {}", op, self.r.ip),
+ }
+ }
+ }
+}
+
+fn main() {
+ let args: Vec<String> = env::args().collect();
+
+ let mut opts = getopts::Options::new();
+ opts.reqopt("m", "mode", "Mode of IO operations", "[arg|stdio]");
+ opts.reqopt("p", "program", "File to load program from", "FILE");
+ let matches = match opts.parse(&args[1..]) {
+ Ok(m) => { m }
+ Err(f) => { panic!(f.to_string()) }
+ };
+ let program:String = matches.opt_str("program").unwrap();
+ let mode = matches.opt_str("mode").unwrap();
+ let mut memory = Memory::new();
+ memory.init(read_program(&program));
+ let mut cpu = Intcode::new();
+
+ if mode == "arg" {
+ cpu.io_method = IOMethod::Variables;
+ let input:IOData = matches.free[0].split(",").map(|v| {
+ let i = v.trim_end().parse().unwrap();
+ i
+ }).collect();
+ cpu.input = input;
+ while cpu.input.len() > 1 {
+ cpu.intcode(memory.clone());
+ }
+ } else if mode == "stdio" {
+ cpu.io_method = IOMethod::StdIO;
+
+ cpu.intcode(memory.clone());
+ } else {
+ panic!("Unknown mode: {}", mode);
+ }
+}