diff --git a/Cargo.toml b/Cargo.toml index d8be311..ad2002f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,4 +24,8 @@ path = "src/day4/main.rs" [[bin]] name = "day5" -path = "src/day5/main.rs" \ No newline at end of file +path = "src/day5/main.rs" + +[[bin]] +name = "day7" +path = "src/day7/main.rs" \ No newline at end of file diff --git a/src/day7/main.rs b/src/day7/main.rs new file mode 100644 index 0000000..aa3543b --- /dev/null +++ b/src/day7/main.rs @@ -0,0 +1,354 @@ +use std::collections::VecDeque; +use std::env; +use std::fs::File; +use std::io::{self, BufRead, BufReader}; + +#[derive(Copy, Clone, Debug)] +enum Mode { + Position, + Immediate, +} + +impl Default for Mode { + fn default() -> Self { + Mode::Position + } +} + +impl From for Mode { + fn from(b: bool) -> Self { + match b { + false => Mode::Position, + true => Mode::Immediate, + } + } +} + +#[derive(Copy, Clone, Default, Debug)] +struct Modes { + a: Mode, + b: Mode, + c: Mode, +} + +impl From<(Mode, Mode, Mode)> for Modes { + fn from(modes: (Mode, Mode, Mode)) -> Self { + Modes { + a: modes.0, + b: modes.1, + c: modes.2, + } + } +} + +impl From<&ModedOpcode> for Modes { + fn from(mo: &ModedOpcode) -> Self { + mo.modes + } +} + +#[derive(Copy, Clone)] +enum Opcode { + Add, + Multiply, + Input, + Output, + JumpIfTrue, + JumpIfFalse, + LessThan, + Equals, + Halt, +} + +impl From<&ModedOpcode> for Opcode { + fn from(mo: &ModedOpcode) -> Self { + mo.op + } +} + +impl From for Opcode { + fn from(i: i64) -> Self { + match i { + 1 => Opcode::Add, + 2 => Opcode::Multiply, + 3 => Opcode::Input, + 4 => Opcode::Output, + 5 => Opcode::JumpIfTrue, + 6 => Opcode::JumpIfFalse, + 7 => Opcode::LessThan, + 8 => Opcode::Equals, + 99 => Opcode::Halt, + _ => panic!("invalid opcode {}", i), + } + } +} + +struct ModedOpcode { + modes: Modes, + op: Opcode, +} + +impl From for ModedOpcode { + fn from(i: i64) -> Self { + let code = i % 100; + let a = i % 1000 >= 100; + let b = i % 10000 >= 1000; + let c = i > 10000; + let modes = (Mode::from(a), Mode::from(b), Mode::from(c)).into(); + ModedOpcode { + modes: modes, + op: Opcode::from(code), + } + } +} + +struct Computer { + pos: usize, + program: Vec, + modes: Modes, + inputs: VecDeque, + outputs: VecDeque, + + halted: bool, + paused: bool, +} + +impl Clone for Computer { + fn clone(&self) -> Self { + Computer::from(self.program.clone()) + } +} + +impl From for Computer { + fn from(f: File) -> Self { + let f = BufReader::new(f); + let program: Vec = f + .split(b',') + .map(|o| { + let o = o.unwrap(); + let mut o = o.iter().peekable(); + let sign = if o.peek() == Some(&&b'-') { -1 } else { 1 }; + o.filter(|i| **i >= b'0' && **i <= b'9') + .fold(0, |s, i| ((i - b'0') as i64 + 10 * s)) + * sign + }) + .collect(); + program.into() + } +} + +impl From> for Computer { + fn from(program: Vec) -> Self { + Computer { + pos: 0, + program: program, + modes: Default::default(), + inputs: VecDeque::new(), + outputs: VecDeque::new(), + halted: false, + paused: false, + } + } +} + +impl Computer { + fn clone_with_modified_program(&self, u: Vec<(usize, i64)>) -> Computer { + let mut computer = self.clone(); + for (pos, val) in u { + computer.program[pos] = val; + } + computer + } + + fn clone_with_input(&self, input: Vec) -> Computer { + let mut computer = self.clone(); + computer.inputs = input.into(); + computer + } + + fn run(&mut self) { + loop { + self.step(); + } + } + + fn run_until_output(&mut self) -> i64 { + loop { + self.step(); + if self.outputs.len() > 0 { + return self.outputs.pop_front().unwrap(); + } + } + } + + fn step(&mut self) { + let instruction = self.program[self.pos]; + let instruction = ModedOpcode::from(instruction); + self.modes = (&instruction).into(); + let advance = match (&instruction).into() { + Opcode::Add => self.add(), + Opcode::Multiply => self.multiply(), + Opcode::Input => self.input(), + Opcode::Output => self.output(), + Opcode::JumpIfTrue => self.jump_if_true(), + Opcode::JumpIfFalse => self.jump_if_false(), + Opcode::LessThan => self.less_than(), + Opcode::Equals => self.equals(), + Opcode::Halt => self.halt(), + }; + self.pos += advance; + } + + fn get_a(&self) -> i64 { + let a = self.program[self.pos + 1]; + match self.modes.a { + Mode::Position => self.program[a as usize], + Mode::Immediate => a, + } + } + + fn set_a(&mut self, value: i64) { + let a = self.program[self.pos + 1]; + match self.modes.a { + Mode::Position => self.program[a as usize] = value, + Mode::Immediate => self.program[self.pos] = value, + } + } + + fn get_b(&self) -> i64 { + let b = self.program[self.pos + 2]; + match self.modes.b { + Mode::Position => self.program[b as usize], + Mode::Immediate => b, + } + } + + fn set_c(&mut self, value: i64) { + let c = self.program[self.pos + 3]; + match self.modes.c { + Mode::Position => self.program[c as usize] = value, + Mode::Immediate => self.program[self.pos] = value, + } + } + + fn add(&mut self) -> usize { + self.set_c(self.get_a() + self.get_b()); + 4 + } + + fn multiply(&mut self) -> usize { + self.set_c(self.get_a() * self.get_b()); + 4 + } + + fn input(&mut self) -> usize { + let value = self.inputs.pop_front().expect("no input provided"); + self.set_a(value); + 2 + } + + fn output(&mut self) -> usize { + self.outputs.push_back(self.get_a()); + 2 + } + + fn jump_if_true(&mut self) -> usize { + if self.get_a() > 0 { + self.pos = self.get_b() as usize; + return 0; + } + 3 + } + + fn jump_if_false(&mut self) -> usize { + if self.get_a() == 0 { + self.pos = self.get_b() as usize; + return 0; + } + 3 + } + + fn less_than(&mut self) -> usize { + if self.get_a() < self.get_b() { + self.set_c(1) + } else { + self.set_c(0) + } + 4 + } + + fn equals(&mut self) -> usize { + if self.get_a() == self.get_b() { + self.set_c(1) + } else { + self.set_c(0) + } + 4 + } + + fn halt(&mut self) -> usize { + self.halted = true; + 0 + } +} + +fn build_args(elements: Vec, length: usize) -> Vec> { + let mut outs: Vec> = Vec::new(); + if length == 1 { + for e in elements { + outs.push(vec![e]); + } + return outs; + } + for i in 0..length { + let current: Vec = vec![elements[i]]; + let mut child_elements = elements.clone(); + child_elements.remove(i); + for child in build_args(child_elements, length - 1) { + let mut current = current.clone(); + current.append(&mut child.clone()); + outs.push(current); + } + } + outs +} + +fn main() -> io::Result<()> { + let filename = env::args().nth(1).expect("provide file as first param"); + let computer = Computer::from(File::open(filename)?); + + let orders = build_args(vec![0, 1, 2, 3, 4], 5); + let results: (i64, String) = orders + .iter() + .map(|order| { + let mut amp_a = computer.clone_with_input(vec![order[0], 0]); + let mut amp_b = computer.clone_with_input(vec![order[1], amp_a.run_until_output()]); + let mut amp_c = computer.clone_with_input(vec![order[2], amp_b.run_until_output()]); + let mut amp_d = computer.clone_with_input(vec![order[3], amp_c.run_until_output()]); + let mut amp_e = computer.clone_with_input(vec![order[4], amp_d.run_until_output()]); + ( + amp_e.run_until_output(), + order + .iter() + .map(|i| i.to_string()) + .collect::>() + .join(""), + ) + }) + .fold(None, |max, (output, order)| { + Some(match max { + None => (output, order), + Some((output_max, order_max)) => { + if output > output_max { + (output, order) + } else { + (output_max, order_max) + } + } + }) + }) + .unwrap(); + println!("{:?}", results); + + Ok(()) +}