diff --git a/Cargo.lock b/Cargo.lock index 7baa389..49ba865 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,3 +5,52 @@ version = 3 [[package]] name = "aoc2021" version = "0.1.0" +dependencies = [ + "bitvec", + "hex", +] + +[[package]] +name = "bitvec" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5237f00a8c86130a0cc317830e558b966dd7850d48a953d998c813f01a41b527" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "funty" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1847abb9cb65d566acd5942e94aea9c8f547ad02c98e1649326fc0e8910b8b1e" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "wyz" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "129e027ad65ce1453680623c3fb5163cbf7107bfe1aa32257e7d0e63f9ced188" +dependencies = [ + "tap", +] diff --git a/Cargo.toml b/Cargo.toml index d9c006d..cc56b57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bitvec = "0.22.3" +hex = "0.4.3" [profile.release] diff --git a/inputs/sample.txt b/inputs/sample.txt index 4ced67a..0a1278e 100644 --- a/inputs/sample.txt +++ b/inputs/sample.txt @@ -1 +1 @@ -9C0141080250320F1802104A08 +A0016C880162017C3686B18A3D4780 diff --git a/src/main.rs b/src/main.rs index 88c20c6..2012e37 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,72 +1,16 @@ #![feature(test)] -use std::collections::HashMap; - extern crate test; +use bitvec::view::BitView; + +type BitSlice<'a> = &'a bitvec::slice::BitSlice; + const INPUTS: [&'static str; 2] = [ include_str!("../inputs/sample.txt"), include_str!("../inputs/input.txt"), ]; -fn parse_input(input: &'static str) -> String { - let mapping = HashMap::from([ - ('0', "0000"), - ('1', "0001"), - ('2', "0010"), - ('3', "0011"), - ('4', "0100"), - ('5', "0101"), - ('6', "0110"), - ('7', "0111"), - ('8', "1000"), - ('9', "1001"), - ('A', "1010"), - ('B', "1011"), - ('C', "1100"), - ('D', "1101"), - ('E', "1110"), - ('F', "1111"), - ]); - let input = input.trim(); - - let mut out = String::with_capacity(input.len() * 4); - - for c in input.chars() { - let v = *mapping.get(&c).unwrap(); - out.push_str(v); - } - - out -} - -fn read_header(s: &mut impl Iterator) -> (u8, u8) { - let raw_version: String = s.by_ref().take(3).collect(); - let version = u8::from_str_radix(&raw_version, 2).unwrap(); - - let raw_type_id: String = s.take(3).collect(); - let type_id = u8::from_str_radix(&raw_type_id, 2).unwrap(); - - (version, type_id) -} - -fn read_literal(input: &mut impl Iterator) -> (u64, u64) { - let mut out = String::new(); - let mut read = 0; - loop { - let last = input.next().unwrap() == '0'; - out.extend(input.take(4)); - read += 5; - - if last { - break; - } - } - - (u64::from_str_radix(&out, 2).unwrap(), read) -} - #[derive(Debug)] - struct PacketHeader { version: u8, type_id: u8, @@ -77,121 +21,148 @@ struct Packet { header: PacketHeader, size: u64, literal: Option, - sub_packets: Vec, } impl Packet { - fn value(&self) -> u64 { - match self.header.type_id { - 0 => self.sub_packets.iter().fold(0, |a, x| a + x.value()), - 1 => self.sub_packets.iter().fold(1, |a, x| a * x.value()), - 2 => self.sub_packets.iter().map(|x| x.value()).min().unwrap(), - 3 => self.sub_packets.iter().map(|x| x.value()).max().unwrap(), - 4 => self.literal.unwrap(), - 5 => { - let value1 = self.sub_packets[0].value(); - let value2 = self.sub_packets[1].value(); + fn new(input: &'static str) -> Self { + let out = hex::decode(input).unwrap(); - if value1 > value2 { - 1 - } else { - 0 - } - } - 6 => { - let value1 = self.sub_packets[0].value(); - let value2 = self.sub_packets[1].value(); + let mut bvec: BitSlice = BitView::view_bits(out.as_slice()); - if value1 < value2 { - 1 - } else { - 0 - } - } - 7 => { - let value1 = self.sub_packets[0].value(); - let value2 = self.sub_packets[1].value(); + Self::parse(&mut bvec) + } - if value1 == value2 { - 1 - } else { - 0 - } + fn read_literal(ip: &mut BitSlice) -> (u64, u64) { + let mut out = 0; + let mut read = 0; + + loop { + let (word, remaining) = ip.split_at(5); + *ip = remaining; + read += 5; + + out = out << 4 | read_as_u8(&word[1..]) as u64; + + if !*word.first().unwrap() { + break; } - _ => unreachable!(), } + + (out, read) + } + + fn read_header(ip: &mut BitSlice) -> (u8, u8) { + (take_u8(ip, 3), take_u8(ip, 3)) + } + + fn parse(input: &mut BitSlice) -> Packet { + let (version, type_id) = Self::read_header(input); + + let mut out = Packet { + size: 6, + literal: None, + header: PacketHeader { version, type_id }, + sub_packets: vec![], + }; + + match type_id { + // Literal + 4 => { + let (literal, read) = Self::read_literal(input); + + out.size += read; + out.literal = Some(literal); + } + + // Operation + _ => { + let length_type_id = take_u8(input, 1); + out.size += 1; + + match length_type_id { + 0 => { + let mut trailing_packet_size = take_u64(input, 15); + out.size += 15; + + while trailing_packet_size > 0 { + let packet = Self::parse(input); + + trailing_packet_size -= packet.size; + out.size += packet.size; + out.sub_packets.push(packet); + } + } + + 1 => { + let trailing_packet_count = take_u64(input, 11); + out.size += 11; + + for _ in 0..trailing_packet_count { + let packet = Self::parse(input); + out.size += packet.size; + out.sub_packets.push(packet); + } + } + _ => unreachable!(), + } + } + }; + out } } -fn read_packet(input: &mut impl Iterator) -> Packet { - let (version, type_id) = read_header(input); +fn take_u8(ip: &mut BitSlice, offset: usize) -> u8 { + let (num, left) = ip.split_at(offset); + *ip = left; - let mut out = Packet { - size: 6, - literal: None, - header: PacketHeader { version, type_id }, - sub_packets: vec![], - }; + read_as_u8(num) +} - match type_id { - // Literal - 4 => { - let (literal, read) = read_literal(input); +fn take_u64(ip: &mut BitSlice, offset: usize) -> u64 { + let (num, left) = ip.split_at(offset); + *ip = left; - out.size += read; - out.literal = Some(literal); - } + read_as_u64(num) +} - // Operation - _ => { - let length_type_id = input.next().unwrap(); - out.size += 1; +fn read_as_u64(ip: BitSlice) -> u64 { + let mut out = 0; - match length_type_id { - '0' => { - let mut trailing_packet_size = - u64::from_str_radix(&input.take(15).collect::(), 2).unwrap(); - out.size += 15; + for bit in ip { + out = out << 1 | if *bit { 1 } else { 0 }; + } - while trailing_packet_size > 0 { - let packet = read_packet(input); - - trailing_packet_size -= packet.size; - out.size += packet.size; - out.sub_packets.push(packet); - } - } - - '1' => { - let trailing_packet_count = - usize::from_str_radix(&input.take(11).collect::(), 2).unwrap(); - out.size += 11; - - for _ in 0..trailing_packet_count { - let packet = read_packet(input); - out.size += packet.size; - out.sub_packets.push(packet); - } - } - - _ => unreachable!(), - } - } - }; out } -fn solution(input: String) -> u64 { - let mut input = input.chars(); - let packet = read_packet(&mut input); +fn read_as_u8(ip: BitSlice) -> u8 { + let mut out = 0; - packet.value() + for bit in ip { + out = out << 1 | if *bit { 1 } else { 0 }; + } + + out +} + +fn solution(input: &'static str) -> u64 { + let packet = Packet::new(input.trim()); + + let mut stack = vec![packet]; + + let mut answer = 0; + while let Some(packet) = stack.pop() { + answer += packet.header.version as u64; + + stack.extend(packet.sub_packets); + } + + answer } fn main() { for input in INPUTS { - let input = parse_input(input); let result = solution(input); println!("Result = {}", result); } @@ -199,9 +170,8 @@ fn main() { #[bench] fn solution_bench(b: &mut test::Bencher) { - let input = parse_input(INPUTS[1]); b.iter(|| { - let result = solution(input.clone()); + let result = solution(INPUTS[1]); test::black_box(result); }) }