mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
Bit-optimize day 8
The state of a seven segment display can be stored in a byte after all. Using NonZeroU8 makes the Options smaller too.
This commit is contained in:
@@ -1,30 +1,33 @@
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
use std::num::NonZeroU8;
|
||||||
|
|
||||||
use crate::common::LineIter;
|
use crate::common::LineIter;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
struct Segments([bool; 7]);
|
struct Segments(NonZeroU8);
|
||||||
|
|
||||||
impl Segments {
|
impl Segments {
|
||||||
pub fn overlap(&self, other: &Segments) -> usize {
|
pub fn overlap(self, other: Segments) -> u32 {
|
||||||
self.0
|
(self.0 | other.0).get().count_ones()
|
||||||
.iter()
|
}
|
||||||
.zip(&other.0)
|
|
||||||
.filter(|&(&ours, &theirs)| ours || theirs)
|
pub fn len(self) -> u32 {
|
||||||
.count()
|
self.0.get().count_ones()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromIterator<char> for Segments {
|
impl<'a> From<&'a str> for Segments {
|
||||||
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
fn from(s: &'a str) -> Self {
|
||||||
let mut buffer = [false; 7];
|
let mut buffer = 0;
|
||||||
|
|
||||||
for c in iter {
|
for &b in s.as_bytes() {
|
||||||
buffer[c as usize - b'a' as usize] = true;
|
debug_assert!((b'a'..=b'g').contains(&b));
|
||||||
|
|
||||||
|
buffer |= 1 << (b - b'a');
|
||||||
}
|
}
|
||||||
|
|
||||||
Self(buffer)
|
Self(NonZeroU8::new(buffer).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,28 +47,29 @@ pub fn part1(input: &mut dyn Read) -> String {
|
|||||||
total.to_string()
|
total.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode(line: &str) -> usize {
|
fn decode(line: &str, unmatched: &mut VecDeque<Segments>) -> usize {
|
||||||
let mut mapping = [None; 10];
|
let mut mapping = [None; 10];
|
||||||
|
|
||||||
let mut unmatched: VecDeque<_> = line.split(' ').filter(|&s| s != "|").collect();
|
unmatched.clear();
|
||||||
|
unmatched.extend(line.split(' ').filter(|&s| s != "|").map(Segments::from));
|
||||||
|
|
||||||
while let Some(digit) = unmatched.pop_front() {
|
while let Some(segments) = unmatched.pop_front() {
|
||||||
let segments: Segments = digit.chars().collect();
|
// Note: this loop might "deduce" a combination more than once, but deducing digits is
|
||||||
|
// idempotent so it does not interfere.
|
||||||
match digit.len() {
|
match segments.len() {
|
||||||
2 => mapping[1] = Some(segments),
|
2 => mapping[1] = Some(segments),
|
||||||
3 => mapping[7] = Some(segments),
|
3 => mapping[7] = Some(segments),
|
||||||
4 => mapping[4] = Some(segments),
|
4 => mapping[4] = Some(segments),
|
||||||
5 => {
|
5 => {
|
||||||
// Could be 2, 3, or 5
|
// Could be 2, 3, or 5
|
||||||
if let Some(one) = mapping[1] {
|
if let Some(one) = mapping[1] {
|
||||||
if segments.overlap(&one) == 5 {
|
if segments.overlap(one) == 5 {
|
||||||
// No lines added, so must be a three
|
// No lines added, so must be a three
|
||||||
mapping[3] = Some(segments);
|
mapping[3] = Some(segments);
|
||||||
continue;
|
continue;
|
||||||
} else if let Some(four) = mapping[4] {
|
} else if let Some(four) = mapping[4] {
|
||||||
// Should be 6 for 5 and 7 for 2
|
// Should be 6 for 5 and 7 for 2
|
||||||
if segments.overlap(&four) == 6 {
|
if segments.overlap(four) == 6 {
|
||||||
mapping[5] = Some(segments);
|
mapping[5] = Some(segments);
|
||||||
} else {
|
} else {
|
||||||
mapping[2] = Some(segments);
|
mapping[2] = Some(segments);
|
||||||
@@ -73,27 +77,27 @@ fn decode(line: &str) -> usize {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unmatched.push_back(digit);
|
unmatched.push_back(segments);
|
||||||
}
|
}
|
||||||
6 => {
|
6 => {
|
||||||
// Could be 0, 6, or 9
|
// Could be 0, 6, or 9
|
||||||
if let Some(one) = mapping[1] {
|
if let Some(one) = mapping[1] {
|
||||||
if segments.overlap(&one) == 7 {
|
if segments.overlap(one) == 7 {
|
||||||
mapping[6] = Some(segments);
|
mapping[6] = Some(segments);
|
||||||
continue;
|
continue;
|
||||||
} else if let Some(four) = mapping[4] {
|
} else if let Some(four) = mapping[4] {
|
||||||
if segments.overlap(&four) == 6 {
|
if segments.overlap(four) == 6 {
|
||||||
mapping[9] = Some(segments);
|
mapping[9] = Some(segments);
|
||||||
} else {
|
} else {
|
||||||
mapping[0] = Some(segments);
|
mapping[0] = Some(segments);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
unmatched.push_back(digit);
|
unmatched.push_back(segments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
7 => mapping[8] = Some(segments),
|
7 => mapping[8] = Some(segments),
|
||||||
_ => panic!("Invalid digit: {}", digit),
|
_ => panic!("Invalid segments!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +105,7 @@ fn decode(line: &str) -> usize {
|
|||||||
.skip_while(|&s| s != "|")
|
.skip_while(|&s| s != "|")
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.map(|s| {
|
.map(|s| {
|
||||||
let segments: Segments = s.chars().collect();
|
let segments = Segments::from(s);
|
||||||
mapping.iter().position(|s| &Some(segments) == s).unwrap()
|
mapping.iter().position(|s| &Some(segments) == s).unwrap()
|
||||||
})
|
})
|
||||||
.fold(0, |acc, n| acc * 10 + n)
|
.fold(0, |acc, n| acc * 10 + n)
|
||||||
@@ -111,8 +115,11 @@ pub fn part2(input: &mut dyn Read) -> String {
|
|||||||
let mut reader = LineIter::new(input);
|
let mut reader = LineIter::new(input);
|
||||||
let mut total = 0;
|
let mut total = 0;
|
||||||
|
|
||||||
|
// Allocate work memory outside the decode function so we can reuse it
|
||||||
|
let mut work_buffer = VecDeque::new();
|
||||||
|
|
||||||
while let Some(line) = reader.next() {
|
while let Some(line) = reader.next() {
|
||||||
total += decode(line);
|
total += decode(line, &mut work_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
total.to_string()
|
total.to_string()
|
||||||
|
|||||||
Reference in New Issue
Block a user