Solutions for 2022, as well as 2015-2018 and 2019 up to day 11
This commit is contained in:
commit
1895197c49
722 changed files with 375457 additions and 0 deletions
8
2017/day23-coprocessor_conflagration/Cargo.toml
Normal file
8
2017/day23-coprocessor_conflagration/Cargo.toml
Normal 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]
|
40
2017/day23-coprocessor_conflagration/challenge.txt
Normal file
40
2017/day23-coprocessor_conflagration/challenge.txt
Normal 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).
|
267750
2017/day23-coprocessor_conflagration/err.log
Normal file
267750
2017/day23-coprocessor_conflagration/err.log
Normal file
File diff suppressed because it is too large
Load diff
188
2017/day23-coprocessor_conflagration/src/lib.rs
Normal file
188
2017/day23-coprocessor_conflagration/src/lib.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
|
32
2017/day23-coprocessor_conflagration/tests/challenge_input
Normal file
32
2017/day23-coprocessor_conflagration/tests/challenge_input
Normal 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
|
0
2017/day23-coprocessor_conflagration/tests/sample_input
Normal file
0
2017/day23-coprocessor_conflagration/tests/sample_input
Normal file
Loading…
Add table
Add a link
Reference in a new issue