From 7982e1c56fa1f7663a227c62604f9b1a151713f6 Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Sat, 3 Dec 2022 22:03:18 +0100 Subject: [PATCH] Some changes --- day03-gen-problem/src/main.rs | 7 ++-- day03/src/bitmask.rs | 62 +++++++++++++++++++++++++++++++++++ day03/src/main.rs | 48 +++++++++++++-------------- 3 files changed, 87 insertions(+), 30 deletions(-) create mode 100644 day03/src/bitmask.rs diff --git a/day03-gen-problem/src/main.rs b/day03-gen-problem/src/main.rs index 5090748..4f96410 100644 --- a/day03-gen-problem/src/main.rs +++ b/day03-gen-problem/src/main.rs @@ -50,12 +50,11 @@ fn replace_rand(rng: &mut rand::rngs::SmallRng, list: &mut Vec, item: I) { } fn gen_triple(rng: &mut rand::rngs::SmallRng) -> [String; 3] { - let amounts = ARGS.half_line_range().choose_multiple(rng, 3); // The badge that is shared in this triple let badge: char = *CHARS.choose(rng).unwrap(); - let mut lines: Vec = amounts - .iter() - .map(|&amount| { + let mut lines: Vec = (0..3) + .map(|_| { + let amount = rng.gen_range(ARGS.half_line_range()); debug_assert!(amount > 0); // The item that is shared between the halves let wrong_between_halves: char = *CHARS diff --git a/day03/src/bitmask.rs b/day03/src/bitmask.rs new file mode 100644 index 0000000..31028fd --- /dev/null +++ b/day03/src/bitmask.rs @@ -0,0 +1,62 @@ +use std::ops::{BitAnd, BitOr}; + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub struct Bitmask(u64); + +impl Bitmask { + #[cfg(test)] + pub const fn from_raw(raw: u64) -> Self { + Self(raw) + } + pub const fn from_alpha(alpha: &u8) -> Self { + // 'a' is the LSB, 'Z' is MSB + if *alpha <= b'Z' { + Self(1 << (alpha - b'A' + 26)) + } else { + Self(1 << (alpha - b'a')) + } + } + /// Get the priority from a rucksack entry + pub const fn to_priority(self) -> super::Prio { + self.0.trailing_zeros() as usize + 1 + } +} + +impl const BitOr for Bitmask { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } +} + +impl const BitAnd for Bitmask { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } +} + +impl std::fmt::Binary for Bitmask { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:b}", self.0) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bitmask_works() { + assert_eq!( + crate::parse_chars::("abc".into()), + Bitmask::from_raw(7) + ); + assert_eq!( + crate::parse_chars::("Z".into()), + Bitmask::from_raw(1 << 51) + ); + } +} diff --git a/day03/src/main.rs b/day03/src/main.rs index 63b3763..1d8c7b5 100644 --- a/day03/src/main.rs +++ b/day03/src/main.rs @@ -1,13 +1,18 @@ #![allow(incomplete_features)] #![feature(iter_array_chunks)] #![feature(generic_const_exprs)] +#![feature(const_trait_impl)] +#![feature(const_ops)] +use bitmask::Bitmask; + use std::{ fs::File, io::{BufRead, BufReader}, }; +mod bitmask; + type Prio = usize; -type AsciiChar = u8; #[derive(Debug, Default)] enum SolvePuzzle { @@ -23,39 +28,29 @@ trait IsTrue {} /// A correct assertion, of course, is true! impl IsTrue for Assert {} -/// Get the priority from a rucksack entry -fn to_priority(char: AsciiChar) -> Prio { - match char { - // a..z have priority 1..=26 - b'a'..=b'z' => (char - b'a' + 1) as usize, - // A..Z have priority 27..=52 - b'A'..=b'Z' => (char - b'A' + 27) as usize, - _ => unreachable!(), - } -} - /// Convert the line into a list of ascii chars -fn parse_chars(line: String) -> Vec { - line.as_bytes().to_vec() +fn parse_chars>(line: S) -> Bitmask { + line.as_ref() + .as_bytes() + .iter() + .map(Bitmask::from_alpha) + .reduce(std::ops::BitOr::bitor) + .unwrap() } /// Given an array of size `N` containing lists of chars. /// Find the char that is contained in all `N` lists, if any. -fn find_n_entry(lists: [Vec; N]) -> Option +fn find_n_entry(lists: [Bitmask; N]) -> Option where Assert<{ N > 1 }>: IsTrue, { - let mut found = lists[0].clone(); - for list in &lists[1..] { - found.retain(|char| list.contains(char)) - } - found.first().copied() + lists.into_iter().reduce(|left, right| left & right) } -/// Split the given [`Vec`] in the middle -fn split_line(line: Vec) -> [Vec; 2] { +/// Split the given [`String`] in the middle +fn split_line(line: String) -> [String; 2] { let (left, right) = line.split_at(line.len() / 2); - [left.to_vec(), right.to_vec()] + [left.to_string(), right.to_string()] } /// Solve the first puzzle: @@ -65,11 +60,12 @@ fn solve_first_puzzle(reader: B) -> usize { reader .lines() .map(Result::unwrap) + .flat_map(split_line) .map(parse_chars) - .map(split_line) + .array_chunks::<2>() .map(find_n_entry) .map(Option::unwrap) - .map(to_priority) + .map(Bitmask::to_priority) .sum() } @@ -84,7 +80,7 @@ fn solve_second_puzzle(reader: B) -> usize { .array_chunks::<3>() .map(find_n_entry) .map(Option::unwrap) - .map(to_priority) + .map(Bitmask::to_priority) .sum() }