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
2018/day16_chronal_classification/Cargo.toml
Normal file
8
2018/day16_chronal_classification/Cargo.toml
Normal 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]
|
88
2018/day16_chronal_classification/challenge.txt
Normal file
88
2018/day16_chronal_classification/challenge.txt
Normal 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).
|
176
2018/day16_chronal_classification/src/lib.rs
Normal file
176
2018/day16_chronal_classification/src/lib.rs
Normal 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));
|
||||
}
|
||||
}
|
4004
2018/day16_chronal_classification/tests/challenge_input
Normal file
4004
2018/day16_chronal_classification/tests/challenge_input
Normal file
File diff suppressed because it is too large
Load diff
0
2018/day16_chronal_classification/tests/sample_input
Normal file
0
2018/day16_chronal_classification/tests/sample_input
Normal file
Loading…
Add table
Add a link
Reference in a new issue