diff --git a/input/day16.txt b/input/day16.txt new file mode 100644 index 0000000..3b5092c --- /dev/null +++ b/input/day16.txt @@ -0,0 +1 @@ +620D7800996600E43184312CC01A88913E1E180310FA324649CD5B9DA6BFD107003A4FDE9C718593003A5978C00A7003C400A70025400D60259D400B3002880792201B89400E601694804F1201119400C600C144008100340013440021279A5801AE93CA84C10CF3D100875401374F67F6119CA46769D8664E76FC9E4C01597748704011E4D54D7C0179B0A96431003A48ECC015C0068670FA7EF1BC5166CE440239EFC226F228129E8C1D6633596716E7D4840129C4C8CA8017FCFB943699B794210CAC23A612012EB40151006E2D4678A4200EC548CF12E4FDE9BD4A5227C600F80021D08219C1A00043A27C558AA200F4788C91A1002C893AB24F722C129BDF5121FA8011335868F1802AE82537709999796A7176254A72F8E9B9005BD600A4FD372109FA6E42D1725EDDFB64FFBD5B8D1802323DC7E0D1600B4BCDF6649252B0974AE48D4C0159392DE0034B356D626A130E44015BD80213183A93F609A7628537EB87980292A0D800F94B66546896CCA8D440109F80233ABB3ABF3CB84026B5802C00084C168291080010C87B16227CB6E454401946802735CA144BA74CFF71ADDC080282C00546722A1391549318201233003361006A1E419866200DC758330525A0C86009CC6E7F2BA00A4E7EF7AD6E873F7BD6B741300578021B94309ABE374CF7AE7327220154C3C4BD395C7E3EB756A72AC10665C08C010D0046458E72C9B372EAB280372DFE1BCA3ECC1690046513E5D5E79C235498B9002BD132451A5C78401B99AFDFE7C9A770D8A0094EDAC65031C0178AB3D8EEF8E729F2C200D26579BEDF277400A9C8FE43D3030E010C6C9A078853A431C0C0169A5CB00400010F8C9052098002191022143D30047C011100763DC71824200D4368391CA651CC0219C51974892338D0 diff --git a/src/day16.rs b/src/day16.rs new file mode 100644 index 0000000..567ca9a --- /dev/null +++ b/src/day16.rs @@ -0,0 +1,240 @@ +use std::str::Chars; + +use itertools::Itertools; + +pub fn day16() { + assert_eq!( + parse_hex_str("D2FE28"), + Packet { + version: 6, + type_id: 4, + length: 21, + payload: Payload::Literal(2021), + }, + ); + assert_eq!( + parse_hex_str("38006F45291200"), + Packet { + version: 1, + type_id: 6, + length: 49, + payload: Payload::Operator(vec![ + Packet { + version: 6, + type_id: 4, + length: 11, + payload: Payload::Literal(10), + }, + Packet { + version: 2, + type_id: 4, + length: 16, + payload: Payload::Literal(20), + }, + ]), + }, + ); + dbg!(parse_hex_str("EE00D40C823060")); + + let sums = &[ + "8A004A801A8002F478", + "620080001611562C8802118E34", + "C0015000016115A2E0802F182340", + "A0016C880162017C3686B18A3D4780", + ] + .map(|s| parse_hex_str(s).sum_versions()); + assert_eq!(sums, &[16, 12, 23, 31]); + + let input = include_str!("../input/day16.txt"); + let packet = parse_hex_str(input); + println!("part 1: {}", packet.sum_versions()); + + let values = &[ + "C200B40A82", + "04005AC33890", + "880086C3E88112", + "CE00C43D881120", + "D8005AC2A8F0", + "F600BC2D8F", + "9C005AC2F8F0", + "9C0141080250320F1802104A08", + ] + .map(|s| parse_hex_str(s).eval()); + assert_eq!(values, &[3, 54, 7, 9, 1, 0, 0, 1]); + + println!("part 2: {}", packet.eval()); +} + +fn parse_hex_str(hex_str: &str) -> Packet { + let mut bits = HexToBits::new(hex_str.chars()); + parse_packet(&mut bits, 0) +} + +fn parse_packet<'a>(bits: &mut HexToBits<'a>, depth: u32) -> Packet { + let mut packet_len = 6; + let version = bits.read(3); + let type_id = bits.read(3); + let payload: Payload; + // println!("type id {} at depth {}", type_id, depth); + if type_id == 4 { + let mut val = 0; + loop { + let is_last = !bits.next().unwrap(); + val <<= 4; + val |= bits.read(4); + packet_len += 5; + if is_last { + break; + } + } + payload = Payload::Literal(val); + } else { + packet_len += 1; + let length_type = bits.next().unwrap(); + let length = if length_type { + packet_len += 11; + bits.read(11) + } else { + packet_len += 15; + bits.read(15) + }; + // println!("length type: {}, length: {}", length_type, length); + let mut sub_packets = vec![]; + let mut sub_packets_len = 0; + loop { + if length_type && sub_packets.len() >= length { + break; + } else if !length_type && sub_packets_len >= length { + break; + } + let packet = parse_packet(bits, depth + 1); + sub_packets_len += packet.length; + // println!( + // "read packet of length {} at depth {}", + // packet.length, + // depth + 1 + // ); + sub_packets.push(packet); + } + payload = Payload::Operator(sub_packets); + packet_len += sub_packets_len; + } + Packet { + version, + type_id, + length: packet_len, + payload, + } +} + +struct HexToBits<'a> { + hex_chars: Chars<'a>, + current: u32, + current_bits_remaining: u32, + read_so_far: usize, +} + +impl<'a> HexToBits<'a> { + fn new(chars: Chars<'a>) -> Self { + Self { + hex_chars: chars, + current: 0, + current_bits_remaining: 0, + read_so_far: 0, + } + } + + fn read(&mut self, len: u32) -> usize { + let mut res = 0; + for _ in 0..len { + res <<= 1; + res |= if self.next().unwrap() { 1 } else { 0 }; + } + res + } +} + +impl<'a> Iterator for HexToBits<'a> { + type Item = bool; + + fn next(&mut self) -> Option { + if self.current_bits_remaining > 0 { + let res = Some((self.current >> self.current_bits_remaining - 1) & 1 == 1); + self.current_bits_remaining -= 1; + self.read_so_far += 1; + res + } else if let Some(c) = self.hex_chars.next() { + self.current = c.to_digit(16).unwrap(); + self.current_bits_remaining = 3; + self.read_so_far += 1; + Some((self.current >> 3) & 1 == 1) + } else { + None + } + } +} + +#[derive(Debug, PartialEq)] +struct Packet { + version: usize, + type_id: usize, + length: usize, + payload: Payload, +} + +impl Packet { + fn sum_versions(&self) -> usize { + match self.payload { + Payload::Literal(_) => self.version, + Payload::Operator(ref sub) => { + self.version + sub.iter().map(|p| p.sum_versions()).sum::() + } + } + } + + fn eval(&self) -> usize { + match self.payload { + Payload::Literal(n) => n, + Payload::Operator(ref sub) => { + let sub_vals = sub.iter().map(|p| p.eval()); + match self.type_id { + 0 => sub_vals.sum(), + 1 => sub_vals.product(), + 2 => sub_vals.min().unwrap(), + 3 => sub_vals.max().unwrap(), + 5 => { + let (a, b) = sub_vals.collect_tuple().unwrap(); + if a > b { + 1 + } else { + 0 + } + } + 6 => { + let (a, b) = sub_vals.collect_tuple().unwrap(); + if a < b { + 1 + } else { + 0 + } + } + 7 => { + let (a, b) = sub_vals.collect_tuple().unwrap(); + if a == b { + 1 + } else { + 0 + } + } + _ => panic!(), + } + } + } + } +} + +#[derive(Debug, PartialEq)] +enum Payload { + Literal(usize), + Operator(Vec), +} diff --git a/src/main.rs b/src/main.rs index 172002c..8a003d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,8 @@ mod day12; mod day13; mod day14; mod day15; +mod day16; fn main() { - day15::day15(); + day16::day16(); }