Solutions for 2022, as well as 2015-2018 and 2019 up to day 11

This commit is contained in:
Chris Alge 2023-03-12 15:20:02 +01:00
commit 1895197c49
722 changed files with 375457 additions and 0 deletions

View file

@ -0,0 +1,8 @@
[package]
name = "day23-coprocessor_conflagration"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,40 @@
\--- Day 23: Coprocessor Conflagration ---
----------
You decide to head directly to the CPU and fix the printer from there. As you get close, you find an *experimental coprocessor* doing so much work that the local programs are afraid it will [halt and catch fire](https://en.wikipedia.org/wiki/Halt_and_Catch_Fire). This would cause serious issues for the rest of the computer, so you head in and see what you can do.
The code it's running seems to be a variant of the kind you saw recently on that [tablet](18). The general functionality seems *very similar*, but some of the instructions are different:
* `set X Y` *sets* register `X` to the value of `Y`.
* `sub X Y` *decreases* register `X` by the value of `Y`.
* `mul X Y` sets register `X` to the result of *multiplying* the value contained in register `X` by the value of `Y`.
* `jnz X Y` *jumps* with an offset of the value of `Y`, but only if the value of `X` is *not zero*. (An offset of `2` skips the next instruction, an offset of `-1` jumps to the previous instruction, and so on.)
Only the instructions listed above are used. The eight registers here, named `a` through `h`, all start at `0`.
The coprocessor is currently set to some kind of *debug mode*, which allows for testing, but prevents it from doing any meaningful work.
If you run the program (your puzzle input), *how many times is the `mul` instruction invoked?*
Your puzzle answer was `9409`.
\--- Part Two ---
----------
Now, it's time to fix the problem.
The *debug mode switch* is wired directly to register `a`. You flip the switch, which makes *register `a` now start at `1`* when the program is executed.
Immediately, the coprocessor begins to overheat. Whoever wrote this program obviously didn't choose a very efficient implementation. You'll need to *optimize the program* if it has any hope of completing before Santa needs that printer working.
The coprocessor's ultimate goal is to determine the final value left in register `h` once the program completes. Technically, if it had that... it wouldn't even need to run the program.
After setting register `a` to `1`, if the program were to run to completion, *what value would be left in register `h`?*
Your puzzle answer was `913`.
Both parts of this puzzle are complete! They provide two gold stars: \*\*
At this point, all that is left is for you to [admire your Advent calendar](/2017).
If you still want to see it, you can [get your puzzle input](23/input).

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,188 @@
use std::str::FromStr;
// use std::thread;
// use std::sync::mpsc::{channel, Receiver, Sender};
type RegIdx = usize;
type RegVal = isize;
#[derive(Debug)]
struct ParseError;
#[derive(Debug, Clone)]
enum Operand {
Reg(RegIdx),
Val(RegVal),
}
impl FromStr for Operand {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(val) = s.parse::<RegVal>() {
Ok(Self::Val(val))
} else {
match s.as_bytes()[0] {
c @ b'a'..=b'z' => Ok(Self::Reg((c - b'a') as RegIdx)),
_ => Err(ParseError),
}
}
}
}
#[derive(Debug, Clone)]
enum Instruction {
// Snd(Operand),
Set(RegIdx, Operand),
// Add(RegIdx, Operand),
Sub(RegIdx, Operand),
Mul(RegIdx, Operand),
// Mod(RegIdx, Operand),
// Rcv(RegIdx),
Jnz(Operand, Operand),
}
impl FromStr for Instruction {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let components: Vec<_> = s.split_whitespace().collect();
match components[0] {
"set" => Ok(Self::Set((components[1].as_bytes()[0] - b'a') as RegIdx, components[2].parse()?)),
"sub" => Ok(Self::Sub((components[1].as_bytes()[0] - b'a') as RegIdx, components[2].parse()?)),
"mul" => Ok(Self::Mul((components[1].as_bytes()[0] - b'a') as RegIdx, components[2].parse()?)),
"jnz" => Ok(Self::Jnz(components[1].parse()?, components[2].parse()?)),
_ => panic!("Instruction not recognized: {s}"),
}
}
}
impl From<&str> for Instruction {
fn from(value: &str) -> Self {
value.parse().expect("Unable to parse {value} into an instruction")
}
}
impl Instruction {
fn perform(&self, registers: &mut [RegVal], out: &mut RegVal, next_instr_ptr: &mut RegIdx) {
*next_instr_ptr += 1;
match self {
Self::Set(to, Operand::Reg(from)) => registers[*to] = registers[*from],
Self::Set(to, Operand::Val(val)) => registers[*to] = *val,
Self::Sub(to, Operand::Reg(from)) => registers[*to] -= registers[*from],
Self::Sub(to, Operand::Val(val)) => registers[*to] -= *val,
Self::Mul(to, Operand::Reg(from)) => {
registers[*to] *= registers[*from];
*out += 1;
}
Self::Mul(to, Operand::Val(val)) => {
registers[*to] *= *val;
*out += 1;
}
Self::Jnz(Operand::Reg(reg_idx), Operand::Val(offset)) => self.jump_not_zero(registers[*reg_idx], *offset, next_instr_ptr),
Self::Jnz(Operand::Val(val), Operand::Val(offset)) => self.jump_not_zero(*val, *offset, next_instr_ptr),
Self::Jnz(Operand::Reg(reg_idx), Operand::Reg(offset_reg)) => self.jump_not_zero(registers[*reg_idx], registers[*offset_reg], next_instr_ptr),
Self::Jnz(Operand::Val(val), Operand::Reg(offset_reg)) => self.jump_not_zero(*val, registers[*offset_reg], next_instr_ptr),
}
}
fn jump_not_zero(&self, compared: RegVal, offset: isize, next_instr_ptr: &mut RegIdx) {
if compared != 0 {
*next_instr_ptr = (*next_instr_ptr as isize + offset - 1) as usize;
}
}
}
#[derive(Clone, Default)]
struct Cpu {
instructions: Vec<Instruction>,
registers: [RegVal; 26],
next_instr_ptr: usize,
out: RegVal,
}
impl Cpu {
/// This only works for this particular AoC challenge input. The only allowed modifications are the numbers range set in instructions (0..=7)!
fn run_optimized(&mut self) {
while self.next_instr_ptr <= 8 {
let next_instruction = &self.instructions[self.next_instr_ptr];
next_instruction.perform(&mut self.registers, &mut self.out, &mut self.next_instr_ptr);
}
self.registers[7] = (self.registers[1]..=self.registers[2]).step_by(17).filter(|i| !Self::is_prime(*i)).count() as isize;
}
fn is_prime(n: isize) -> bool {
!(2..n).any(|a| n % a == 0)
}
fn run(&mut self) -> RegVal {
loop {
if self.next_instr_ptr >= self.instructions.len() {
return self.out;
}
match &self.instructions[self.next_instr_ptr] {
Instruction::Jnz(Operand::Reg(g), Operand::Val(-8)) => {
if self.registers[*g] == 0 {
self.next_instr_ptr += 1;
} else {
match ( &self.instructions[self.next_instr_ptr - 8], &self.instructions[self.next_instr_ptr - 7], &self.instructions[self.next_instr_ptr - 6], &self.instructions[self.next_instr_ptr - 5], &self.instructions[self.next_instr_ptr - 4], &self.instructions[self.next_instr_ptr - 3], &self.instructions[self.next_instr_ptr - 2], &self.instructions[self.next_instr_ptr - 1] ) {
( Instruction::Set(g1, Operand::Reg(d)),
Instruction::Mul(g2, Operand::Reg(e)),
Instruction::Sub(g3, Operand::Reg(b)),
Instruction::Jnz(Operand::Reg(g4), Operand::Val(2)),
Instruction::Set(f, Operand::Val(0)),
Instruction::Sub(e1, Operand::Val(-1)),
Instruction::Set(g5, Operand::Reg(e2)),
Instruction::Sub(g6, Operand::Reg(b1))
) if g == g1 && g == g2 && g == g3 && g == g4 && g == g5 && g == g6 && b == b1 && e == e1 && e == e2 => {
let new_e = self.registers[*b];
if new_e % self.registers[*d] == 0 {
self.registers[*f] = 0;
}
self.out += new_e - self.registers[*e];
self.registers[*e] = new_e;
self.registers[*g] = 0;
self.next_instr_ptr += 1;
}
_ => self.next_instr_ptr -= 8,
}
}
},
next_instruction => next_instruction.perform(&mut self.registers, &mut self.out, &mut self.next_instr_ptr),
}
}
}
}
pub fn run(input: &str) -> (isize, isize) {
let mut cpu = Cpu {
instructions: input.lines().map(Instruction::from).collect(),
..Default::default()
};
let mut cpu_1 = cpu.clone();
cpu_1.registers[0] = 1;
cpu.run();
let first = cpu.out;
// cpu_1.run();
cpu_1.run_optimized();
let second = cpu_1.registers[7];
(first, second)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
#[test]
fn test_challenge() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run(&challenge_input), (9409, 913));
}
}

View file

@ -0,0 +1,32 @@
set b 99
set c b
jnz a 2
jnz 1 5
mul b 100
sub b -100000
set c b
sub c -17000
set f 1
set d 2
set e 2
set g d
mul g e
sub g b
jnz g 2
set f 0
sub e -1
set g e
sub g b
jnz g -8
sub d -1
set g d
sub g b
jnz g -13
jnz f 2
sub h -1
set g b
sub g c
jnz g 2
jnz 1 3
sub b -17
jnz 1 -23