summaryrefslogtreecommitdiff
path: root/2020
diff options
context:
space:
mode:
Diffstat (limited to '2020')
-rw-r--r--2020/rust/Cargo.toml2
-rw-r--r--2020/rust/day08/Cargo.toml9
-rw-r--r--2020/rust/day08/src/intcode/mod.rs134
-rw-r--r--2020/rust/day08/src/lib.rs1
-rw-r--r--2020/rust/day08/src/main.rs97
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(&registers.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(&registers.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),
+ }
+}