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::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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user