diff --git a/day-8/.gitignore b/day-8/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/day-8/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/day-8/Cargo.lock b/day-8/Cargo.lock new file mode 100644 index 0000000..81f3040 --- /dev/null +++ b/day-8/Cargo.lock @@ -0,0 +1,106 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "day-8" +version = "0.1.0" +dependencies = [ + "indoc", + "snafu", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "indoc" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47741a8bc60fb26eb8d6e0238bbb26d8575ff623fdc97b1a2c00c050b9684ed8" +dependencies = [ + "indoc-impl", + "proc-macro-hack", +] + +[[package]] +name = "indoc-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", + "unindent", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "snafu" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab12d3c261b2308b0d80c26fffb58d17eba81a4be97890101f416b478c79ca7" +dependencies = [ + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "unindent" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" diff --git a/day-8/Cargo.toml b/day-8/Cargo.toml new file mode 100644 index 0000000..9cc66ff --- /dev/null +++ b/day-8/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "day-8" +version = "0.1.0" +authors = ["Tristan Daniƫl Maat "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +indoc = "0.3" +snafu = "0.6" diff --git a/day-8/input b/day-8/input new file mode 100644 index 0000000..764999d --- /dev/null +++ b/day-8/input @@ -0,0 +1,612 @@ +jmp +236 +acc +43 +acc +28 +jmp +149 +acc +28 +acc +13 +acc +36 +acc +42 +jmp +439 +acc -14 +jmp +29 +jmp +154 +acc +16 +acc -13 +acc -16 +nop +317 +jmp +497 +acc +21 +jmp +386 +jmp +373 +acc +22 +jmp +311 +acc -16 +acc +27 +acc +21 +acc +43 +jmp +512 +jmp +218 +jmp +217 +acc +12 +acc +44 +nop +367 +nop +180 +jmp +134 +acc -2 +acc +42 +acc +13 +acc -11 +jmp +442 +nop +457 +jmp +151 +acc +15 +acc -4 +acc +0 +jmp +131 +acc +6 +acc -2 +acc +37 +jmp +112 +acc +32 +acc +6 +acc -15 +jmp +474 +jmp +515 +acc +12 +acc +11 +acc +4 +jmp +339 +acc -3 +acc +36 +jmp +220 +nop +91 +acc -12 +jmp +49 +acc -17 +jmp +204 +acc +40 +jmp +535 +acc +37 +acc +8 +nop +147 +nop +174 +jmp +306 +jmp +305 +acc +7 +acc +33 +jmp +305 +acc +22 +acc +17 +acc +24 +jmp +458 +jmp +1 +acc +36 +acc +34 +jmp +113 +acc -3 +nop +113 +nop -34 +jmp +506 +acc -19 +acc +21 +acc +35 +acc -1 +jmp +74 +acc +15 +acc +7 +jmp +79 +acc +29 +acc +42 +jmp +427 +acc +33 +jmp +29 +acc +6 +acc +13 +nop +477 +acc +26 +jmp +493 +acc +33 +acc +43 +acc +49 +acc +35 +jmp +409 +acc -7 +acc +35 +acc +40 +jmp +309 +acc -13 +acc -14 +acc +32 +jmp +322 +jmp +10 +jmp +44 +acc +20 +acc +25 +jmp +175 +acc +22 +acc +16 +acc +1 +acc +36 +jmp -65 +jmp +231 +acc +35 +jmp +155 +jmp +218 +acc -10 +acc -13 +acc +38 +jmp -92 +acc +15 +jmp +134 +acc -16 +acc +18 +jmp -30 +nop -41 +acc +48 +acc +49 +jmp -107 +acc +4 +acc +34 +acc +38 +acc -18 +jmp +247 +acc +45 +acc +23 +jmp +149 +nop +164 +acc +26 +jmp -24 +jmp +240 +jmp +77 +acc +30 +acc -13 +jmp -158 +nop +136 +jmp +33 +jmp +189 +jmp +143 +jmp +1 +acc +4 +acc +30 +jmp -106 +acc +16 +nop -52 +acc +37 +jmp +119 +acc -11 +acc -9 +acc +15 +acc +4 +jmp +301 +jmp +1 +acc -3 +jmp +188 +nop +86 +nop +125 +acc -10 +jmp -105 +acc +36 +acc +9 +acc +0 +jmp +317 +jmp +347 +acc +48 +nop +380 +acc -18 +acc +28 +jmp +398 +jmp -152 +jmp -86 +acc +22 +acc +11 +acc +39 +jmp -173 +jmp +343 +nop +194 +nop +98 +nop +382 +jmp +300 +acc +35 +nop +287 +acc -8 +jmp +302 +acc +19 +acc +45 +jmp +95 +acc +29 +jmp +274 +acc +18 +acc -13 +acc +23 +acc +7 +jmp +164 +acc +17 +acc +36 +acc -5 +jmp +153 +acc +21 +jmp +105 +jmp +1 +nop +267 +jmp +277 +jmp +88 +acc +2 +acc +18 +nop +182 +jmp +189 +acc +37 +acc +46 +jmp +258 +acc +22 +acc +15 +jmp +249 +acc +17 +jmp -162 +jmp +25 +acc -6 +nop +314 +jmp -30 +jmp +312 +acc +34 +nop -230 +acc -2 +jmp +158 +acc -4 +acc +37 +jmp +318 +acc +18 +acc +23 +acc -8 +jmp -248 +jmp +181 +acc +17 +acc +4 +jmp -189 +acc +27 +acc -13 +acc -4 +acc +8 +jmp +222 +jmp +310 +acc -5 +acc +35 +jmp +241 +jmp -130 +jmp +124 +acc -19 +jmp +331 +acc -8 +acc +45 +jmp +106 +acc +23 +acc +48 +jmp -107 +acc +7 +acc -19 +acc +3 +jmp +130 +jmp -104 +nop +5 +acc +29 +acc +8 +acc -6 +jmp +7 +acc +12 +jmp +102 +acc -4 +acc +46 +acc -17 +jmp -209 +acc +20 +jmp -271 +acc +48 +jmp +30 +nop +204 +acc -19 +acc +4 +acc +38 +jmp +17 +jmp +116 +acc -17 +acc +23 +jmp -75 +jmp -129 +jmp +152 +acc +36 +nop -193 +acc +26 +acc +38 +jmp +242 +jmp -197 +acc +32 +acc -5 +acc -19 +jmp -201 +jmp -304 +acc +9 +jmp +175 +acc +1 +jmp -15 +jmp +1 +nop -74 +jmp -38 +nop -165 +acc -19 +jmp -317 +acc -19 +acc -1 +jmp +17 +acc +0 +nop +151 +jmp +93 +acc +32 +acc +29 +acc +0 +jmp -340 +acc +39 +jmp -115 +acc +0 +acc +47 +nop -320 +jmp +244 +acc +29 +jmp +81 +jmp -84 +acc +2 +acc +16 +nop -345 +acc +23 +jmp +9 +acc +26 +jmp -67 +acc -11 +acc +38 +jmp +150 +acc +19 +acc -2 +jmp -244 +jmp +88 +acc -4 +jmp -157 +acc +22 +acc +33 +acc +41 +jmp -117 +acc +31 +acc +50 +acc +24 +jmp -265 +jmp +1 +jmp -352 +jmp -312 +acc +35 +acc +30 +jmp -90 +jmp +8 +acc +14 +acc +39 +jmp -112 +acc -11 +acc -3 +acc +22 +jmp -116 +acc +48 +jmp -194 +acc -5 +jmp -252 +jmp +66 +jmp -295 +jmp +196 +acc +25 +acc -11 +nop +112 +acc +33 +jmp +123 +acc -10 +acc +28 +nop -119 +acc +12 +jmp -166 +jmp -356 +acc +8 +acc +16 +jmp +161 +acc +25 +acc +3 +jmp -5 +acc +32 +acc +40 +jmp +181 +acc -11 +acc -5 +jmp +1 +acc +0 +jmp -265 +acc +5 +acc +24 +acc +15 +acc -17 +jmp -326 +nop +103 +acc -9 +acc +13 +jmp -379 +acc +38 +acc +16 +jmp -65 +jmp +1 +jmp +1 +jmp +1 +acc -1 +jmp -191 +acc +35 +acc -19 +acc -6 +jmp -52 +acc +15 +jmp -357 +nop -134 +acc -3 +nop +103 +jmp -123 +acc +43 +acc +0 +acc +47 +jmp -373 +acc +0 +acc +50 +acc +44 +acc +21 +jmp -114 +acc -19 +jmp -339 +acc +25 +jmp -410 +jmp -126 +acc -2 +acc -6 +acc +14 +jmp -207 +acc +35 +acc -7 +jmp +75 +acc +9 +acc +22 +jmp +114 +acc +18 +acc +36 +acc +0 +acc +40 +jmp -192 +acc +35 +acc +0 +acc +28 +acc +3 +jmp -346 +nop -131 +acc +46 +nop -467 +nop -179 +jmp -151 +jmp -120 +acc +30 +acc +22 +acc -7 +acc +18 +jmp -157 +acc +5 +jmp +76 +nop -315 +acc +25 +jmp -357 +acc +44 +jmp -12 +acc +0 +acc +19 +nop -485 +jmp -495 +nop -115 +acc +12 +jmp -8 +acc +31 +acc -7 +jmp -158 +acc +44 +acc +32 +jmp +87 +acc +1 +acc +37 +acc +44 +jmp -86 +acc +0 +acc +17 +acc -13 +jmp -434 +acc +37 +jmp -342 +acc +3 +jmp +1 +acc +29 +jmp -242 +acc +48 +jmp -442 +jmp -283 +acc -19 +acc +6 +acc +20 +acc +44 +jmp -533 +acc -15 +nop -356 +acc +18 +jmp -408 +acc -9 +acc +17 +acc +16 +jmp -385 +nop -130 +jmp +1 +acc +38 +acc +39 +jmp -324 +jmp -141 +acc +4 +acc +3 +acc -4 +jmp -114 +acc +2 +jmp +1 +acc +44 +jmp -360 +acc +43 +acc +36 +nop -177 +nop -288 +jmp -496 +acc +45 +acc +0 +jmp -322 +acc +13 +jmp -511 +acc -2 +acc +36 +jmp -460 +acc +28 +acc +28 +jmp -455 +acc -4 +acc +38 +jmp -145 +jmp -163 +jmp -331 +nop -227 +jmp -470 +acc +35 +nop -419 +acc +39 +acc +0 +jmp -435 +jmp +1 +jmp -69 +acc +20 +acc +46 +nop +2 +jmp -239 +acc -3 +acc +12 +acc +38 +jmp -259 +jmp -60 +jmp -67 +nop -542 +jmp -397 +acc +32 +jmp -57 +acc +30 +nop -393 +jmp -380 +acc +16 +acc -7 +acc +0 +acc +2 +jmp +1 diff --git a/day-8/src/emulator.rs b/day-8/src/emulator.rs new file mode 100644 index 0000000..6afb2ed --- /dev/null +++ b/day-8/src/emulator.rs @@ -0,0 +1,187 @@ +use std::collections::HashSet; + +use snafu::Snafu; + +pub type Program = Vec<(Instruction, i64)>; + +#[derive(Debug, PartialEq, Snafu)] +pub enum EmulatorError { + #[snafu(display("Segmentation fault"))] + SegmentationFault, + #[snafu(display("Infinite loop: {}", accumulator))] + InfiniteLoop { accumulator: i64 }, +} + +#[derive(Debug, Snafu)] +pub enum ProgramParseError { + #[snafu(display("Invalid instruction given: {}", instruction))] + InvalidInstruction { instruction: String }, + #[snafu(display("Invalid argument given: {}", argument))] + InvalidArgument { argument: String }, + #[snafu(display("Unparseable line: {}", line))] + InvalidLine { line: String }, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Instruction { + Accumulate, + Jump, + NoOperation, +} + +pub struct Emulator { + accumulator: i64, + program_counter: usize, +} + +impl Emulator { + pub fn new() -> Self { + Self { + accumulator: 0, + program_counter: 0, + } + } + + pub fn reset(&mut self) { + self.accumulator = 0; + self.program_counter = 0; + } + + fn execute_instruction( + &mut self, + instruction: Instruction, + argument: i64, + ) -> Result<(), EmulatorError> { + match instruction { + Instruction::Accumulate => self.accumulator += argument, + Instruction::Jump => { + self.program_counter = self.program_counter.wrapping_add(argument as usize - 1) + } + Instruction::NoOperation => {} + } + + Ok(()) + } + + pub fn run_program_finitely(&mut self, program: &Program) -> Result { + let mut executed = HashSet::new(); + + loop { + let (instruction, argument) = program + .get(self.program_counter) + .ok_or(EmulatorError::SegmentationFault)?; + executed.insert(self.program_counter); + + self.execute_instruction(*instruction, *argument)?; + + self.program_counter += 1; + + if executed.contains(&self.program_counter) { + break Err(EmulatorError::InfiniteLoop { + accumulator: self.accumulator, + }); + } else if self.program_counter >= program.len() { + break Ok(self.accumulator); + } + } + } +} + +pub fn parse_code(input: &str) -> Result { + input + .lines() + .map(|instruction| { + let (name, argument): (&str, &str) = { + let temp: Vec<&str> = instruction.split(' ').collect(); + if temp.len() != 2 { + Err(ProgramParseError::InvalidLine { + line: instruction.to_string(), + }) + } else { + Ok((temp[0], temp[1])) + } + }?; + + Ok(( + match name { + "acc" => Instruction::Accumulate, + "jmp" => Instruction::Jump, + "nop" => Instruction::NoOperation, + _ => Err(ProgramParseError::InvalidInstruction { + instruction: name.to_string(), + })?, + }, + argument + .parse() + .or(Err(ProgramParseError::InvalidArgument { + argument: argument.to_string(), + }))?, + )) + }) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + use indoc::indoc; + + #[test] + fn test_parsing() -> Result<(), Box> { + let input = indoc!( + "nop +0 + acc +1 + jmp +4 + acc +3 + jmp -3 + acc -99 + acc +1 + jmp -4 + acc +6 + " + ); + + let code = parse_code(&input)?; + assert_eq!( + code, + vec![ + (Instruction::NoOperation, 0), + (Instruction::Accumulate, 1), + (Instruction::Jump, 4), + (Instruction::Accumulate, 3), + (Instruction::Jump, -3), + (Instruction::Accumulate, -99), + (Instruction::Accumulate, 1), + (Instruction::Jump, -4), + (Instruction::Accumulate, 6), + ] + ); + + Ok(()) + } + + #[test] + fn test_running() -> Result<(), Box> { + let input = indoc!( + "nop +0 + acc +1 + jmp +4 + acc +3 + jmp -3 + acc -99 + acc +1 + jmp -4 + acc +6 + " + ); + + let code = parse_code(&input)?; + let mut emulator = Emulator::new(); + assert_eq!( + emulator.run_program_finitely(&code), + Err(EmulatorError::InfiniteLoop { accumulator: 5 }) + ); + + Ok(()) + } +} diff --git a/day-8/src/main.rs b/day-8/src/main.rs new file mode 100644 index 0000000..e4afef8 --- /dev/null +++ b/day-8/src/main.rs @@ -0,0 +1,49 @@ +use std::fs; + +mod emulator; + +use emulator::{parse_code, Emulator, EmulatorError, Instruction}; + +fn main() -> Result<(), Box> { + let input = fs::read_to_string("input")?; + let program = parse_code(&input)?; + + // Part 1 + let mut emulator = Emulator::new(); + if let Err(EmulatorError::InfiniteLoop { accumulator }) = + emulator.run_program_finitely(&program) + { + println!("{}", accumulator); + } else { + panic!("We should get an infinite loop."); + } + + // Part 2 + let result = program + .iter() + .enumerate() + .filter_map(|(i, instruction)| match instruction { + (Instruction::Jump, arg) => { + let mut program = program.clone(); + program[i] = (Instruction::NoOperation, *arg); + emulator.reset(); + emulator.run_program_finitely(&program).ok() + } + (Instruction::NoOperation, arg) => { + let mut program = program.clone(); + program[i] = (Instruction::Jump, *arg); + emulator.reset(); + emulator.run_program_finitely(&program).ok() + } + (_, _) => None, + }) + .next(); + + if let Some(result) = result { + println!("{}", result); + } else { + panic!("We should have at least one non-infinte loop."); + } + + Ok(()) +}