From aac5c02b823d5ff5c94703a19ee60ca044f994a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Thu, 16 Dec 2021 14:08:24 +0100 Subject: [PATCH] implement day 16 part 1 --- src/day_16/mod.rs | 45 +++++++ src/day_16/model.rs | 39 ++++++ src/day_16/parsing.rs | 280 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 4 files changed, 365 insertions(+) create mode 100644 src/day_16/mod.rs create mode 100644 src/day_16/model.rs create mode 100644 src/day_16/parsing.rs diff --git a/src/day_16/mod.rs b/src/day_16/mod.rs new file mode 100644 index 0000000..783091e --- /dev/null +++ b/src/day_16/mod.rs @@ -0,0 +1,45 @@ +mod model; +mod parsing; + +use aoc_runner_derive::{aoc, aoc_generator}; +pub use model::{Kind, Operator, Packet}; +pub use parsing::{parse_packet, BinTokens}; + +#[aoc_generator(day16)] +pub fn parse_input(input: &str) -> Packet { + parse_packet(&mut input.parse::().unwrap()).unwrap() +} + +#[aoc(day16, part1)] +pub fn part1(input: &Packet) -> usize { + input.sum_of_version_numbers() +} + +//#[aoc(dayx, part2)] +//pub fn part2(input: &[Input]) -> usize { +// unimplemented!(); +//} + +#[cfg(test)] +mod test { + const EXAMPLE_INPUT: &str = "A0016C880162017C3686B18A3D4780"; + const RESULT_PART_1: usize = 31; + //const RESULT_PART_2: usize = 0; + + #[test] + fn part1_example() { + let result = super::part1(&super::parse_input(EXAMPLE_INPUT)); + assert_eq!(result, RESULT_PART_1); + } + + //#[test] + //fn part2_example() { + // let result = super::part2(&super::parse_input(EXAMPLE_INPUT)); + // assert_eq!(result, RESULT_PART_2); + //} + + #[test] + fn parse_example() { + super::parse_input(EXAMPLE_INPUT); + } +} diff --git a/src/day_16/model.rs b/src/day_16/model.rs new file mode 100644 index 0000000..2f7187b --- /dev/null +++ b/src/day_16/model.rs @@ -0,0 +1,39 @@ +#[derive(Debug, PartialEq)] +pub struct Packet { + pub version: usize, + pub kind: Kind, +} + +impl Packet { + pub fn sum_of_version_numbers(&self) -> usize { + match &self.kind { + Kind::Operator(op) => { + op.sub_packets + .iter() + .map(|sub| sub.sum_of_version_numbers()) + .sum::() + + self.version + } + Kind::LiteralValue(_) => self.version, + } + } +} + +#[derive(Debug, PartialEq)] +pub enum Kind { + Operator(Operator), + LiteralValue(usize), +} + +#[derive(Debug, PartialEq)] +pub struct Operator { + pub opcode: usize, + pub length: Length, + pub sub_packets: Vec, +} + +#[derive(Debug, PartialEq)] +pub enum Length { + Count(usize), + Length(usize), +} diff --git a/src/day_16/parsing.rs b/src/day_16/parsing.rs new file mode 100644 index 0000000..8fa5c96 --- /dev/null +++ b/src/day_16/parsing.rs @@ -0,0 +1,280 @@ +use std::str::FromStr; + +use bitvec::bitvec; +use bitvec::order::Msb0; +use bitvec::prelude::BitVec; +use yap::TokenLocation; +use yap::Tokens; + +use crate::day_16::model::Length; +use crate::day_16::Operator; + +use super::{Kind, Packet}; + +pub fn parse_packet(tokens: &mut impl Tokens) -> Option { + yap::one_of!(tokens; + parse_literal_value(tokens), + parse_operator(tokens), + ) +} + +pub fn parse_literal_value(tokens: &mut impl Tokens) -> Option { + tokens.optional(|t| { + let (version, type_id) = parse_version_and_type(t)?; + + if type_id != 4 { + return None; + } + + let mut value = 0usize; + let mut last_group_reached = false; + loop { + match t.next() { + Some(true) => (), + Some(false) => last_group_reached = true, + None => return None, + } + + value <<= 4; + value |= read_number(t, 4)?; + + if last_group_reached { + break; + } + } + Some(Packet { + version, + kind: Kind::LiteralValue(value), + }) + }) +} + +pub fn read_number(tokens: &mut impl Tokens, bits: usize) -> Option { + tokens.optional(|t| { + let mut value = 0usize; + for _ in 0..bits { + value <<= 1; + value |= t.next()? as usize; + } + Some(value) + }) +} + +pub fn parse_operator(tokens: &mut impl Tokens) -> Option { + tokens.optional(|t| { + let (version, opcode) = parse_version_and_type(t)?; + + if opcode == 4 { + return None; + } + + let length = match t.next()? { + true => Length::Count(read_number(t, 11)?), + false => Length::Length(read_number(t, 15)?), + }; + + let mut sub_packets = Vec::new(); + + match length { + Length::Count(count) => { + for _ in 0..count { + sub_packets.push(parse_packet(t)?) + } + } + Length::Length(length) => { + let sub_start = t.offset(); + while t.offset() - sub_start < length { + sub_packets.push(parse_packet(t)?) + } + if t.offset() - sub_start != length { + return None; + } + } + } + + Some(Packet { + version, + kind: Kind::Operator(Operator { + opcode, + length, + sub_packets, + }), + }) + }) +} + +pub fn parse_version_and_type(tokens: &mut impl Tokens) -> Option<(usize, usize)> { + tokens.optional(|t| { + let version = read_number(t, 3)?; + let type_id = read_number(t, 3)?; + Some((version, type_id)) + }) +} + +pub struct BinTokens { + bits: BitVec, + location: BinLocation, +} + +impl FromStr for BinTokens { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + let mut vec = BitVec::new(); + for hex in s.chars() { + vec.append(&mut match hex { + '0' => bitvec![Msb0, u8; 0, 0, 0, 0], + '1' => bitvec![Msb0, u8; 0, 0, 0, 1], + '2' => bitvec![Msb0, u8; 0, 0, 1, 0], + '3' => bitvec![Msb0, u8; 0, 0, 1, 1], + '4' => bitvec![Msb0, u8; 0, 1, 0, 0], + '5' => bitvec![Msb0, u8; 0, 1, 0, 1], + '6' => bitvec![Msb0, u8; 0, 1, 1, 0], + '7' => bitvec![Msb0, u8; 0, 1, 1, 1], + '8' => bitvec![Msb0, u8; 1, 0, 0, 0], + '9' => bitvec![Msb0, u8; 1, 0, 0, 1], + 'A' => bitvec![Msb0, u8; 1, 0, 1, 0], + 'B' => bitvec![Msb0, u8; 1, 0, 1, 1], + 'C' => bitvec![Msb0, u8; 1, 1, 0, 0], + 'D' => bitvec![Msb0, u8; 1, 1, 0, 1], + 'E' => bitvec![Msb0, u8; 1, 1, 1, 0], + 'F' => bitvec![Msb0, u8; 1, 1, 1, 1], + _ => return Err("string contains non-hex characters"), + }) + } + + Ok(BinTokens { + bits: vec, + location: BinLocation { offset: 0 }, + }) + } +} + +impl Iterator for BinTokens { + type Item = bool; + + fn next(&mut self) -> Option { + if self.bits.len() > self.location.offset { + let bit = self.bits.get(self.location.offset)?; + self.location.increment(); + Some(*bit) + } else { + None + } + } +} + +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub struct BinLocation { + offset: usize, +} + +impl BinLocation { + fn increment(&mut self) { + self.offset += 1; + } +} + +impl TokenLocation for BinLocation { + fn offset(&self) -> usize { + self.offset + } +} + +impl Tokens for BinTokens { + type Location = BinLocation; + + fn location(&self) -> Self::Location { + self.location + } + + fn set_location(&mut self, location: Self::Location) { + self.location = location + } + + fn is_at_location(&self, location: &Self::Location) -> bool { + self.location == *location + } +} + +#[cfg(test)] +mod test { + use crate::day_16::model::Length; + + use super::super::{Kind, Operator, Packet}; + use super::BinTokens; + + #[test] + fn hex_binary_tokens() { + let mut tokens = "D2FE28".parse::().unwrap(); + let expected = vec![ + true, true, false, true, false, false, true, false, true, true, true, true, true, true, + true, false, false, false, true, false, true, false, false, false, + ]; + for val in expected.iter() { + assert_eq!(tokens.next(), Some(*val)); + } + assert!(tokens.next().is_none()); + } + + #[test] + fn parse_version_and_type() { + assert_eq!( + super::parse_version_and_type(&mut "D2FE28".parse::().unwrap()), + Some((6, 4)) + ) + } + + #[test] + fn parse_literal_value_first_example() { + let packet = Packet { + version: 6, + kind: Kind::LiteralValue(2021), + }; + assert_eq!( + super::parse_literal_value(&mut "D2FE28".parse::().unwrap()), + Some(packet) + ) + } + + #[test] + fn parse_operator_first_example() { + let packet = + super::parse_operator(&mut "38006F45291200".parse::().unwrap()).unwrap(); + assert_eq!(packet.version, 1); + if let Kind::Operator(operator) = packet.kind { + assert_eq!(operator.opcode, 6); + assert_eq!(operator.length, Length::Length(27)); + assert_eq!(operator.sub_packets.len(), 2); + } else { + panic!(); + } + } + + #[test] + fn parse_operator_second_example() { + let packet = + super::parse_operator(&mut "EE00D40C823060".parse::().unwrap()).unwrap(); + assert_eq!(packet.version, 7); + if let Kind::Operator(operator) = packet.kind { + assert_eq!(operator.opcode, 3); + assert_eq!(operator.length, Length::Count(3)); + assert_eq!(operator.sub_packets.len(), 3); + } else { + panic!(); + } + } + + #[test] + fn other_examples() { + for (string, sum) in [ + ("8A004A801A8002F478", 16), + ("620080001611562C8802118E34", 12), + ("C0015000016115A2E0802F182340", 23), + ("A0016C880162017C3686B18A3D4780", 31), + ] { + let packet = super::parse_packet(&mut string.parse::().unwrap()).unwrap(); + assert_eq!(packet.sum_of_version_numbers(), sum); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 8015de6..0f9c2b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,5 +17,6 @@ pub mod day_12; pub mod day_13; pub mod day_14; pub mod day_15; +pub mod day_16; aoc_lib! { year = 2021 }