A Day 8 solution I'm pretty happy with.

This commit is contained in:
2020-12-08 09:58:17 -08:00
parent 001a8cf012
commit ab417307f3
4 changed files with 867 additions and 0 deletions

641
inputs/day8.txt Normal file
View File

@@ -0,0 +1,641 @@
acc -8
acc +19
nop +178
jmp +493
acc +7
jmp +267
jmp +604
acc +30
acc +11
acc -17
nop +402
jmp +81
acc +20
acc +19
acc +36
jmp +6
acc +43
acc +6
acc +10
nop +326
jmp +228
nop +371
acc +49
nop +140
acc -11
jmp +3
nop +366
jmp +74
nop +229
jmp +554
acc +12
acc +6
jmp +163
acc +43
acc +23
jmp +310
acc -6
nop +341
acc +50
acc +44
jmp +378
acc +28
acc +29
nop +76
jmp +136
nop +445
acc +27
acc -8
acc +34
jmp +199
acc +39
acc +25
acc -14
acc +1
jmp +189
acc +2
acc -9
jmp -7
acc +28
acc +28
jmp +458
jmp +1
nop +299
jmp +427
acc +10
acc +32
jmp +340
acc +26
jmp +563
jmp +1
acc +9
jmp +42
acc +4
jmp +468
acc +1
acc +3
jmp +258
acc +29
acc +7
acc -5
jmp +288
acc +22
acc +32
acc -6
jmp +173
acc +48
acc +42
acc +26
jmp +380
jmp +126
acc +45
jmp -27
acc +50
jmp +14
jmp +472
acc -19
jmp +363
acc +19
acc -8
acc +25
jmp +450
acc -7
acc +27
acc +44
acc +17
jmp +487
jmp +89
nop +216
nop +345
acc -1
acc +37
jmp +455
jmp +294
acc +20
acc +38
jmp +419
acc +17
acc +17
jmp +125
jmp +81
acc +37
acc -8
acc +9
acc +31
jmp +218
acc +24
acc +28
acc -4
acc -12
jmp -40
acc +40
nop +359
nop +182
nop +306
jmp +296
acc -8
jmp +1
nop +43
acc -14
jmp +239
acc +13
acc +10
jmp +1
jmp -36
acc -16
acc +2
jmp +344
jmp +442
acc +35
acc +2
acc +27
acc +17
jmp -27
nop +478
acc +6
acc +7
jmp +454
nop -145
acc +20
acc -6
jmp +182
nop +251
jmp -37
acc +26
jmp +300
acc +29
acc +44
acc +32
nop +56
jmp +31
acc -16
jmp +53
acc -9
jmp +84
jmp +1
nop +30
acc -15
jmp +262
acc -19
jmp +163
jmp +441
acc +27
jmp +449
acc +42
acc +45
acc +21
acc +22
jmp +338
acc +24
jmp +301
acc +42
acc +42
acc +26
jmp +348
jmp +361
acc -5
acc -19
acc +4
jmp -117
jmp +254
jmp +1
acc +47
acc -3
jmp +271
jmp +388
acc +2
acc -17
acc +37
jmp -73
acc +37
acc +34
jmp +1
jmp -148
jmp -56
jmp +103
acc -5
acc +23
acc +3
jmp +405
nop +255
acc +14
nop -41
acc +12
jmp +94
acc +22
acc +30
jmp -107
acc +12
acc -2
jmp +65
acc +35
acc -4
jmp -174
nop -159
acc +47
jmp -52
acc +35
jmp +73
acc +1
acc +19
acc +35
acc +15
jmp -59
jmp +312
acc +20
acc +25
acc +45
jmp -4
acc -4
nop -160
acc -8
acc +31
jmp +166
acc +20
acc +16
acc -1
jmp +234
acc +0
jmp -45
acc +47
acc +17
nop -187
nop +206
jmp +17
acc +36
acc +0
acc +7
jmp +263
acc +32
acc -6
nop +35
jmp -101
acc +49
nop -60
jmp +118
acc -1
acc -7
nop -94
acc +21
jmp +82
nop +216
acc +5
nop -99
jmp -47
acc +31
acc +2
acc +26
acc +27
jmp -224
acc +15
acc +48
jmp +220
nop +152
jmp -69
acc +4
acc +24
jmp +200
acc +14
jmp +126
acc +48
acc +47
acc +10
jmp +26
acc +16
jmp -203
acc +21
jmp -158
acc -15
acc -13
jmp -94
jmp -136
nop -247
acc +16
jmp -130
acc +31
jmp +115
jmp -159
acc +7
acc +50
jmp +52
acc +22
acc +26
jmp +249
acc -18
jmp +1
jmp -251
nop +254
jmp -127
acc +37
jmp -93
nop +73
acc +11
acc +36
jmp +277
acc +29
acc +16
jmp -88
nop +8
acc +18
acc +47
acc -9
jmp +184
jmp -142
acc +50
jmp +287
jmp -250
jmp -296
jmp -83
acc +13
acc +29
acc +28
acc +16
jmp +40
acc +33
acc -13
jmp +43
nop +275
acc +24
nop -257
nop -65
jmp -112
acc +4
acc +38
jmp -193
jmp +1
acc -18
acc +15
jmp -223
acc -18
jmp -55
jmp -207
acc -6
jmp -215
acc +16
acc +44
jmp +1
acc +47
jmp -35
acc +47
acc +47
acc +35
jmp +144
jmp +1
acc +45
acc +25
jmp -293
acc +32
jmp -381
nop +65
jmp +1
acc +2
jmp -74
acc -13
acc -9
acc +4
jmp -251
jmp +1
jmp +71
acc -12
acc +7
acc +15
jmp +11
jmp -68
acc +33
jmp -330
jmp +48
acc -15
acc -11
jmp +97
acc -9
acc -10
jmp +100
acc +29
acc +21
jmp -134
acc -18
acc +38
jmp +67
jmp -12
acc +27
acc +26
acc -8
acc -2
jmp -124
jmp +165
nop -245
acc -16
acc +25
acc -19
jmp -328
nop -182
acc -7
acc +46
jmp -250
acc +45
acc -7
nop -256
acc -2
jmp +21
acc +21
acc +37
jmp +156
nop +32
jmp -195
nop -355
acc -14
nop -302
acc +48
jmp -407
acc +50
acc -9
acc +47
jmp -110
acc +31
acc +37
acc +15
jmp -162
acc -14
jmp -437
acc +44
jmp +1
acc +24
jmp -139
jmp -362
acc +40
jmp -41
acc +38
jmp -231
acc +31
acc +23
jmp +135
acc -19
acc +15
jmp +148
acc +16
acc -18
acc -3
acc +1
jmp -189
acc -12
acc -6
acc -18
nop -454
jmp +83
nop -190
jmp -17
acc -7
acc +34
acc -1
jmp +94
acc +42
jmp +34
nop -150
nop +90
nop -126
jmp -161
acc +5
acc +11
acc +20
acc +38
jmp -97
acc +49
acc +29
acc +26
jmp -36
acc +4
acc -14
acc +30
acc +42
jmp -192
jmp -336
acc +34
acc +31
acc +2
acc +33
jmp +65
acc +4
jmp -459
nop -399
acc -6
nop -256
jmp -420
acc -12
acc -17
jmp -276
acc +45
acc +40
jmp -180
acc +50
jmp -501
acc +17
jmp -232
acc +12
jmp -109
nop -291
nop -345
jmp +100
acc +36
acc +2
acc -2
jmp +1
acc +23
nop -299
acc +24
acc +30
jmp -476
acc +0
acc +6
acc +49
jmp +6
nop -461
jmp -539
nop -62
acc +48
jmp -526
jmp -365
acc +47
acc +10
acc +32
jmp -490
nop -148
acc +42
acc +5
jmp -358
acc -5
jmp -101
jmp -502
acc +15
acc +45
nop -399
jmp +1
acc +31
acc +47
acc +49
jmp -269
acc +6
acc +45
acc -8
acc -6
jmp +36
jmp +51
acc +39
jmp -64
acc +47
jmp +1
acc -8
jmp -102
acc -8
jmp -202
jmp -18
acc +1
jmp -484
acc +35
acc +30
acc +49
jmp -562
jmp -515
acc -13
nop -6
jmp -369
acc +27
acc +18
nop -477
acc -10
jmp -430
jmp +1
acc +7
nop -111
jmp -445
jmp +12
jmp -50
acc +7
acc +3
nop -433
jmp -390
acc -5
acc +50
jmp -67
acc +45
acc -10
jmp -446
jmp -496
jmp -17
acc +14
acc +33
jmp -239
acc +3
acc -3
acc +27
acc -3
jmp -162
jmp -16
acc +23
acc +26
acc +25
jmp -346
acc +40
acc +45
acc +42
acc -4
jmp +1

