advent-of-code/aoc2020/src/day08.rs

158 lines
3.6 KiB
Rust
Raw Normal View History

2020-12-08 16:03:33 +01:00
use std::collections::HashSet;
use std::fmt::Write;
2020-12-14 18:08:16 +01:00
use anyhow::{anyhow, bail, Context, Result};
2020-12-08 16:03:33 +01:00
const INPUT: &str = include_str!("../input/day08.txt");
2020-12-14 18:08:16 +01:00
pub fn run() -> Result<String> {
2020-12-08 16:03:33 +01:00
let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?;
2020-12-08 16:50:43 +01:00
writeln!(res, "part 2: {}", part2(INPUT)?)?;
2020-12-08 16:03:33 +01:00
Ok(res)
}
2020-12-14 18:08:16 +01:00
fn part1(input: &str) -> Result<i64> {
2020-12-08 16:03:33 +01:00
let instructions = input
.lines()
.map(str::parse)
2020-12-14 18:08:16 +01:00
.collect::<Result<Vec<Instruction>>>()?;
2020-12-08 16:03:33 +01:00
let mut interpreter = Interpreter::new(instructions);
2020-12-08 16:39:31 +01:00
Ok(match interpreter.run() {
ExitStatus::InfiniteLoop(value) => value,
2020-12-14 18:08:16 +01:00
ExitStatus::End(_) => bail!("interpreter doesn't have an infinite loop"),
2020-12-08 16:39:31 +01:00
})
2020-12-08 16:03:33 +01:00
}
2020-12-14 18:08:16 +01:00
fn part2(input: &str) -> Result<i64> {
2020-12-08 16:50:43 +01:00
let instructions = input
.lines()
.map(str::parse)
2020-12-14 18:08:16 +01:00
.collect::<Result<Vec<Instruction>>>()?;
2020-12-08 16:50:43 +01:00
for idx in 0..instructions.len() {
let mut instructions = instructions.clone();
match instructions[idx] {
Instruction::Acc(_) => continue,
Instruction::Jmp(offset) => instructions[idx] = Instruction::Nop(offset),
Instruction::Nop(offset) => instructions[idx] = Instruction::Jmp(offset),
};
let mut interpreter = Interpreter::new(instructions);
match interpreter.run() {
ExitStatus::InfiniteLoop(_) => continue,
ExitStatus::End(value) => return Ok(value),
}
}
2020-12-14 18:08:16 +01:00
Err(anyhow!(
2020-12-08 16:50:43 +01:00
"interpreter always had an infinite loop, no solution found"
))
}
2020-12-08 16:03:33 +01:00
struct Interpreter {
idx: usize,
accumulator: i64,
memory: Vec<Instruction>,
}
2020-12-08 16:39:31 +01:00
enum ExitStatus {
InfiniteLoop(i64),
End(i64),
}
2020-12-08 16:03:33 +01:00
impl Interpreter {
fn new(instructions: Vec<Instruction>) -> Self {
Self {
idx: 0,
accumulator: 0,
memory: instructions,
}
}
fn step(&mut self) {
match self.memory[self.idx] {
Instruction::Acc(arg) => {
self.accumulator += arg;
self.idx += 1;
}
Instruction::Jmp(offset) => self.idx = self.idx.wrapping_add(offset as usize),
2020-12-08 16:50:43 +01:00
Instruction::Nop(_) => self.idx += 1,
2020-12-08 16:03:33 +01:00
}
}
2020-12-08 16:39:31 +01:00
fn run(&mut self) -> ExitStatus {
let mut set = HashSet::new();
while self.idx < self.memory.len() {
if !set.insert(self.idx) {
return ExitStatus::InfiniteLoop(self.accumulator);
}
self.step();
}
2020-12-08 16:50:43 +01:00
ExitStatus::End(self.accumulator)
2020-12-08 16:39:31 +01:00
}
2020-12-08 16:03:33 +01:00
}
2020-12-08 16:50:43 +01:00
#[derive(Clone)]
2020-12-08 16:03:33 +01:00
enum Instruction {
Acc(i64),
Jmp(i64),
2020-12-08 16:50:43 +01:00
Nop(i64),
2020-12-08 16:03:33 +01:00
}
impl std::str::FromStr for Instruction {
2020-12-14 18:08:16 +01:00
type Err = anyhow::Error;
2020-12-08 16:03:33 +01:00
2020-12-14 18:08:16 +01:00
fn from_str(s: &str) -> Result<Self> {
let space = s.find(' ').context("couldn't split on space")?;
2020-12-08 16:03:33 +01:00
let inst = &s[..space];
let arg = s[(space + 1)..]
.parse()
2020-12-14 18:08:16 +01:00
.context("couldn't parse argument for instruction")?;
2020-12-08 16:03:33 +01:00
Ok(match inst {
"acc" => Self::Acc(arg),
"jmp" => Self::Jmp(arg),
2020-12-08 16:50:43 +01:00
"nop" => Self::Nop(arg),
2020-12-14 18:08:16 +01:00
_ => bail!("unrecognized instruction `{}`", inst),
2020-12-08 16:03:33 +01:00
})
}
}
#[cfg(test)]
mod tests {
use super::*;
const PROVIDED: &str = include_str!("../input/day08_provided.txt");
#[test]
fn part1_provided() {
assert_eq!(part1(PROVIDED).unwrap(), 5);
}
#[test]
fn part1_real() {
assert_eq!(part1(INPUT).unwrap(), 1675);
}
2020-12-08 16:50:43 +01:00
#[test]
fn part2_provided() {
assert_eq!(part2(PROVIDED).unwrap(), 8);
}
#[test]
fn part2_real() {
assert_eq!(part2(INPUT).unwrap(), 1532);
}
2020-12-08 16:03:33 +01:00
}