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 = "day16_chronal_classification"
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,88 @@
As you see the Elves defend their hot chocolate successfully, you go back to falling through time. This is going to become a problem.
If you're ever going to return to your own time, you need to understand how this device on your wrist works. You have a little while before you reach your next destination, and with a bit of trial and error, you manage to pull up a programming manual on the device's tiny screen.
According to the manual, the device has four [registers](https://en.wikipedia.org/wiki/Hardware_register) (numbered `0` through `3`) that can be manipulated by [instructions](https://en.wikipedia.org/wiki/Instruction_set_architecture#Instructions) containing one of 16 opcodes. The registers start with the value `0`.
Every instruction consists of four values: an *opcode*, two *inputs* (named `A` and `B`), and an *output* (named `C`), in that order. The opcode specifies the behavior of the instruction and how the inputs are interpreted. The output, `C`, is always treated as a register.
In the opcode descriptions below, if something says "*value `A`*", it means to take the number given as `A` *literally*. (This is also called an "immediate" value.) If something says "*register `A`*", it means to use the number given as `A` to read from (or write to) the *register with that number*. So, if the opcode `addi` adds register `A` and value `B`, storing the result in register `C`, and the instruction `addi 0 7 3` is encountered, it would add `7` to the value contained by register `0` and store the sum in register `3`, never modifying registers `0`, `1`, or `2` in the process.
Many opcodes are similar except for how they interpret their arguments. The opcodes fall into seven general categories:
Addition:
* `addr` (add register) stores into register `C` the result of adding register `A` and register `B`.
* `addi` (add immediate) stores into register `C` the result of adding register `A` and value `B`.
Multiplication:
* `mulr` (multiply register) stores into register `C` the result of multiplying register `A` and register `B`.
* `muli` (multiply immediate) stores into register `C` the result of multiplying register `A` and value `B`.
[Bitwise AND](https://en.wikipedia.org/wiki/Bitwise_AND):
* `banr` (bitwise AND register) stores into register `C` the result of the bitwise AND of register `A` and register `B`.
* `bani` (bitwise AND immediate) stores into register `C` the result of the bitwise AND of register `A` and value `B`.
[Bitwise OR](https://en.wikipedia.org/wiki/Bitwise_OR):
* `borr` (bitwise OR register) stores into register `C` the result of the bitwise OR of register `A` and register `B`.
* `bori` (bitwise OR immediate) stores into register `C` the result of the bitwise OR of register `A` and value `B`.
Assignment:
* `setr` (set register) copies the contents of register `A` into register `C`. (Input `B` is ignored.)
* `seti` (set immediate) stores value `A` into register `C`. (Input `B` is ignored.)
Greater-than testing:
* `gtir` (greater-than immediate/register) sets register `C` to `1` if value `A` is greater than register `B`. Otherwise, register `C` is set to `0`.
* `gtri` (greater-than register/immediate) sets register `C` to `1` if register `A` is greater than value `B`. Otherwise, register `C` is set to `0`.
* `gtrr` (greater-than register/register) sets register `C` to `1` if register `A` is greater than register `B`. Otherwise, register `C` is set to `0`.
Equality testing:
* `eqir` (equal immediate/register) sets register `C` to `1` if value `A` is equal to register `B`. Otherwise, register `C` is set to `0`.
* `eqri` (equal register/immediate) sets register `C` to `1` if register `A` is equal to value `B`. Otherwise, register `C` is set to `0`.
* `eqrr` (equal register/register) sets register `C` to `1` if register `A` is equal to register `B`. Otherwise, register `C` is set to `0`.
Unfortunately, while the manual gives the *name* of each opcode, it doesn't seem to indicate the *number*. However, you can monitor the CPU to see the contents of the registers before and after instructions are executed to try to work them out. Each opcode has a number from `0` through `15`, but the manual doesn't say which is which. For example, suppose you capture the following sample:
```
Before: [3, 2, 1, 1]
9 2 1 2
After: [3, 2, 2, 1]
```
This sample shows the effect of the instruction `9 2 1 2` on the registers. Before the instruction is executed, register `0` has value `3`, register `1` has value `2`, and registers `2` and `3` have value `1`. After the instruction is executed, register `2`'s value becomes `2`.
The instruction itself, `9 2 1 2`, means that opcode `9` was executed with `A=2`, `B=1`, and `C=2`. Opcode `9` could be any of the 16 opcodes listed above, but only three of them behave in a way that would cause the result shown in the sample:
* Opcode `9` could be `mulr`: register `2` (which has a value of `1`) times register `1` (which has a value of `2`) produces `2`, which matches the value stored in the output register, register `2`.
* Opcode `9` could be `addi`: register `2` (which has a value of `1`) plus value `1` produces `2`, which matches the value stored in the output register, register `2`.
* Opcode `9` could be `seti`: value `2` matches the value stored in the output register, register `2`; the number given for `B` is irrelevant.
None of the other opcodes produce the result captured in the sample. Because of this, the sample above *behaves like three opcodes*.
You collect many of these samples (the first section of your puzzle input). The manual also includes a small test program (the second section of your puzzle input) - you can *ignore it for now*.
Ignoring the opcode numbers, *how many samples in your puzzle input behave like three or more opcodes?*
Your puzzle answer was `588`.
\--- Part Two ---
----------
Using the samples you collected, work out the number of each opcode and execute the test program (the second section of your puzzle input).
*What value is contained in register `0` after executing the test program?*
Your puzzle answer was `627`.
Both parts of this puzzle are complete! They provide two gold stars: \*\*
At this point, you should [return to your Advent calendar](/2018) and try another puzzle.
If you still want to see it, you can [get your puzzle input](16/input).

View file

@ -0,0 +1,176 @@
use std::collections::{HashMap, HashSet};
type Register = usize;
type Value = isize;
enum Operation {
AddR(Register, Register, Register),
AddI(Register, Value, Register),
MulR(Register, Register, Register),
MulI(Register, Value, Register),
BAnR(Register, Register, Register),
BAnI(Register, Value, Register),
BOrR(Register, Register, Register),
BOrI(Register, Value, Register),
SetR(Register, Value, Register),
SetI(Value, Value, Register),
GtIR(Value, Register, Register),
GtRI(Register, Value, Register),
GtRR(Register, Register, Register),
EqIR(Value, Register, Register),
EqRI(Register, Value, Register),
EqRR(Register, Register, Register),
}
impl Operation {
fn perform(&self, registers: &mut [Value; 4]) {
match self {
Self::AddR(a, b, c) => registers[*c] = registers[*a] + registers[*b],
Self::AddI(a, b, c) => registers[*c] = registers[*a] + *b,
Self::MulR(a, b, c) => registers[*c] = registers[*a] * registers[*b],
Self::MulI(a, b, c) => registers[*c] = registers[*a] * *b,
Self::BAnR(a, b, c) => registers[*c] = registers[*a] & registers[*b],
Self::BAnI(a, b, c) => registers[*c] = registers[*a] & *b,
Self::BOrR(a, b, c) => registers[*c] = registers[*a] | registers[*b],
Self::BOrI(a, b, c) => registers[*c] = registers[*a] | *b,
Self::SetR(a, _b, c) => registers[*c] = registers[*a],
Self::SetI(a, _b, c) => registers[*c] = *a,
Self::GtIR(a, b, c) => registers[*c] = if *a > registers[*b] { 1 } else { 0 },
Self::GtRI(a, b, c) => registers[*c] = if registers[*a] > *b { 1 } else { 0 },
Self::GtRR(a, b, c) => registers[*c] = if registers[*a] > registers[*b] { 1 } else { 0 },
Self::EqIR(a, b, c) => registers[*c] = if *a == registers[*b] { 1 } else { 0 },
Self::EqRI(a, b, c) => registers[*c] = if registers[*a] == *b { 1 } else { 0 },
Self::EqRR(a, b, c) => registers[*c] = if registers[*a] == registers[*b] { 1 } else { 0 },
}
}
fn try_parse(op_nr: usize, opcode: &[&str]) -> Option<Self> {
assert_eq!(opcode.len(), 3);
let a_reg = opcode[0].parse::<usize>();
let a_val = opcode[0].parse::<isize>();
let b_reg = opcode[1].parse::<usize>();
let b_val = opcode[1].parse::<isize>();
let c_reg = opcode[2].parse::<usize>();
match (op_nr, a_reg, a_val, b_reg, b_val, c_reg) {
(0, Ok(a), _, Ok(b), _, Ok(c)) if a<4 && b<4 && c<4 => Some(Self::AddR(a, b, c)),
(1, Ok(a), _, _, Ok(b), Ok(c)) if a<4 && c<4 => Some(Self::AddI(a, b, c)),
(2, Ok(a), _, Ok(b), _, Ok(c)) if a<4 && b<4 && c<4 => Some(Self::MulR(a, b, c)),
(3, Ok(a), _, _, Ok(b), Ok(c)) if a<4 && c<4 => Some(Self::MulI(a, b, c)),
(4, Ok(a), _, Ok(b), _, Ok(c)) if a<4 && b<4 && c<4 => Some(Self::BAnR(a, b, c)),
(5, Ok(a), _, _, Ok(b), Ok(c)) if a<4 && c<4 => Some(Self::BAnI(a, b, c)),
(6, Ok(a), _, Ok(b), _, Ok(c)) if a<4 && b<4 && c<4 => Some(Self::BOrR(a, b, c)),
(7, Ok(a), _, _, Ok(b), Ok(c)) if a<4 && c<4 => Some(Self::BOrI(a, b, c)),
(8, Ok(a), _, _, _, Ok(c)) if a<4 && c<4 => Some(Self::SetR(a, 0, c)),
(9, _, Ok(a), _, _, Ok(c)) if c<4 => Some(Self::SetI(a, 0, c)),
(10, _, Ok(a), Ok(b), _, Ok(c)) if b<4 && c<4 => Some(Self::GtIR(a, b, c)),
(11, Ok(a), _, _, Ok(b), Ok(c)) if a<4 && c<4 => Some(Self::GtRI(a, b, c)),
(12, Ok(a), _, Ok(b), _, Ok(c)) if a<4 && b<4 && c<4 => Some(Self::GtRR(a, b, c)),
(13, _, Ok(a), Ok(b), _, Ok(c)) if b<4 && c<4 => Some(Self::EqIR(a, b, c)),
(14, Ok(a), _, _, Ok(b), Ok(c)) if a<4 && c<4 => Some(Self::EqRI(a, b, c)),
(15, Ok(a), _, Ok(b), _, Ok(c)) if a<4 && b<4 && c<4 => Some(Self::EqRR(a, b, c)),
_ => None,
}
}
fn try_all(sample: &str) -> (usize, Vec<usize>) {
let mut possibilities = Vec::new();
let lines: Vec<_> = sample.split('\n').collect();
assert_eq!(lines.len(), 3);
let before: Vec<_> = lines[0].split(&[' ', ',', '[', ']']).collect();
assert_eq!(before.len(), 10);
let registers = [
before[2].parse().unwrap(),
before[4].parse().unwrap(),
before[6].parse().unwrap(),
before[8].parse().unwrap(),
];
let opcode: Vec<_> = lines[1].split_whitespace().collect();
assert_eq!(opcode.len(), 4);
let after: Vec<_> = lines[2].split(&[' ', ',', '[', ']']).collect();
assert_eq!(after.len(), 11);
let expected = [
after[3].parse().unwrap(),
after[5].parse().unwrap(),
after[7].parse().unwrap(),
after[9].parse().unwrap(),
];
for op_nr in 0..16 {
if let Some(op) = Self::try_parse(op_nr, &opcode[1..4]) {
let mut actual = registers;
op.perform(&mut actual);
if actual == expected {
possibilities.push(op_nr);
}
}
}
(opcode[0].parse().unwrap(), possibilities)
}
fn from(line: &str, mappings: &HashMap<usize, HashSet<usize>>) -> Self {
let components: Vec<&str> = line.split_whitespace().collect();
assert_eq!(components.len(), 4);
let op_nr = *mappings.get(&components[0].parse()
.unwrap())
.unwrap()
.iter()
.next()
.unwrap();
Self::try_parse(op_nr, &components[1..4]).unwrap()
}
}
pub fn run(input: &str) -> (usize, isize) {
let (samples, program) = input.split_once("\n\n\n\n").unwrap();
let possible_mappings: Vec<_> = samples.split("\n\n").map(Operation::try_all).collect();
// dbg!(&possible_mappings);
let first = possible_mappings.iter().filter(|(_code, ops)| ops.len() > 2).count();
let mut mappings: HashMap<usize, HashSet<usize>> = HashMap::new();
possible_mappings.iter().for_each(|(op_code, possibilities)| {
mappings.entry(*op_code)
.and_modify(|e| *e = e.intersection(&possibilities.iter().cloned().collect()).cloned().collect())
.or_insert(possibilities.iter().cloned().collect());
});
loop {
let known: Vec<_> = mappings.clone().into_iter().filter(|(_op_code, possibilities)| possibilities.len() == 1).collect();
if known.len() == mappings.len() {
break;
}
for (op_code, mapping) in known {
let mapping = mapping.iter().next().unwrap();
mappings.iter_mut().filter(|(code, possibilities)| **code != op_code && possibilities.contains(mapping)).for_each(|(_, possibilities)| { possibilities.remove(mapping); });
}
}
let mut registers = [0; 4];
program.lines().map(|line| Operation::from(line, &mappings)).for_each(|op| op.perform(&mut registers));
let second = registers[0];
(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_sample() {
// let sample_input = read_file("tests/sample_input");
// assert_eq!(run(&sample_input), (0, 0));
// }
#[test]
fn test_challenge() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run(&challenge_input), (588, 0));
}
}

File diff suppressed because it is too large Load diff