Compare commits

...

2 commits

Author SHA1 Message Date
Jan Christian Grünhage 7c8b242698 implement day 16 part 2 2021-12-16 14:42:27 +01:00
Jan Christian Grünhage aac5c02b82 implement day 16 part 1 2021-12-16 14:08:24 +01:00
4 changed files with 416 additions and 0 deletions

40
src/day_16/mod.rs Normal file
View file

@ -0,0 +1,40 @@
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::<BinTokens>().unwrap()).unwrap()
}
#[aoc(day16, part1)]
pub fn part1(input: &Packet) -> usize {
input.sum_of_version_numbers()
}
#[aoc(day16, part2)]
pub fn part2(input: &Packet) -> usize {
input.calculate()
}
#[cfg(test)]
mod test {
#[test]
fn part2_example() {
for (input, result) in [
("C200B40A82", 3),
("04005AC33890", 54),
("880086C3E88112", 7),
("CE00C43D881120", 9),
("D8005AC2A8F0", 1),
("F600BC2D8F", 0),
("9C005AC2F8F0", 0),
("9C0141080250320F1802104A08", 1),
] {
assert_eq!(super::part2(&super::parse_input(input)), result);
}
}
}

83
src/day_16/model.rs Normal file
View file

@ -0,0 +1,83 @@
#[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::<usize>()
+ self.version
}
Kind::LiteralValue(_) => self.version,
}
}
pub fn calculate(&self) -> usize {
match &self.kind {
Kind::Operator(op) => match op.operation {
Operation::Sum => op.sub_packets.iter().map(|p| p.calculate()).sum(),
Operation::Product => op.sub_packets.iter().map(|p| p.calculate()).product(),
Operation::Minimum => op.sub_packets.iter().map(|p| p.calculate()).min().unwrap(),
Operation::Maximum => op.sub_packets.iter().map(|p| p.calculate()).max().unwrap(),
Operation::GreaterThan => {
if op.sub_packets[0].calculate() > op.sub_packets[1].calculate() {
1
} else {
0
}
}
Operation::LessThan => {
if op.sub_packets[0].calculate() < op.sub_packets[1].calculate() {
1
} else {
0
}
}
Operation::EqualTo => {
if op.sub_packets[0].calculate() == op.sub_packets[1].calculate() {
1
} else {
0
}
}
},
Kind::LiteralValue(val) => *val,
}
}
}
#[derive(Debug, PartialEq)]
pub enum Kind {
Operator(Operator),
LiteralValue(usize),
}
#[derive(Debug, PartialEq)]
pub struct Operator {
pub operation: Operation,
pub length: Length,
pub sub_packets: Vec<Packet>,
}
#[derive(Debug, PartialEq)]
pub enum Length {
Count(usize),
Length(usize),
}
#[derive(Debug, PartialEq)]
pub enum Operation {
Sum,
Product,
Minimum,
Maximum,
GreaterThan,
LessThan,
EqualTo,
}

292
src/day_16/parsing.rs Normal file
View file

@ -0,0 +1,292 @@
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::model::Operation;
use super::{Kind, Packet};
pub fn parse_packet(tokens: &mut impl Tokens<Item = bool>) -> Option<Packet> {
yap::one_of!(tokens;
parse_literal_value(tokens),
parse_operator(tokens),
)
}
pub fn parse_literal_value(tokens: &mut impl Tokens<Item = bool>) -> Option<Packet> {
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<Item = bool>, bits: usize) -> Option<usize> {
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<Item = bool>) -> Option<Packet> {
tokens.optional(|t| {
let (version, opcode) = parse_version_and_type(t)?;
let operation = get_op_from_code(opcode)?;
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 {
operation,
length,
sub_packets,
}),
})
})
}
fn get_op_from_code(opcode: usize) -> Option<Operation> {
Some(match opcode {
0 => Operation::Sum,
1 => Operation::Product,
2 => Operation::Minimum,
3 => Operation::Maximum,
5 => Operation::GreaterThan,
6 => Operation::LessThan,
7 => Operation::EqualTo,
_ => return None,
})
}
pub fn parse_version_and_type(tokens: &mut impl Tokens<Item = bool>) -> Option<(usize, usize)> {
tokens.optional(|t| {
let version = read_number(t, 3)?;
let opcode = read_number(t, 3)?;
Some((version, opcode))
})
}
pub struct BinTokens {
bits: BitVec,
location: BinLocation,
}
impl FromStr for BinTokens {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
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<Self::Item> {
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, Operation};
use super::super::{Kind, Packet};
use super::BinTokens;
#[test]
fn hex_binary_tokens() {
let mut tokens = "D2FE28".parse::<BinTokens>().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::<BinTokens>().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::<BinTokens>().unwrap()),
Some(packet)
)
}
#[test]
fn parse_operator_first_example() {
let packet =
super::parse_operator(&mut "38006F45291200".parse::<BinTokens>().unwrap()).unwrap();
assert_eq!(packet.version, 1);
if let Kind::Operator(operator) = packet.kind {
assert_eq!(operator.operation, Operation::LessThan);
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::<BinTokens>().unwrap()).unwrap();
assert_eq!(packet.version, 7);
if let Kind::Operator(operator) = packet.kind {
assert_eq!(operator.operation, Operation::Maximum);
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::<BinTokens>().unwrap()).unwrap();
assert_eq!(packet.sum_of_version_numbers(), sum);
}
}
}

View file

@ -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 }