day 16
This commit is contained in:
parent
5633d37773
commit
6492fd5c03
127
2021/src/day16.rs
Normal file
127
2021/src/day16.rs
Normal 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))
|
||||
}
|
127
2021/src/lib.rs
127
2021/src/lib.rs
@ -4,7 +4,7 @@ use std::collections::HashMap;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fs::File;
|
||||
use std::hash::Hash;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::ops::{Index, IndexMut, Range};
|
||||
use std::str::FromStr;
|
||||
|
||||
use color_eyre::{eyre};
|
||||
@ -341,3 +341,128 @@ impl<K> Counter<K>
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -4,14 +4,14 @@ use color_eyre::eyre;
|
||||
|
||||
mod lib;
|
||||
use lib::load;
|
||||
mod day15;
|
||||
mod day16;
|
||||
|
||||
|
||||
fn main() -> eyre::Result<()> {
|
||||
let data = load("data/15.txt")?;
|
||||
let data = load("data/16.txt")?;
|
||||
|
||||
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 precision = 2.0 - dur.log10().floor();
|
||||
|
Loading…
x
Reference in New Issue
Block a user