This commit is contained in:
Joseph Montanaro 2022-01-02 10:55:42 -08:00
parent 5633d37773
commit 6492fd5c03
3 changed files with 256 additions and 4 deletions

127
2021/src/day16.rs Normal file
View File

@ -0,0 +1,127 @@
use color_eyre::eyre;
use crate::lib::{BitVec, BitSlice};
#[derive(Debug)]
struct Packet {
version: u8,
type_id: u8,
content: PacketContent,
}
#[derive(Debug)]
enum PacketContent {
Literal(u64),
Operator(Vec<Packet>),
}
struct Parser {
data: BitVec,
idx: usize,
}
impl Parser {
fn from_hex(hex: &str) -> eyre::Result<Self> {
let mut data = BitVec::with_capacity(hex.len() * 4);
for i in 0 .. (hex.len() / 2) { // we are assuming valid hex
let chunk = &hex[i * 2 .. (i + 1) * 2];
let byte = u8::from_str_radix(chunk, 16)?;
data.push_byte(byte);
}
Ok(Parser {data, idx: 0})
}
fn next(&mut self, n: usize) -> BitSlice {
let range = self.idx .. self.idx + n;
self.idx += n;
self.data.slice(range)
}
fn parse_value(&mut self) -> u64 {
let mut value = 0;
loop {
let group = self.next(5);
value = (value << 4) | group.slice(1..5).as_u64();
if !group[0] {break;}
}
value
}
fn parse_children(&mut self) -> Vec<Packet> {
let mut children = Vec::new();
if self.next(1).as_bool() {
// count refers to number of sub-packets
let count = self.next(11).as_u16();
children = Vec::with_capacity(count as usize);
for _i in 0..count {
children.push(self.parse());
}
}
else {
// len refers to content length in bits
let len = self.next(15).as_u16();
let end = self.idx + (len as usize);
while self.idx < end {
children.push(self.parse());
}
}
children
}
fn parse(&mut self) -> Packet {
let version = self.next(3).as_u8();
let type_id = self.next(3).as_u8();
let content = match type_id {
4 => PacketContent::Literal(self.parse_value()),
_ => PacketContent::Operator(self.parse_children()),
};
Packet {version, type_id, content}
}
}
fn sum_versions(root: &Packet) -> usize {
let mut total = root.version as usize;
if let PacketContent::Operator(children) = &root.content {
for child in children {
total += sum_versions(child);
}
}
total
}
fn evaluate(packet: &Packet) -> usize {
let children = match &packet.content {
PacketContent::Literal(value) => {return *value as usize;}, // early exit here
PacketContent::Operator(children) => children,
};
let mut values = children.iter().map(|p| evaluate(p));
match packet.type_id {
0 => values.sum(),
1 => values.product(),
2 => values.min().unwrap(),
3 => values.max().unwrap(),
// it's guaranteed that comparisons will have exactly two children
5 => if values.next().unwrap() > values.next().unwrap() {1} else {0},
6 => if values.next().unwrap() < values.next().unwrap() {1} else {0},
7 => if values.next().unwrap() == values.next().unwrap() {1} else {0},
// the version number was a 3-bit value, so:
_ => unreachable!(),
}
}
pub fn run(data: &str) -> eyre::Result<(usize, usize)> {
let mut parser = Parser::from_hex(data)?;
let root = parser.parse();
let one = sum_versions(&root);
let two = evaluate(&root);
Ok((one, two))
}

View File

@ -4,7 +4,7 @@ use std::collections::HashMap;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::fs::File; use std::fs::File;
use std::hash::Hash; use std::hash::Hash;
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut, Range};
use std::str::FromStr; use std::str::FromStr;
use color_eyre::{eyre}; use color_eyre::{eyre};
@ -341,3 +341,128 @@ impl<K> Counter<K>
self.data.iter().map(|(k, v)| (k, *v)) self.data.iter().map(|(k, v)| (k, *v))
} }
} }
pub struct BitVec {
data: Vec<u8>,
len: usize,
}
#[allow(dead_code)]
impl BitVec {
fn new() -> Self {
BitVec {data: vec![], len: 0}
}
pub fn with_capacity(cap: usize) -> Self {
let true_cap = (cap + 8 - 1) / 8; // ceiling division
BitVec {
data: Vec::with_capacity(true_cap),
len: 0
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn push(&mut self, b: bool) {
let offset = self.len % 8;
let v = (b as u8) << (7 - offset);
match offset {
0 => self.data.push(v),
_ => *self.data.last_mut().unwrap() |= v,
}
self.len += 1;
}
pub fn push_byte(&mut self, byte: u8) {
if self.len() % 8 != 0 {
panic!("Attempted to push full byte across byte boundary");
}
self.data.push(byte);
self.len += 8;
}
pub fn slice(&self, range: Range<usize>) -> BitSlice {
if range.end > self.len {
panic!("Slice out of bounds: {}..{}", range.start, range.end);
}
BitSlice {
src: self,
start: range.start,
end: range.end,
}
}
}
impl Index<usize> for BitVec {
type Output = bool;
fn index(&self, index: usize) -> &bool {
let (idx, offset) = (index / 8, index % 8);
let bit = self.data[idx] & (128 >> offset);
if bit == 0 {&false} else {&true}
}
}
#[derive(Copy, Clone)]
pub struct BitSlice<'a> {
src: &'a BitVec,
start: usize,
end: usize,
}
#[allow(dead_code)]
impl BitSlice<'_> {
pub fn len(&self) -> usize {
self.end - self.start
}
pub fn as_u64(&self) -> u64 {
if self.len() > 64 {panic!("Slice too large for u64");}
let mut value = 0;
for i in self.start..self.end {
let offset = self.end - 1 - i;
value |= (self.src[i] as u64) << offset;
}
value
}
pub fn as_u16(&self) -> u16 {
if self.len() > 16 {panic!("Slice too large for u16");}
self.as_u64() as u16
}
pub fn as_u8(&self) -> u8 {
if self.len() > 8 {panic!("Slice too large for u8");}
self.as_u64() as u8
}
pub fn as_bool(&self) -> bool {
if self.len() != 1 {
panic!("Only slices of length 1 can be converted to bool");
}
self.as_u64() == 1
}
pub fn slice(&self, range: Range<usize>) -> BitSlice {
if range.end > self.len() {
panic!("Slice out of bounds: {}..{}", range.start, range.end);
}
BitSlice {
src: &self.src,
start: self.start + range.start,
end: self.start + range.end,
}
}
}
impl Index<usize> for BitSlice<'_> {
type Output = bool;
fn index(&self, index: usize) -> &bool {
self.src.index(self.start + index)
}
}

View File

@ -4,14 +4,14 @@ use color_eyre::eyre;
mod lib; mod lib;
use lib::load; use lib::load;
mod day15; mod day16;
fn main() -> eyre::Result<()> { fn main() -> eyre::Result<()> {
let data = load("data/15.txt")?; let data = load("data/16.txt")?;
let start = Instant::now(); let start = Instant::now();
let (one, two) = day15::run(&data)?; let (one, two) = day16::run(&data)?;
let (dur, unit) = format_ns(start.elapsed().as_nanos()); let (dur, unit) = format_ns(start.elapsed().as_nanos());
let precision = 2.0 - dur.log10().floor(); let precision = 2.0 - dur.log10().floor();