From e306f40e3a0f07825d152933ead1e265032e5911 Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Tue, 11 Jan 2022 20:21:05 +0200 Subject: [PATCH] feat: solve day 16 part 1 & 2 --- input/16.txt | 1 + src/day16.rs | 256 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 3 + 3 files changed, 260 insertions(+) create mode 100644 input/16.txt create mode 100644 src/day16.rs diff --git a/input/16.txt b/input/16.txt new file mode 100644 index 0000000..1c38429 --- /dev/null +++ b/input/16.txt @@ -0,0 +1 @@ +A20D790042F1274011955491808B802F1C60B20030327AF2CC248AA800E7CDD726F3D78F4966F571A300BA54D668E2519249265160803EA9DE562A1801204ACE53C954ACE53C94C659BDF318FD1366EF44D96EB11005FB39154E0068A7C3A6B379646C80348A0055E6642B332109B8D6F0F12980452C9D322B28012EC72D51B300426CF70017996DE6C2B2C70C01A04B67B9F9EC8DAFE679D0992A80380104065FA8012805BD380120051E380146006380142004A00E920034C0801CA007B0099420053007144016E28018800CCC8CBB5FE79A3D91E1DC9FB151A1006CC0188970D6109803B1D61344320042615C198C2A014C589D00943096B3CCC081009173D015B004C401C8E10421E8002110BA18C193004A52257E0094BCE1ABB94C2C9005112DFAA5E80292B405927020106BC01494DFA6E329BF4DD273B69E233DB04C435BEF7A0CC00CFCDF31DC6AD20A3002A498CC01D00042229479890200E4438A91700010F88F0EA251802D33FE976802538EF38E2401B84CA05004833529CD2A5BD9DDAC566009CC33E8024200CC528E71F40010A8DF0C61D8002B5076719A5D418034891895CFD320730F739A119CB2EA0072D25E870EA465E189FDC1126AF4B91100A03600A0803713E2FC7D00043A25C3B8A12F89D2E6440242489A7802400086C788FB09C0010C8BB132309005A1400D2CBE7E7F2F9F9F4BB83803B25286DFE628E129EBCB7483C8802F3D0A2542E3004AC0169BD944AFF263361F1B48010496089807100BA54A66675769B1787D230C621EF8B9007893F058A009AE4ED7A5BBDBE05262CEC0002FC7C20082622E0020D0D66A2D04021D5003ED3D396E19A1149054FCA3586BD00020129B0037300042E0CC1184C000874368F70A251D840239798AC8DC9A56F7C6C0E0728015294D9290030B226938A928D0 diff --git a/src/day16.rs b/src/day16.rs new file mode 100644 index 0000000..a984476 --- /dev/null +++ b/src/day16.rs @@ -0,0 +1,256 @@ + +pub enum PacketBody { + Literal(u64), + Operator(Vec) +} + +pub struct Packet { + version: u8, + r#type: u8, + body: PacketBody +} + +fn to_bits(hex: &str) -> String { + let mut bits = String::new(); + for c in hex.bytes() { + match c { + b'0'..=b'9' => bits.push_str(format!("{:0>4b}", c - b'0').as_str()), + b'A'..=b'F' => bits.push_str(format!("{:0>4b}", c - b'A' + 10).as_str()), + _ => () + } + } + return bits; +} + +fn parse_literal_body(bits_str: &str) -> (PacketBody, u32) { + let mut value_bits = String::new(); + let mut cursor = 0; + let bytes = bits_str.as_bytes(); + loop { + value_bits.push_str(&bits_str[cursor+1..cursor+5]); + if bytes[cursor] == b'0' { break; } + cursor += 5; + } + let value = u64::from_str_radix(&value_bits, 2).unwrap(); + + return (PacketBody::Literal(value), (cursor+5) as u32); +} + +fn parse_operator_body(bits_str: &str) -> (PacketBody, u32) { + let mut size: usize = 0; + let mut packets = Vec::new(); + let bytes = bits_str.as_bytes(); + size += 1; + if bytes[0] == b'1' { + size += 11; + let count = u32::from_str_radix(&bits_str[1..12], 2).unwrap(); + for _ in 0..count { + let (packet, s) = parse_packet(&bits_str[size..]); + packets.push(packet); + size += s as usize; + } + } else { + let total_size = u32::from_str_radix(&bits_str[1..16], 2).unwrap(); + size += 15; + while ((size-16) as u32) < total_size { + let (packet, s) = parse_packet(&bits_str[size..]); + packets.push(packet); + size += s as usize; + } + } + return (PacketBody::Operator(packets), size as u32); +} + +fn parse_packet(bits: &str) -> (Packet, u32) { + let r#type = u8::from_str_radix(&bits[3..6], 2).unwrap(); + let (body, body_size) = match r#type { + 4 => parse_literal_body(&bits[6..]), + _ => parse_operator_body(&bits[6..]) + }; + + return (Packet { + version: u8::from_str_radix(&bits[0..3], 2).unwrap(), + r#type, + body + }, + body_size + 6 + ); +} + +pub fn parse_input(input: &str) -> Packet { + let (packet, _) = parse_packet(&to_bits(input)); + return packet; +} + +fn sum_packet_versions(packet: &Packet) -> u32 { + let mut sum: u32 = packet.version.into(); + match &packet.body { + PacketBody::Operator(packets) => { + for sub_packet in packets { + sum += sum_packet_versions(sub_packet); + } + }, + _ => () + }; + return sum; +} + +pub fn part1(packet: &Packet) -> u32 { + sum_packet_versions(packet) +} + +fn eval_sum_packets(packets: &[Packet]) -> u64 { + let mut sum = 0; + for packet in packets { + sum += eval_packet(packet); + } + return sum; +} +fn eval_product_packets(packets: &[Packet]) -> u64 { + let mut product = 1; + for packet in packets { + product *= eval_packet(packet); + } + return product; +} +fn eval_minimum_packets(packets: &[Packet]) -> u64 { + let mut min = u64::MAX; + for packet in packets { + min = min.min(eval_packet(packet)); + } + return min; +} +fn eval_maximum_packets(packets: &[Packet]) -> u64 { + let mut max = 0; + for packet in packets { + max = max.max(eval_packet(packet)); + } + return max; +} +fn eval_greater_packets(packets: &[Packet]) -> u64 { + let first_packet = packets.get(0).unwrap(); + let second_packet = packets.get(1).unwrap(); + if eval_packet(first_packet) > eval_packet(second_packet) { 1 } else { 0 } +} +fn eval_less_packets(packets: &[Packet]) -> u64 { + let first_packet = packets.get(0).unwrap(); + let second_packet = packets.get(1).unwrap(); + if eval_packet(first_packet) < eval_packet(second_packet) { 1 } else { 0 } +} +fn eval_equal_packets(packets: &[Packet]) -> u64 { + let first_packet = packets.get(0).unwrap(); + let second_packet = packets.get(1).unwrap(); + if eval_packet(first_packet) == eval_packet(second_packet) { 1 } else { 0 } +} + +fn eval_packet(packet: &Packet) -> u64 { + match &packet.body { + PacketBody::Literal(value) => *value, + PacketBody::Operator(packets) => { + match packet.r#type { + 0 => eval_sum_packets(packets), + 1 => eval_product_packets(packets), + 2 => eval_minimum_packets(packets), + 3 => eval_maximum_packets(packets), + 5 => eval_greater_packets(packets), + 6 => eval_less_packets(packets), + 7 => eval_equal_packets(packets), + _ => unreachable!() + } + } + } +} + +pub fn part2(packet: &Packet) -> u64 { + eval_packet(packet) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn part1_example_1() { + let packet = parse_input("8A004A801A8002F478"); + let result = part1(&packet); + assert_eq!(result, 16); + } + + #[test] + fn part1_example_2() { + let packet = parse_input("620080001611562C8802118E34"); + let result = part1(&packet); + assert_eq!(result, 12); + } + + #[test] + fn part1_example_3() { + let packet = parse_input("C0015000016115A2E0802F182340"); + let result = part1(&packet); + assert_eq!(result, 23); + } + + #[test] + fn part1_example_4() { + let packet = parse_input("A0016C880162017C3686B18A3D4780"); + let result = part1(&packet); + assert_eq!(result, 31); + } + + #[test] + fn part2_example_1() { + let packet = parse_input("C200B40A82"); + let result = part2(&packet); + assert_eq!(result, 3); + } + + #[test] + fn part2_example_2() { + let packet = parse_input("04005AC33890"); + let result = part2(&packet); + assert_eq!(result, 54); + } + + #[test] + fn part2_example_3() { + let packet = parse_input("880086C3E88112"); + let result = part2(&packet); + assert_eq!(result, 7); + } + + #[test] + fn part2_example_4() { + let packet = parse_input("CE00C43D881120"); + let result = part2(&packet); + assert_eq!(result, 9); + } + + #[test] + fn part2_example_5() { + let packet = parse_input("D8005AC2A8F0"); + let result = part2(&packet); + assert_eq!(result, 1); + } + + #[test] + fn part2_example_6() { + let packet = parse_input("F600BC2D8F"); + let result = part2(&packet); + assert_eq!(result, 0); + } + + #[test] + fn part2_example_7() { + let packet = parse_input("9C005AC2F8F0"); + let result = part2(&packet); + assert_eq!(result, 0); + } + + #[test] + fn part2_example_8() { + let packet = parse_input("9C0141080250320F1802104A08"); + let result = part2(&packet); + assert_eq!(result, 1); + } +} + diff --git a/src/main.rs b/src/main.rs index fcd6491..21a6104 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ mod day12; mod day13; mod day14; mod day15; +mod day16; use std::{env, process}; use std::fs::File; @@ -59,6 +60,8 @@ fn run(day: i32, part: i32, input_filename: &str) { "14.2" => println!("{}", day14::part2(&day14::parse_input(&contents))), "15.1" => println!("{}", day15::part1(&day15::parse_input(&contents))), "15.2" => println!("{}", day15::part2(&mut day15::parse_input(&contents))), + "16.1" => println!("{}", day16::part1(&day16::parse_input(&contents))), + "16.2" => println!("{}", day16::part2(&day16::parse_input(&contents))), _ => println!("Day {} part {} not found", day, part) } }