mirror of
https://github.com/Smaug123/advent-of-code-2021
synced 2025-10-06 04:28:41 +00:00
Day 16, in Mathematica and Rust (#14)
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -206,6 +206,13 @@ dependencies = [
|
||||
"criterion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day_16"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"criterion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day_2"
|
||||
version = "0.1.0"
|
||||
|
@@ -13,4 +13,5 @@ members = [
|
||||
"day_11",
|
||||
"day_12",
|
||||
"day_13",
|
||||
"day_16",
|
||||
]
|
||||
|
15
day_16/Cargo.toml
Normal file
15
day_16/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "day_16"
|
||||
version = "0.1.0"
|
||||
authors = ["Smaug123 <patrick+github@patrickstevens.co.uk>"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
|
||||
[[bench]]
|
||||
name = "bench"
|
||||
harness = false
|
19
day_16/benches/bench.rs
Normal file
19
day_16/benches/bench.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use day_16::day_16::{input, part_1, part_2};
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
let input = input();
|
||||
c.bench_function("day 16 part 1", |b| {
|
||||
b.iter(|| {
|
||||
black_box(part_1(&input));
|
||||
})
|
||||
});
|
||||
c.bench_function("day 16 part 2", |b| {
|
||||
b.iter(|| {
|
||||
black_box(part_2(&input));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
34
day_16/day16.m
Normal file
34
day_16/day16.m
Normal file
@@ -0,0 +1,34 @@
|
||||
(* ::Package:: *)
|
||||
|
||||
nums=With[{chars=Characters@StringTrim@ReadString[StringJoin[NotebookDirectory[], "/input.txt"]]},Flatten[IntegerDigits[FromDigits[#,16],2,4]&/@chars]];
|
||||
|
||||
|
||||
consumePacket[{}]={};
|
||||
consumePacket[{v1_,v2_,v3_,t1_,t2_,t3_,otherBits___}]:=With[{packetVersion=Sow@FromDigits[{v1,v2,v3},2],typeId=FromDigits[{t1,t2,t3},2], rest={otherBits}},
|
||||
Switch[typeId,
|
||||
4,With[{thisPacket=TakeWhile[Partition[rest,UpTo@5],#[[1]]==1&]},
|
||||
{FromDigits[Flatten[Join[Rest/@thisPacket,rest[[5 Length@thisPacket+2;;5 Length@thisPacket+5]]]],2],rest[[5 Length@thisPacket+6;;]]}
|
||||
],
|
||||
|
||||
_,
|
||||
With[{operator=
|
||||
Switch[typeId,0,Plus,1,Times,2,Min,3,Max,5,Boole@*Greater,6,Boole@*Less,7,Boole@*Equal]
|
||||
},
|
||||
Switch[rest[[1]],
|
||||
0,With[{subPacketLen=FromDigits[rest[[2;;16]],2]},
|
||||
{operator@@consumePackets[rest[[17;;16+subPacketLen]]],rest[[17+subPacketLen;;]]}
|
||||
],
|
||||
1,With[{subPackets=FromDigits[rest[[2;;12]],2]},
|
||||
With[{result=Nest[With[{c=consumePacket[#[[2]]]},{Join[#[[1]],{c[[1]]}],c[[2]]}]&,{{},rest[[13;;]]},subPackets]},
|
||||
{operator@@result[[1]],result[[2]]}
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
]
|
||||
]]
|
||||
|
||||
consumePackets[l_]:=First@NestWhile[With[{c=consumePacket[#[[2]]]},{Join[#[[1]],{c[[1]]}],c[[2]]}]&,{{},l},Not@AllTrue[#[[2]],#==0&]&]
|
||||
|
||||
|
||||
MapAt[Total@*Flatten,Reap@consumePackets[nums],{2}]
|
1
day_16/input.txt
Normal file
1
day_16/input.txt
Normal file
@@ -0,0 +1 @@
|
||||
005410C99A9802DA00B43887138F72F4F652CC0159FE05E802B3A572DBBE5AA5F56F6B6A4600FCCAACEA9CE0E1002013A55389B064C0269813952F983595234002DA394615002A47E06C0125CF7B74FE00E6FC470D4C0129260B005E73FCDFC3A5B77BF2FB4E0009C27ECEF293824CC76902B3004F8017A999EC22770412BE2A1004E3DCDFA146D00020670B9C0129A8D79BB7E88926BA401BAD004892BBDEF20D253BE70C53CA5399AB648EBBAAF0BD402B95349201938264C7699C5A0592AF8001E3C09972A949AD4AE2CB3230AC37FC919801F2A7A402978002150E60BC6700043A23C618E20008644782F10C80262F005679A679BE733C3F3005BC01496F60865B39AF8A2478A04017DCBEAB32FA0055E6286D31430300AE7C7E79AE55324CA679F9002239992BC689A8D6FE084012AE73BDFE39EBF186738B33BD9FA91B14CB7785EC01CE4DCE1AE2DCFD7D23098A98411973E30052C012978F7DD089689ACD4A7A80CCEFEB9EC56880485951DB00400010D8A30CA1500021B0D625450700227A30A774B2600ACD56F981E580272AA3319ACC04C015C00AFA4616C63D4DFF289319A9DC401008650927B2232F70784AE0124D65A25FD3A34CC61A6449246986E300425AF873A00CD4401C8A90D60E8803D08A0DC673005E692B000DA85B268E4021D4E41C6802E49AB57D1ED1166AD5F47B4433005F401496867C2B3E7112C0050C20043A17C208B240087425871180C01985D07A22980273247801988803B08A2DC191006A2141289640133E80212C3D2C3F377B09900A53E00900021109623425100723DC6884D3B7CFE1D2C6036D180D053002880BC530025C00F700308096110021C00C001E44C00F001955805A62013D0400B400ED500307400949C00F92972B6BC3F47A96D21C5730047003770004323E44F8B80008441C8F51366F38F240
|
240
day_16/src/lib.rs
Normal file
240
day_16/src/lib.rs
Normal file
@@ -0,0 +1,240 @@
|
||||
pub mod day_16 {
|
||||
|
||||
pub(crate) fn parse(s: &str) -> Vec<u8> {
|
||||
let s = s.trim_end();
|
||||
let mut answer = Vec::with_capacity(s.len() * 4);
|
||||
|
||||
for c in s.chars() {
|
||||
let number = if ('0'..='9').contains(&c) {
|
||||
c as u8 - b'0'
|
||||
} else {
|
||||
(c as u8 - b'A') + 10
|
||||
};
|
||||
answer.push(number / 8);
|
||||
answer.push((number / 4) % 2);
|
||||
answer.push((number / 2) % 2);
|
||||
answer.push(number % 2);
|
||||
}
|
||||
|
||||
answer
|
||||
}
|
||||
|
||||
struct OperatorPacket {
|
||||
version: u8,
|
||||
type_id: u8,
|
||||
sub_packets: Vec<Packet>,
|
||||
}
|
||||
|
||||
struct LiteralPacket {
|
||||
version: u8,
|
||||
value: u64,
|
||||
}
|
||||
|
||||
enum Packet {
|
||||
Literal(LiteralPacket),
|
||||
Operator(OperatorPacket),
|
||||
}
|
||||
|
||||
fn consume_literal(bits: &[u8], places: u8) -> u32 {
|
||||
let mut answer = 0;
|
||||
for &bit in bits.iter().take(places as usize) {
|
||||
answer *= 2;
|
||||
answer += bit as u32;
|
||||
}
|
||||
answer
|
||||
}
|
||||
|
||||
fn chomp_literal(bits: &[u8]) -> (u8, &[u8], bool) {
|
||||
(
|
||||
consume_literal(&bits[1..=4], 4) as u8,
|
||||
&bits[5..],
|
||||
bits[0] == 1,
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_packet(bits: &[u8]) -> (Packet, &[u8]) {
|
||||
let version = 4 * bits[0] + 2 * bits[1] + bits[2];
|
||||
let type_id = 4 * bits[3] + 2 * bits[4] + bits[5];
|
||||
match type_id {
|
||||
4 => {
|
||||
let mut value = 0;
|
||||
let mut should_continue = true;
|
||||
let mut bits = &bits[6..];
|
||||
while should_continue {
|
||||
let (byte, bits_2, should_continue_2) = chomp_literal(bits);
|
||||
should_continue &= should_continue_2;
|
||||
bits = bits_2;
|
||||
value = value * 16 + byte as u64;
|
||||
}
|
||||
(Packet::Literal(LiteralPacket { version, value }), bits)
|
||||
}
|
||||
_ => match bits[6] {
|
||||
0 => {
|
||||
let length = consume_literal(&bits[7..], 15);
|
||||
let unparsed = &bits[7 + 15 + (length as usize)..];
|
||||
let to_parse = &bits[7 + 15..7 + 15 + length as usize];
|
||||
let sub_packets = parse_packets(to_parse);
|
||||
(
|
||||
Packet::Operator(OperatorPacket {
|
||||
version,
|
||||
type_id,
|
||||
sub_packets,
|
||||
}),
|
||||
unparsed,
|
||||
)
|
||||
}
|
||||
1 => {
|
||||
let packets = consume_literal(&bits[7..], 11);
|
||||
let mut sub_packets = Vec::with_capacity(packets as usize);
|
||||
let mut bits = &bits[7 + 11..];
|
||||
for _ in 0..packets {
|
||||
let (packet, next_bits) = parse_packet(bits);
|
||||
bits = next_bits;
|
||||
sub_packets.push(packet);
|
||||
}
|
||||
(
|
||||
Packet::Operator(OperatorPacket {
|
||||
version,
|
||||
type_id,
|
||||
sub_packets,
|
||||
}),
|
||||
bits,
|
||||
)
|
||||
}
|
||||
c => panic!("Expected a bit, got {}", c),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_packets(bits: &[u8]) -> Vec<Packet> {
|
||||
let mut bits = bits;
|
||||
let mut answer = Vec::new();
|
||||
while bits.contains(&1) {
|
||||
let (packet, unparsed) = parse_packet(bits);
|
||||
answer.push(packet);
|
||||
bits = unparsed;
|
||||
}
|
||||
|
||||
answer
|
||||
}
|
||||
|
||||
fn sum_versions(p: &Packet) -> u32 {
|
||||
match p {
|
||||
Packet::Literal(p) => p.version as u32,
|
||||
Packet::Operator(p) => {
|
||||
p.sub_packets.iter().map(sum_versions).sum::<u32>() + p.version as u32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate(p: &Packet) -> u64 {
|
||||
match p {
|
||||
Packet::Literal(p) => p.value,
|
||||
Packet::Operator(p) => match p.type_id {
|
||||
0 => p.sub_packets.iter().map(evaluate).sum(),
|
||||
1 => p.sub_packets.iter().map(evaluate).product(),
|
||||
2 => p.sub_packets.iter().map(evaluate).min().unwrap() as u64,
|
||||
3 => p.sub_packets.iter().map(evaluate).max().unwrap() as u64,
|
||||
5 => {
|
||||
let v1 = evaluate(&p.sub_packets[0]);
|
||||
let v2 = evaluate(&p.sub_packets[1]);
|
||||
if v1 > v2 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
6 => {
|
||||
let v1 = evaluate(&p.sub_packets[0]);
|
||||
let v2 = evaluate(&p.sub_packets[1]);
|
||||
if v1 < v2 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
7 => {
|
||||
let v1 = evaluate(&p.sub_packets[0]);
|
||||
let v2 = evaluate(&p.sub_packets[1]);
|
||||
if v1 == v2 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
_ => panic!("Unexpected type"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input() -> Vec<u8> {
|
||||
parse(include_str!("../input.txt"))
|
||||
}
|
||||
|
||||
pub fn part_1(data: &[u8]) -> u32 {
|
||||
sum_versions(&parse_packets(data)[0])
|
||||
}
|
||||
|
||||
pub fn part_2(data: &[u8]) -> u64 {
|
||||
evaluate(&parse_packets(data)[0])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::day_16::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
assert_eq!(
|
||||
parse("D2FE28"),
|
||||
[1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part1_known() {
|
||||
// Version sums
|
||||
let data = parse("8A004A801A8002F478");
|
||||
assert_eq!(part_1(&data), 16);
|
||||
let data = parse("620080001611562C8802118E34");
|
||||
assert_eq!(part_1(&data), 12);
|
||||
let data = parse("C0015000016115A2E0802F182340");
|
||||
assert_eq!(part_1(&data), 23);
|
||||
let data = parse("A0016C880162017C3686B18A3D4780");
|
||||
assert_eq!(part_1(&data), 31);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_known() {
|
||||
let data = parse("D2FE28");
|
||||
assert_eq!(part_2(&data), 2021);
|
||||
let data = parse("38006F45291200");
|
||||
assert_eq!(part_2(&data), 1);
|
||||
let data = parse("EE00D40C823060");
|
||||
assert_eq!(part_2(&data), 3);
|
||||
let data = parse("C200B40A82");
|
||||
assert_eq!(part_2(&data), 3);
|
||||
let data = parse("04005AC33890");
|
||||
assert_eq!(part_2(&data), 54);
|
||||
let data = parse("880086C3E88112");
|
||||
assert_eq!(part_2(&data), 7);
|
||||
let data = parse("CE00C43D881120");
|
||||
assert_eq!(part_2(&data), 9);
|
||||
let data = parse("D8005AC2A8F0");
|
||||
assert_eq!(part_2(&data), 1);
|
||||
let data = parse("F600BC2D8F");
|
||||
assert_eq!(part_2(&data), 0);
|
||||
let data = parse("9C005AC2F8F0");
|
||||
assert_eq!(part_2(&data), 0);
|
||||
let data = parse("9C0141080250320F1802104A08");
|
||||
assert_eq!(part_2(&data), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_day_16() {
|
||||
let input = input();
|
||||
assert_eq!(part_1(&input), 923);
|
||||
assert_eq!(part_2(&input), 258888628940);
|
||||
}
|
||||
}
|
7
day_16/src/main.rs
Normal file
7
day_16/src/main.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use day_16::day_16::{input, part_1, part_2};
|
||||
|
||||
fn main() {
|
||||
let input = input();
|
||||
println!("part 1 => {}", part_1(&input));
|
||||
println!("part 2 => {}", part_2(&input));
|
||||
}
|
Reference in New Issue
Block a user