diff options
author | cos <cos> | 2020-12-08 10:12:11 +0100 |
---|---|---|
committer | cos <cos> | 2020-12-08 19:10:38 +0100 |
commit | 8df0925373b4ee9677db89ec676522d5b3570bb5 (patch) | |
tree | 00f05f19c45942e8e6ede9cdcff84ee1d0a72952 | |
parent | 3cc461bc73e3fde58df6952f4db9c67a89b65858 (diff) | |
download | adventofcode-8df0925373b4ee9677db89ec676522d5b3570bb5.zip |
Add day08, 2020
-rw-r--r-- | 2020/rust/Cargo.toml | 2 | ||||
-rw-r--r-- | 2020/rust/day08/Cargo.toml | 9 | ||||
-rw-r--r-- | 2020/rust/day08/src/intcode/mod.rs | 134 | ||||
-rw-r--r-- | 2020/rust/day08/src/lib.rs | 1 | ||||
-rw-r--r-- | 2020/rust/day08/src/main.rs | 97 |
5 files changed, 242 insertions, 1 deletions
diff --git a/2020/rust/Cargo.toml b/2020/rust/Cargo.toml index 3949f7e..6ace855 100644 --- a/2020/rust/Cargo.toml +++ b/2020/rust/Cargo.toml @@ -8,7 +8,7 @@ members = [ "day05", "day06", "day07", -# "day08", + "day08", # "day09", # "day10", # "day11", diff --git a/2020/rust/day08/Cargo.toml b/2020/rust/day08/Cargo.toml new file mode 100644 index 0000000..bb718df --- /dev/null +++ b/2020/rust/day08/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "day08" +version = "0.1.0" +authors = ["cos <cos>"] +edition = "2018" + +[dependencies] +aoc = { path = "../aoc" } +anyhow = "1.0" diff --git a/2020/rust/day08/src/intcode/mod.rs b/2020/rust/day08/src/intcode/mod.rs new file mode 100644 index 0000000..5ec50e3 --- /dev/null +++ b/2020/rust/day08/src/intcode/mod.rs @@ -0,0 +1,134 @@ +use std::convert::TryFrom; +use std::error::Error; +use std::fmt; + +const DEBUG: bool = false; + +#[derive(Clone,Debug)] +pub enum Instruction { + Acc(Wordsize), + Jmp(Wordsize), + Nop(Wordsize), +} + +#[derive(Debug)] +pub enum InstructionError { + InvalidOperation, + InvalidArgument, + Parse(std::num::ParseIntError), +} + +impl fmt::Display for InstructionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "InstructionError") + } +} + +impl Error for InstructionError { } + +impl TryFrom<String> for Instruction { + type Error = InstructionError; + + fn try_from(string: String) -> Result<Self, Self::Error> { + Instruction::try_from(string.as_str()) + } +} + +impl TryFrom<&str> for Instruction { + type Error = InstructionError; + + fn try_from(string: &str) -> Result<Self, Self::Error> { + let mut iter = string.split(" "); + let (op, arg) = (iter.next().ok_or(InstructionError::InvalidOperation)?, + iter.next().ok_or(InstructionError::InvalidArgument)?); + let num = arg.parse().map_err(InstructionError::Parse)?; + match op { + "nop" => Ok(Instruction::Nop(num)), + "jmp" => Ok(Instruction::Jmp(num)), + "acc" => Ok(Instruction::Acc(num)), + _ => Err(InstructionError::InvalidOperation), + } + } +} + +pub type Wordsize = i128; + +#[derive(Clone,Debug)] +pub struct Registers { + pub ip: usize, + pub accumulator: Wordsize, +} + +pub struct Intcode { + program: Vec<Instruction>, + registers: Registers, +} + +impl Intcode { + pub fn new() -> Intcode { + Intcode { + program: vec![], + registers: Registers { + accumulator: 0, + ip: 0, + } + } + } + + fn op_acc(&mut self, arg: &Wordsize) { + self.registers.accumulator += arg; + } + + fn op_jmp(&mut self, arg: &Wordsize) { + if *arg >= 0 { + self.registers.ip += *arg as usize; + } else { + self.registers.ip -= (*arg * -1) as usize; + } + } + + pub fn load(&mut self, program: &[Instruction]) { + if DEBUG { + eprintln!("{:?}", program); + } + self.program = program.into(); + } + + pub fn regdump(&self) -> Registers { + self.registers.clone() + } + + pub fn step(&mut self) -> Result<(), IntcodeError>{ + let op = match self.program.get(self.registers.ip) { + Some(v) => v.clone(), + None => return Err(IntcodeError::MemoryAccessViolation), + }; + if DEBUG { + eprintln!("ip: {}, op: {:?}", self.registers.ip, op); + } + match op { + Instruction::Acc(arg) => self.op_acc(&arg), + Instruction::Jmp(arg) => self.op_jmp(&arg), + Instruction::Nop(_) => {}, + } + match op { + Instruction::Jmp(_) => {}, + _ => self.registers.ip += 1, + } + + Ok(()) + } +} + +#[derive(Debug)] +pub enum IntcodeError { + MemoryAccessViolation, +} + +impl fmt::Display for IntcodeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "IntcodeError") + } +} + +impl Error for IntcodeError { } diff --git a/2020/rust/day08/src/lib.rs b/2020/rust/day08/src/lib.rs new file mode 100644 index 0000000..b6b007e --- /dev/null +++ b/2020/rust/day08/src/lib.rs @@ -0,0 +1 @@ +pub mod intcode; diff --git a/2020/rust/day08/src/main.rs b/2020/rust/day08/src/main.rs new file mode 100644 index 0000000..884ba54 --- /dev/null +++ b/2020/rust/day08/src/main.rs @@ -0,0 +1,97 @@ +use anyhow::Result; +use std::convert::TryFrom; +use std::env::args; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::path::Path; + +use day08::intcode::{Instruction, Intcode}; +use day08::intcode; + +fn read_input<T: AsRef<Path>>(filename: T) -> Result<Vec<Instruction>> { + let f = File::open(filename)?; + let reader = BufReader::new(f); + + reader.lines() + .map(|v| Ok(Instruction::try_from(v?)?)) + .collect() +} + +fn part1(program: &[Instruction]) -> Result<intcode::Wordsize> { + let mut visited = vec![]; + let mut intcode = Intcode::new(); + intcode.load(program); + + loop { + let registers = intcode.regdump(); + if visited.contains(®isters.ip) { + return Ok(registers.accumulator); + } + visited.push(registers.ip); + intcode.step()? + } +} + +fn part2(program: &[Instruction]) -> Option<intcode::Wordsize> { + 'reset: for i in 0..program.len() { + let mut modified = program.to_vec(); + match modified.get_mut(i) { + Some(instruction) => match instruction { + Instruction::Jmp(arg) => *instruction = Instruction::Nop(*arg), + Instruction::Nop(arg) => *instruction = Instruction::Jmp(*arg), + _ => {}, + }, + None => panic!("Invalid loop index"), + } + + let mut visited = vec![]; + let mut intcode = Intcode::new(); + intcode.load(&modified); + + loop { + let registers = intcode.regdump(); + if visited.contains(®isters.ip) { + continue 'reset; + } + visited.push(registers.ip); + match intcode.step() { + Ok(_) => {}, + Err(_) => { + // Weird as it might seem, Error is Success in this aoc case. + let registers = intcode.regdump(); + return Some(registers.accumulator); + } + } + } + } + None +} + +fn main() { + let (do_part_1, do_part_2) = aoc::do_parts(); + + let filename = match args().nth(1) { + Some(f) => f, + None => { + eprintln!("Missing input filename"); + std::process::exit(1); + }, + }; + match read_input(filename) { + Ok(input) => { + if do_part_1 { + match part1(&input) { + Ok(solution) => println!("Part1, {}", solution), + Err(err) => eprintln!("Part1, {}", err), + } + } + if do_part_2 { + match part2(&input) { + Some(solution) => println!("Part2, {}", solution), + None => eprintln!("Part2, no solution found"), + } + } + }, + Err(err) => eprintln!("Could not read input: {}", err), + } +} |