Day 17
This commit is contained in:
parent
9c18eb6812
commit
d3520308c2
5 changed files with 305 additions and 3 deletions
83
Cargo.lock
generated
83
Cargo.lock
generated
|
@ -1,7 +1,88 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aoc2024"
|
name = "aoc2024"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"itertools",
|
||||||
|
"num-derive",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-derive"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.90"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||||
|
|
15
Cargo.toml
15
Cargo.toml
|
@ -3,6 +3,11 @@ name = "aoc2024"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
itertools = "0.13.0"
|
||||||
|
num-derive = "0.4.2"
|
||||||
|
num-traits = "0.2.19"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "d11p2"
|
name = "d11p2"
|
||||||
path = "day11/part2.rs"
|
path = "day11/part2.rs"
|
||||||
|
@ -29,4 +34,12 @@ path = "day15/part1.rs"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "d15p2"
|
name = "d15p2"
|
||||||
path = "day15/part2.rs"
|
path = "day15/part2.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "d17p1"
|
||||||
|
path = "day17/part1.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "d17p2"
|
||||||
|
path = "day17/part2.rs"
|
||||||
|
|
|
@ -230,7 +230,7 @@ if (import.meta.main) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (inputResponse.status === 200) {
|
if (inputResponse.status === 200) {
|
||||||
console.log(await inputResponse.text())
|
Deno.stdout.write(await inputResponse.bytes())
|
||||||
} else {
|
} else {
|
||||||
console.log(`Error fetching input for day ${day}:\n\n${await inputResponse.text()}`)
|
console.log(`Error fetching input for day ${day}:\n\n${await inputResponse.text()}`)
|
||||||
}
|
}
|
||||||
|
|
150
day17/part1.rs
Normal file
150
day17/part1.rs
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
use std::collections::LinkedList;
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
use num_derive::FromPrimitive;
|
||||||
|
use num_traits::FromPrimitive;
|
||||||
|
|
||||||
|
struct Register {
|
||||||
|
a: u64,
|
||||||
|
b: u64,
|
||||||
|
c: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Instruction {
|
||||||
|
OpCode(OpCode),
|
||||||
|
Operand(Operand)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(FromPrimitive, Clone, Copy)]
|
||||||
|
enum OpCode {
|
||||||
|
/// Performs division
|
||||||
|
/// A register <- (A register)/(2^(combo operand))
|
||||||
|
/// !!round towards 0!!
|
||||||
|
Adv = 0,
|
||||||
|
/// Calculates bitwise XOR
|
||||||
|
/// B register <- (B register) ^ (literal operand)
|
||||||
|
Bxl = 1,
|
||||||
|
/// Calculates modulo 8 (thereby keeping only its lowest 3 bits)
|
||||||
|
/// (combo operand) % 8
|
||||||
|
Bst = 2,
|
||||||
|
/// Conditional jump
|
||||||
|
/// If (A register) == 0: do nothing
|
||||||
|
/// Else: Jump to literal operand
|
||||||
|
/// !!if this instruction jumps, the instruction pointer is not increased by 2 after this instruction.!!
|
||||||
|
Jnz = 3,
|
||||||
|
/// Calculates bitwise XOR
|
||||||
|
/// B register <- (B register) ^ (C register)
|
||||||
|
/// !!reads the operand, but ignores it!!
|
||||||
|
Bxc = 4,
|
||||||
|
/// Outputs data mod 8
|
||||||
|
/// print ((combo operand) % 8)
|
||||||
|
/// !!multiple outputs are separated by commas!!
|
||||||
|
Out = 5,
|
||||||
|
/// Performs division
|
||||||
|
/// B register <- (A register) / (2^(combo operand))
|
||||||
|
/// !!round towards 0!!
|
||||||
|
Bdv = 6,
|
||||||
|
/// Performs division
|
||||||
|
/// C register <- (A register) / (2^(combo operand))
|
||||||
|
/// !!round towards 0!!
|
||||||
|
Cdv = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
enum Operand {
|
||||||
|
Literal(u8),
|
||||||
|
RegisterA = 4,
|
||||||
|
RegisterB = 5,
|
||||||
|
RegisterC = 6,
|
||||||
|
Reserved = 7
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Operand {
|
||||||
|
fn from_opcode(opcode: OpCode, num: u8) -> Self {
|
||||||
|
match opcode {
|
||||||
|
OpCode::Bxl | OpCode::Jnz | OpCode::Bxc => Operand::Literal(num),
|
||||||
|
OpCode::Adv | OpCode::Bst | OpCode::Out | OpCode::Bdv | OpCode::Cdv => match num {
|
||||||
|
0..=3 => Operand::Literal(num),
|
||||||
|
4 => Operand::RegisterA,
|
||||||
|
5 => Operand::RegisterB,
|
||||||
|
6 => Operand::RegisterC,
|
||||||
|
7 | _ => unreachable!(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_value(&self, register: &mut Register) -> u64 {
|
||||||
|
match self {
|
||||||
|
Operand::Literal(n) => (*n).into(),
|
||||||
|
Operand::RegisterA => register.a,
|
||||||
|
Operand::RegisterB => register.b,
|
||||||
|
Operand::RegisterC => register.c,
|
||||||
|
Operand::Reserved => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const INPUT: &str = include_str!("input.txt");
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Input parsing
|
||||||
|
let (registers, instructions) = INPUT.split_once("\n\n").unwrap();
|
||||||
|
let mut register = registers
|
||||||
|
.split("\n")
|
||||||
|
.map(|l| l[12..].parse::<u64>().unwrap())
|
||||||
|
.enumerate()
|
||||||
|
.fold(Register { a: 0, b: 0, c: 0 }, |mut acc, (i, v)| {
|
||||||
|
match i {
|
||||||
|
0 => acc.a = v,
|
||||||
|
1 => acc.b = v,
|
||||||
|
2 => acc.c = v,
|
||||||
|
_ => unreachable!()
|
||||||
|
};
|
||||||
|
acc
|
||||||
|
});
|
||||||
|
let instructions = instructions[9..]
|
||||||
|
.trim_ascii_end()
|
||||||
|
.split(",")
|
||||||
|
.chunks(2)
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|mut chunk| {
|
||||||
|
let opcode = OpCode::from_u8(chunk.next().unwrap().parse::<u8>().unwrap()).unwrap();
|
||||||
|
[
|
||||||
|
Instruction::OpCode(opcode),
|
||||||
|
Instruction::Operand(Operand::from_opcode(opcode, chunk.next().unwrap().parse::<u8>().unwrap()))
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.collect_vec();
|
||||||
|
let mut instruction_pointer = 0;
|
||||||
|
|
||||||
|
// Actual logic
|
||||||
|
loop {
|
||||||
|
let Some(Instruction::OpCode(opcode)) = instructions.get(instruction_pointer) else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
let Some(Instruction::Operand(operand)) = instructions.get(instruction_pointer + 1) else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
let operand = operand.get_value(&mut register);
|
||||||
|
|
||||||
|
match opcode {
|
||||||
|
OpCode::Adv => register.a = register.a / 2u64.pow(operand.try_into().unwrap()),
|
||||||
|
OpCode::Bxl => register.b = register.b ^ operand,
|
||||||
|
OpCode::Bst => register.b = operand % 8,
|
||||||
|
OpCode::Jnz => if register.a != 0 {
|
||||||
|
instruction_pointer = operand.try_into().unwrap();
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
OpCode::Bxc => register.b = register.b ^ register.c,
|
||||||
|
OpCode::Out => print!("{},", register.b % 8),
|
||||||
|
OpCode::Bdv => register.b = register.a / 2u64.pow(operand.try_into().unwrap()),
|
||||||
|
OpCode::Cdv => register.c = register.a / 2u64.pow(operand.try_into().unwrap()),
|
||||||
|
}
|
||||||
|
|
||||||
|
instruction_pointer += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erase last comma
|
||||||
|
print!("\x08 \n");
|
||||||
|
}
|
58
day17/part2.rs
Normal file
58
day17/part2.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#![feature(vec_push_within_capacity)]
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
const INPUT: &str = include_str!("input.txt");
|
||||||
|
|
||||||
|
// I manually transpiled my program input to rust code, and then removed everything after the first print
|
||||||
|
// I hope this doesn't count as including my input, please have mercy o lord Eric Wastl
|
||||||
|
fn calculate(value: u64) -> u64 {
|
||||||
|
let mut register = (value, 0, 0);
|
||||||
|
|
||||||
|
register.1 = register.0 & 0b111; // Take the lowest 3 bits
|
||||||
|
register.1 ^= 0b010; // XOR Depends on lowest 3 bits
|
||||||
|
register.2 = register.0 / 2u64.pow(register.1.try_into().unwrap()); // Remove some bits of A depending on its last 3
|
||||||
|
register.1 ^= 0b011; // XOR Depends on lowest 3 bits
|
||||||
|
register.1 ^= register.2; // XOR Depends on lowest 3 and full
|
||||||
|
return register.1 & 0b111;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Input parsing
|
||||||
|
let (_, instructions) = INPUT.split_once("\n\n").unwrap();
|
||||||
|
let instructions = instructions[9..]
|
||||||
|
.trim_ascii_end()
|
||||||
|
.split(",")
|
||||||
|
.into_iter()
|
||||||
|
.map(|num| {
|
||||||
|
num.parse::<u64>().unwrap()
|
||||||
|
})
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
// Since the program operates in chunks of 3
|
||||||
|
// And since each chunk relies on the chunks above it
|
||||||
|
// If we start from the last digit then we can get that right, and work from there
|
||||||
|
|
||||||
|
// We have to use a queue/stack, because multiple inputs can get a correct digit, so we have to support branching
|
||||||
|
let mut branches = VecDeque::<(u64, usize)>::with_capacity(instructions.len() * 3);
|
||||||
|
branches.push_back((0b000, 0));
|
||||||
|
let mut results = Vec::<u64>::new();
|
||||||
|
loop {
|
||||||
|
let Some((next, digits_calculated)) = branches.pop_back() else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
for i in 0b000..=0b111u64 {
|
||||||
|
if calculate(next | i) == instructions[15 - digits_calculated] {
|
||||||
|
if digits_calculated == 15 {
|
||||||
|
results.push(next | i);
|
||||||
|
} else {
|
||||||
|
branches.push_back(((next | i) << 3, digits_calculated + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Result: {}", results.iter().min().unwrap());
|
||||||
|
}
|
Loading…
Reference in a new issue