| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490 |
- /*
- --- 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]
- );
- }
- }
|