9
inputs/day8_test.txt Normal file
View File

@@ -0,0 +1,9 @@
nop +0
acc +1
jmp +4
acc +3
jmp -3
acc -99
acc +1
jmp -4
acc +6

186
src/bin/machine.rs Normal file
View File

@@ -0,0 +1,186 @@
use advent2020::errors::{ExecutionError, InstructionParseError, TopLevelError};
use std::{collections::HashSet, env};
use std::fmt;
use std::fs;
use std::str::FromStr;
#[derive(Clone)]
struct Machine {
instructions: Vec<Instruction>,
accumulator: isize,
location: isize,
}
#[derive(Clone)]
enum Instruction {
NOP(isize),
ACC(isize),
JMP(isize),
}
impl FromStr for Instruction {
type Err = InstructionParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut lowered = s.to_string();
lowered.make_ascii_lowercase();
let mut items = s.split(' ');
let instruction = items.next().ok_or(InstructionParseError::EmptyInstruction)?;
let operand = items.next().ok_or(InstructionParseError::MissingOperand(instruction.to_string()))?;
let operand_value = isize::from_str(operand)?;
match instruction {
"nop" => Ok(Instruction::NOP(operand_value)),
"acc" => Ok(Instruction::ACC(operand_value)),
"jmp" => Ok(Instruction::JMP(operand_value)),
_ => Err(InstructionParseError::UnknownOpcode(instruction.to_string())),
}
}
}
impl fmt::Display for Instruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Instruction::NOP(s) => write!(f, "NOP {:+}", s),
Instruction::ACC(s) => write!(f, "ACC {:+}", s),
Instruction::JMP(s) => write!(f, "JMP {:+}", s),
}
}
}
impl FromStr for Machine {
type Err = InstructionParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut instructions = Vec::new();
for line in s.lines() {
let instruction = Instruction::from_str(line)?;
instructions.push(instruction);
}
Ok(Machine {
instructions,
accumulator: 0,
location: 0,
})
}
}
impl Machine {
fn pretty_print(&self) {
for (idx, instr) in self.instructions.iter().enumerate() {
let pointer = if (idx as isize) == self.location { "--> " } else { " " };
println!("{} {:04}: {}", pointer, idx, instr);
}
}
fn step(&mut self) -> Result<(), ExecutionError> {
if self.location < 0 || self.location >= (self.instructions.len() as isize) {
return Err(ExecutionError::NonExistentLocation(self.location));
}
match self.instructions[self.location as usize] {
Instruction::NOP(_) => self.location += 1,
Instruction::JMP(x) => self.location += x,
Instruction::ACC(x) => {
self.location += 1;
self.accumulator += x;
}
}
Ok(())
}
fn terminates(&mut self) -> Result<(bool, isize), ExecutionError> {
let mut visited_locations = HashSet::new();
loop {
let current_location = self.location;
let current_accumulator = self.accumulator;
visited_locations.insert(current_location);
self.step()?;
if visited_locations.contains(&self.location) {
return Ok((false, current_accumulator));
}
if self.location == (self.instructions.len() as isize) {
return Ok((true, self.accumulator));
}
}
}
fn variants(&self) -> VariantGenerator {
VariantGenerator {
next_offset: 0,
base_machine: self.clone(),
}
}
}
struct VariantGenerator {
next_offset: usize,
base_machine: Machine,
}
impl Iterator for VariantGenerator {
type Item = Machine;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.next_offset >= self.base_machine.instructions.len() {
return None;
}
match self.base_machine.instructions[self.next_offset] {
Instruction::ACC(_) => self.next_offset += 1,
Instruction::JMP(x) => {
let mut retval = self.base_machine.clone();
retval.instructions[self.next_offset] = Instruction::NOP(x);
self.next_offset += 1;
return Some(retval);
}
Instruction::NOP(x) => {
let mut retval = self.base_machine.clone();
retval.instructions[self.next_offset] = Instruction::JMP(x);
self.next_offset += 1;
return Some(retval);
}
}
}
}
}
fn real_main() -> Result<(), TopLevelError> {
let filename = env::args().skip(1).next().expect("No file argument given.");
let contents = fs::read_to_string(filename)?;
let machine = Machine::from_str(&contents)?;
machine.pretty_print();
// this is part 1
let (terminated, last_accum) = machine.clone().terminates()?;
if terminated {
println!("WARNING: Somehow the initial input terminated.");
}
println!("Last accumulator before looping forever: {}", last_accum);
// this is part 2
for mut variant in machine.variants() {
if let Ok((true, final_value)) = variant.terminates() {
println!("\nFound a variant that halts! Its last value is {}", final_value);
variant.pretty_print();
}
}
Ok(())
}
fn main() {
if let Err(e) = real_main() {
eprintln!("ERROR: {}", e);
}
}

View File

@@ -30,6 +30,16 @@ pub enum TopLevelError {
#[from]
source: BaggageRuleParseError,
},
#[error("Failed to parse instruction: {source}")]
InstructionParseError {
#[from]
source: InstructionParseError,
},
#[error("Error executing machine: {source}")]
MachineExecutionError {
#[from]
source: ExecutionError,
},
}
#[derive(Error, Debug)]
@@ -97,3 +107,24 @@ impl<'a> From<nom::Err<nom::error::Error<&'a str>>> for BaggageRuleParseError {
}
}
}
#[derive(Error,Debug)]
pub enum InstructionParseError {
#[error("Unknown opcode {0}")]
UnknownOpcode(String),
#[error("Couldn't convert number: {source}")]
NumConversionError {
#[from]
source: ParseIntError
},
#[error("Encountered an empty instruction (?)")]
EmptyInstruction,
#[error("Instruction '{0}' missing an operand")]
MissingOperand(String),
}
#[derive(Error,Debug)]
pub enum ExecutionError {
#[error("Tried to execute non-existent instruction at {0}")]
NonExistentLocation(isize),
}