Ver Fonte

Day 5 puzzle 1 complete

.. struggling to convert this to something that accepts BufRead/Write
traits to mock user input
Andrew Swistak há 6 anos atrás
pai
commit
4f3a9599c5
3 ficheiros alterados com 495 adições e 1 exclusões
  1. 490 0
      src/day_5/mod.rs
  2. 3 1
      src/logger/mod.rs
  3. 2 0
      src/main.rs

+ 490 - 0
src/day_5/mod.rs

@@ -0,0 +1,490 @@
+/*
+--- Day 5: Sunny with a Chance of Asteroids ---
+
+You're starting to sweat as the ship makes its way toward Mercury. The Elves suggest that you get
+the air conditioner working by upgrading your ship computer to support the Thermal Environment
+Supervision Terminal.
+
+The Thermal Environment Supervision Terminal (TEST) starts by running a diagnostic program (your
+puzzle input). The TEST diagnostic program will run on your existing Intcode computer after a few
+modifications:
+
+First, you'll need to add two new instructions:
+
+    Opcode 3 takes a single integer as input and saves it to the position given by its only
+    parameter. For example, the instruction 3,50 would take an input value and store it at address
+    50.
+
+    Opcode 4 outputs the value of its only parameter. For example, the instruction 4,50 would
+    output the value at address 50.
+
+Programs that use these instructions will come with documentation that explains what should be
+connected to the input and output. The program 3,0,4,0,99 outputs whatever it gets as input, then
+halts.
+
+Second, you'll need to add support for parameter modes:
+
+Each parameter of an instruction is handled based on its parameter mode. Right now, your ship
+computer already understands parameter mode 0, position mode, which causes the parameter to be
+interpreted as a position - if the parameter is 50, its value is the value stored at address 50 in
+memory. Until now, all parameters have been in position mode.
+
+Now, your ship computer will also need to handle parameters in mode 1, immediate mode. In immediate
+mode, a parameter is interpreted as a value - if the parameter is 50, its value is simply 50.
+
+Parameter modes are stored in the same value as the instruction's opcode. The opcode is a two-digit
+number based only on the ones and tens digit of the value, that is, the opcode is the rightmost two
+digits of the first value in an instruction. Parameter modes are single digits, one per parameter,
+read right-to-left from the opcode: the first parameter's mode is in the hundreds digit, the second
+parameter's mode is in the thousands digit, the third parameter's mode is in the ten-thousands
+digit, and so on. Any missing modes are 0.
+
+For example, consider the program 1002,4,3,4,33.
+
+The first instruction, 1002,4,3,4, is a multiply instruction - the rightmost two digits of the
+first value, 02, indicate opcode 2, multiplication. Then, going right to left, the parameter modes
+are 0 (hundreds digit), 1 (thousands digit), and 0 (ten-thousands digit, not present and therefore
+zero):
+
+ABCDE
+ 1002
+
+DE - two-digit opcode,      02 == opcode 2
+ C - mode of 1st parameter,  0 == position mode
+ B - mode of 2nd parameter,  1 == immediate mode
+ A - mode of 3rd parameter,  0 == position mode,
+                                  omitted due to being a leading zero
+
+This instruction multiplies its first two parameters. The first parameter, 4 in position mode,
+works like it did before - its value is the value stored at address 4 (33). The second parameter, 3
+in immediate mode, simply has value 3. The result of this operation, 33 * 3 = 99, is written
+according to the third parameter, 4 in position mode, which also works like it did before - 99 is
+written to address 4.
+
+Parameters that an instruction writes to will never be in immediate mode.
+
+Finally, some notes:
+
+    It is important to remember that the instruction pointer should increase by the number of
+    values in the instruction after the instruction finishes. Because of the new instructions, this
+    amount is no longer always 4.
+
+    Integers can be negative: 1101,100,-1,4,0 is a valid program (find 100 + -1, store the result
+        in position 4).
+
+The TEST diagnostic program will start by requesting from the user the ID of the system to test by
+running an input instruction - provide it 1, the ID for the ship's air conditioner unit.
+
+It will then perform a series of diagnostic tests confirming that various parts of the Intcode
+computer, like parameter modes, function correctly. For each test, it will run an output
+instruction indicating how far the result of the test was from the expected value, where 0 means
+the test was successful. Non-zero outputs mean that a function is not working correctly; check the
+instructions that were run before the output instruction to see which one failed.
+
+Finally, the program will output a diagnostic code and immediately halt. This final output isn't an
+error; an output followed immediately by a halt means the program finished. If all outputs were
+zero except the diagnostic code, the diagnostic program ran successfully.
+
+After providing 1 to the only input instruction and passing all the tests, what diagnostic code
+does the program produce?
+*/
+
+use log::{debug, trace};
+use std::convert::TryFrom;
+use std::io::{stdin, stdout, Write};
+
+use crate::puzzle::options::Options;
+use crate::puzzle::traits::{Create, Solve};
+
+pub struct Puzzle {
+    options: Options,
+    computer: Computer,
+}
+
+impl Solve for Puzzle {
+    fn solve(&self) -> i32 {
+        match self.options.puzzle {
+            1 => self.solve_p1(),
+            2 => self.solve_p2(),
+            x => panic!("Unknown puzzle {}", x),
+        }
+    }
+}
+
+impl Create for Puzzle {
+    fn new(opt: Options) -> Box<dyn Solve> {
+        Box::new(Self {
+            options: opt,
+            computer: Computer {
+                opcodes: if opt.prompt {
+                    Self::prompt_for_opcodes()
+                } else {
+                    Self::default_opcodes()
+                },
+            },
+        })
+    }
+}
+
+impl Puzzle {
+    fn solve_p1(&self) -> i32 {
+        let result = self.computer.run();
+        match result {
+            Ok(res) => res[0],
+            Err(e) => panic!(e),
+        }
+    }
+
+    fn solve_p2(&self) -> i32 {
+        unimplemented!()
+    }
+
+    fn prompt_for_opcodes() -> Vec<i32> {
+        println!("enter comma-separated values, like given: ");
+        let mut input = String::new();
+
+        match stdin().read_line(&mut input) {
+            Ok(_) => (),
+            Err(e) => panic!("Failed to read input: {}", e),
+        }
+
+        input
+            .split(',')
+            .map(|code| match code.trim().parse::<i32>() {
+                Ok(i) => i,
+                Err(e) => panic!("failed to parse {}: {}", code, e),
+            })
+            .collect()
+    }
+
+    fn default_opcodes() -> Vec<i32> {
+        vec![
+            3, 225, 1, 225, 6, 6, 1100, 1, 238, 225, 104, 0, 1101, 11, 91, 225, 1002, 121, 77, 224,
+            101, -6314, 224, 224, 4, 224, 1002, 223, 8, 223, 1001, 224, 3, 224, 1, 223, 224, 223,
+            1102, 74, 62, 225, 1102, 82, 7, 224, 1001, 224, -574, 224, 4, 224, 102, 8, 223, 223,
+            1001, 224, 3, 224, 1, 224, 223, 223, 1101, 28, 67, 225, 1102, 42, 15, 225, 2, 196, 96,
+            224, 101, -4446, 224, 224, 4, 224, 102, 8, 223, 223, 101, 6, 224, 224, 1, 223, 224,
+            223, 1101, 86, 57, 225, 1, 148, 69, 224, 1001, 224, -77, 224, 4, 224, 102, 8, 223, 223,
+            1001, 224, 2, 224, 1, 223, 224, 223, 1101, 82, 83, 225, 101, 87, 14, 224, 1001, 224,
+            -178, 224, 4, 224, 1002, 223, 8, 223, 101, 7, 224, 224, 1, 223, 224, 223, 1101, 38, 35,
+            225, 102, 31, 65, 224, 1001, 224, -868, 224, 4, 224, 1002, 223, 8, 223, 1001, 224, 5,
+            224, 1, 223, 224, 223, 1101, 57, 27, 224, 1001, 224, -84, 224, 4, 224, 102, 8, 223,
+            223, 1001, 224, 7, 224, 1, 223, 224, 223, 1101, 61, 78, 225, 1001, 40, 27, 224, 101,
+            -89, 224, 224, 4, 224, 1002, 223, 8, 223, 1001, 224, 1, 224, 1, 224, 223, 223, 4, 223,
+            99, 0, 0, 0, 677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1105, 0, 99999, 1105, 227, 247,
+            1105, 1, 99999, 1005, 227, 99999, 1005, 0, 256, 1105, 1, 99999, 1106, 227, 99999, 1106,
+            0, 265, 1105, 1, 99999, 1006, 0, 99999, 1006, 227, 274, 1105, 1, 99999, 1105, 1, 280,
+            1105, 1, 99999, 1, 225, 225, 225, 1101, 294, 0, 0, 105, 1, 0, 1105, 1, 99999, 1106, 0,
+            300, 1105, 1, 99999, 1, 225, 225, 225, 1101, 314, 0, 0, 106, 0, 0, 1105, 1, 99999,
+            1008, 677, 226, 224, 1002, 223, 2, 223, 1006, 224, 329, 101, 1, 223, 223, 8, 226, 677,
+            224, 102, 2, 223, 223, 1005, 224, 344, 101, 1, 223, 223, 1107, 226, 677, 224, 102, 2,
+            223, 223, 1006, 224, 359, 101, 1, 223, 223, 1007, 226, 226, 224, 102, 2, 223, 223,
+            1006, 224, 374, 101, 1, 223, 223, 7, 677, 677, 224, 102, 2, 223, 223, 1005, 224, 389,
+            1001, 223, 1, 223, 108, 677, 677, 224, 1002, 223, 2, 223, 1005, 224, 404, 101, 1, 223,
+            223, 1008, 226, 226, 224, 102, 2, 223, 223, 1005, 224, 419, 1001, 223, 1, 223, 1107,
+            677, 226, 224, 102, 2, 223, 223, 1005, 224, 434, 1001, 223, 1, 223, 1108, 677, 677,
+            224, 102, 2, 223, 223, 1006, 224, 449, 1001, 223, 1, 223, 7, 226, 677, 224, 102, 2,
+            223, 223, 1005, 224, 464, 101, 1, 223, 223, 1008, 677, 677, 224, 102, 2, 223, 223,
+            1005, 224, 479, 101, 1, 223, 223, 1007, 226, 677, 224, 1002, 223, 2, 223, 1006, 224,
+            494, 101, 1, 223, 223, 8, 677, 226, 224, 1002, 223, 2, 223, 1005, 224, 509, 101, 1,
+            223, 223, 1007, 677, 677, 224, 1002, 223, 2, 223, 1006, 224, 524, 101, 1, 223, 223,
+            107, 226, 226, 224, 102, 2, 223, 223, 1006, 224, 539, 101, 1, 223, 223, 107, 226, 677,
+            224, 102, 2, 223, 223, 1005, 224, 554, 1001, 223, 1, 223, 7, 677, 226, 224, 102, 2,
+            223, 223, 1006, 224, 569, 1001, 223, 1, 223, 107, 677, 677, 224, 1002, 223, 2, 223,
+            1005, 224, 584, 101, 1, 223, 223, 1107, 677, 677, 224, 102, 2, 223, 223, 1005, 224,
+            599, 101, 1, 223, 223, 1108, 226, 677, 224, 102, 2, 223, 223, 1006, 224, 614, 101, 1,
+            223, 223, 8, 226, 226, 224, 102, 2, 223, 223, 1006, 224, 629, 101, 1, 223, 223, 108,
+            226, 677, 224, 102, 2, 223, 223, 1005, 224, 644, 1001, 223, 1, 223, 108, 226, 226, 224,
+            102, 2, 223, 223, 1005, 224, 659, 101, 1, 223, 223, 1108, 677, 226, 224, 102, 2, 223,
+            223, 1006, 224, 674, 1001, 223, 1, 223, 4, 223, 99, 226,
+        ]
+    }
+}
+
+#[derive(Debug, Copy, Clone)]
+enum OpMode {
+    Position = 0,
+    Immediate,
+}
+
+impl TryFrom<i32> for OpMode {
+    type Error = i32;
+
+    fn try_from(v: i32) -> Result<Self, Self::Error> {
+        trace!("try converting {} to enum OpMode", v);
+
+        match v {
+            x if x == OpMode::Position as i32 => Ok(OpMode::Position),
+            x if x == OpMode::Immediate as i32 => Ok(OpMode::Immediate),
+            _ => Err(v),
+        }
+    }
+}
+
+#[derive(Debug)]
+enum OpCode {
+    Add = 1,
+    Mult,
+    Input,
+    Output,
+    Halt = 99,
+}
+
+impl TryFrom<i32> for OpCode {
+    type Error = i32;
+
+    fn try_from(v: i32) -> Result<Self, Self::Error> {
+        trace!("try converting {} to enum OpCode", v);
+
+        match v {
+            x if x == OpCode::Add as i32 => Ok(OpCode::Add),
+            x if x == OpCode::Mult as i32 => Ok(OpCode::Mult),
+            x if x == OpCode::Input as i32 => Ok(OpCode::Input),
+            x if x == OpCode::Output as i32 => Ok(OpCode::Output),
+            x if x == OpCode::Halt as i32 => Ok(OpCode::Halt),
+            _ => Err(v),
+        }
+    }
+}
+
+impl OpCode {
+    fn num_args(&self) -> i32 {
+        match self {
+            OpCode::Add => 4,
+            OpCode::Mult => 4,
+            OpCode::Input => 2,
+            OpCode::Output => 2,
+            OpCode::Halt => 0,
+        }
+    }
+}
+
+#[derive(Debug)]
+struct Operation<'a> {
+    opcode: OpCode,
+    opcodes: &'a mut Vec<i32>,
+    param_1_mode: OpMode,
+    param_2_mode: OpMode,
+    param_3_mode: OpMode,
+}
+
+type OperationParseError = String;
+
+impl Operation<'_> {
+    fn new(opcodes: &mut Vec<i32>, index: usize) -> Result<Operation, OperationParseError> {
+        let mut opcode = opcodes[index];
+        debug!("try converting {} to Operation", opcode);
+
+        let operation = match OpCode::try_from(opcode % 100) {
+            Ok(x) => x,
+            Err(e) => return Err(format!("Unknown opcode {}", e)),
+        };
+        opcode /= 100;
+        trace!("operation: {:?}, opcode: {}", operation, opcode);
+
+        let param_1_mode = match OpMode::try_from(opcode % 10) {
+            Ok(x) => x,
+            Err(e) => return Err(format!("unknown operation mode {}", e)),
+        };
+        opcode /= 10;
+        trace!("param_1_mode: {:?}, opcode: {}", param_1_mode, opcode);
+
+        let param_2_mode = match OpMode::try_from(opcode % 10) {
+            Ok(x) => x,
+            Err(e) => return Err(format!("unknown operation mode {}", e)),
+        };
+        opcode /= 10;
+        trace!("param_2_mode: {:?}, opcode: {}", param_2_mode, opcode);
+
+        let param_3_mode = match OpMode::try_from(opcode % 10) {
+            Ok(x) => x,
+            Err(e) => return Err(format!("unknown operation mode {}", e)),
+        };
+        trace!("param_3_mode: {:?}", param_2_mode);
+
+        Ok(Operation {
+            opcode: operation,
+            opcodes: opcodes,
+            param_1_mode,
+            param_2_mode,
+            param_3_mode,
+        })
+    }
+
+    fn apply(&mut self, index: usize) -> Result<Option<usize>, String> {
+        debug!("applying {:?} at index {}", self, index);
+
+        match &self.opcode {
+            OpCode::Add => {
+                let arg1 = self.value_from(self.param_1_mode, index + 1);
+                let arg2 = self.value_from(self.param_2_mode, index + 2);
+                self.value_to(self.param_3_mode, index + 3, arg1 + arg2);
+            }
+            OpCode::Mult => {
+                let arg1 = self.value_from(self.param_1_mode, index + 1);
+                let arg2 = self.value_from(self.param_2_mode, index + 2);
+                self.value_to(self.param_3_mode, index + 3, arg1 * arg2);
+            }
+            OpCode::Input => {
+                print!("Input: ");
+                stdout().flush().unwrap();
+
+                let input = &mut String::new();
+                let _ = stdin().read_line(input);
+                let num: i32 = match input.trim().parse() {
+                    Ok(x) => x,
+                    Err(e) => return Err(format!("failed to decode input {}: {}", input, e)),
+                };
+                self.value_to(self.param_1_mode, index + 1, num);
+            }
+            OpCode::Output => {
+                let arg1 = self.value_from(self.param_1_mode, index + 1);
+                println!("Output: {}", arg1);
+            }
+            OpCode::Halt => return Ok(None),
+        }
+
+        Ok(Some(self.opcode.num_args() as usize))
+    }
+
+    fn value_from(&mut self, opmode: OpMode, index: usize) -> i32 {
+        debug!("GET via OpMode {:?} from index {}", opmode, index);
+
+        match opmode {
+            OpMode::Position => {
+                let val = self.opcodes[self.opcodes[index] as usize];
+                trace!("retrieving position {}: {}", index, val);
+                val
+            }
+            OpMode::Immediate => {
+                let val = self.opcodes[index];
+                trace!("directly using position {}: {}", index, val);
+                val
+            }
+        }
+    }
+
+    fn value_to(&mut self, opmode: OpMode, index: usize, value: i32) {
+        debug!("SET {} via OpMode {:?} to index {}", value, opmode, index);
+
+        match opmode {
+            OpMode::Position => {
+                let pos = self.opcodes[index] as usize;
+                trace!(
+                    "storing value {} to position {}: {}",
+                    value,
+                    pos,
+                    self.opcodes[index]
+                );
+                self.opcodes[pos] = value
+            }
+            OpMode::Immediate => {
+                trace!(
+                    "immediately storing value {} to position {}: {}",
+                    value,
+                    index,
+                    self.opcodes[index]
+                );
+                self.opcodes[index] = value
+            }
+        }
+    }
+}
+
+struct Computer {
+    opcodes: Vec<i32>,
+}
+
+impl Computer {
+    fn run(&self) -> Result<Vec<i32>, String> {
+        let mut index: usize = 0;
+        let ref mut opcodes = self.opcodes.clone();
+
+        while index < opcodes.len() {
+            let mut op = match Operation::new(opcodes, index) {
+                Ok(x) => x,
+                Err(e) => {
+                    return Err(format!(
+                        "failed to parse operation '{}': {}",
+                        opcodes[index], e
+                    ))
+                }
+            };
+
+            match op.apply(index) {
+                Ok(Some(i)) => index += i,
+                Ok(None) => break,
+                Err(e) => return Err(e),
+            }
+        }
+
+        debug!("resulting opcodes: {:?}", opcodes);
+
+        Ok(opcodes.to_owned())
+    }
+}
+
+#[cfg(test)]
+mod puzzle_1 {
+    use super::Computer;
+
+    #[test]
+    fn opcodes_sample_1() {
+        assert_eq!(
+            Computer {
+                opcodes: vec![1002, 4, 3, 4, 33],
+            }
+            .run()
+            .unwrap(),
+            vec![1002, 4, 3, 4, 99]
+        );
+    }
+
+    #[test]
+    fn opcodes_sample_2() {
+        assert_eq!(
+            Computer {
+                opcodes: vec![1, 9, 10, 3, 2, 3, 11, 0, 99, 30, 40, 50]
+            }
+            .run()
+            .unwrap(),
+            vec![3500, 9, 10, 70, 2, 3, 11, 0, 99, 30, 40, 50]
+        );
+    }
+
+    #[test]
+    fn opcodes_sample_3() {
+        assert_eq!(
+            Computer {
+                opcodes: vec![10001, 1, 1, 0, 99]
+            }
+            .run()
+            .unwrap(),
+            vec![10001, 1, 1, 2, 99]
+        );
+    }
+
+    #[test]
+    fn opcodes_sample_4() {
+        assert_eq!(
+            Computer {
+                opcodes: vec![1, 0, 0, 0, 99]
+            }
+            .run()
+            .unwrap(),
+            vec![2, 0, 0, 0, 99]
+        );
+    }
+
+    #[test]
+    fn opcodes_sample_5() {
+        assert_eq!(
+            Computer {
+                opcodes: vec![3, 0, 4, 0, 99]
+            }
+            .run()
+            .unwrap(),
+            vec![2, 0, 0, 0, 99]
+        );
+    }
+}

+ 3 - 1
src/logger/mod.rs

@@ -20,7 +20,9 @@ impl log::Log for Logger {
 pub fn init(level: u8) -> Result<(), SetLoggerError> {
     let level_filter: LevelFilter = match level {
         0 => LevelFilter::Off,
-        _ => LevelFilter::Debug,
+        1 => LevelFilter::Debug,
+        2 => LevelFilter::Trace,
+        _ => LevelFilter::Trace,
     };
 
     log::set_boxed_logger(Box::new(Logger)).map(|()| log::set_max_level(level_filter))

+ 2 - 0
src/main.rs

@@ -7,6 +7,7 @@ mod day_1;
 mod day_2;
 mod day_3;
 mod day_4;
+mod day_5;
 
 mod puzzle;
 
@@ -34,6 +35,7 @@ fn main() {
         2 => day_2::Puzzle::new(options),
         3 => day_3::Puzzle::new(options),
         4 => day_4::Puzzle::new(options),
+        5 => day_5::Puzzle::new(options),
         e => panic!("unknown day {}", e),
     };