Day 16, in Mathematica and Rust (#14)

This commit is contained in:
Patrick Stevens
2021-12-16 11:06:04 +00:00
committed by GitHub
parent b257d486bd
commit b4ed32b7f7
8 changed files with 324 additions and 0 deletions

7
Cargo.lock generated
View File

@@ -206,6 +206,13 @@ dependencies = [
"criterion",
]
[[package]]
name = "day_16"
version = "0.1.0"
dependencies = [
"criterion",
]
[[package]]
name = "day_2"
version = "0.1.0"

View File

@@ -13,4 +13,5 @@ members = [
"day_11",
"day_12",
"day_13",
"day_16",
]

15
day_16/Cargo.toml Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1 @@
005410C99A9802DA00B43887138F72F4F652CC0159FE05E802B3A572DBBE5AA5F56F6B6A4600FCCAACEA9CE0E1002013A55389B064C0269813952F983595234002DA394615002A47E06C0125CF7B74FE00E6FC470D4C0129260B005E73FCDFC3A5B77BF2FB4E0009C27ECEF293824CC76902B3004F8017A999EC22770412BE2A1004E3DCDFA146D00020670B9C0129A8D79BB7E88926BA401BAD004892BBDEF20D253BE70C53CA5399AB648EBBAAF0BD402B95349201938264C7699C5A0592AF8001E3C09972A949AD4AE2CB3230AC37FC919801F2A7A402978002150E60BC6700043A23C618E20008644782F10C80262F005679A679BE733C3F3005BC01496F60865B39AF8A2478A04017DCBEAB32FA0055E6286D31430300AE7C7E79AE55324CA679F9002239992BC689A8D6FE084012AE73BDFE39EBF186738B33BD9FA91B14CB7785EC01CE4DCE1AE2DCFD7D23098A98411973E30052C012978F7DD089689ACD4A7A80CCEFEB9EC56880485951DB00400010D8A30CA1500021B0D625450700227A30A774B2600ACD56F981E580272AA3319ACC04C015C00AFA4616C63D4DFF289319A9DC401008650927B2232F70784AE0124D65A25FD3A34CC61A6449246986E300425AF873A00CD4401C8A90D60E8803D08A0DC673005E692B000DA85B268E4021D4E41C6802E49AB57D1ED1166AD5F47B4433005F401496867C2B3E7112C0050C20043A17C208B240087425871180C01985D07A22980273247801988803B08A2DC191006A2141289640133E80212C3D2C3F377B09900A53E00900021109623425100723DC6884D3B7CFE1D2C6036D180D053002880BC530025C00F700308096110021C00C001E44C00F001955805A62013D0400B400ED500307400949C00F92972B6BC3F47A96D21C5730047003770004323E44F8B80008441C8F51366F38F240

240
day_16/src/lib.rs Normal file
View 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
View 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));
}