1
Fork 0
advent-of-code/source/year_2021/day_08/display.rs

118 lines
3.4 KiB
Rust
Raw Permalink Normal View History

2024-01-14 21:04:10 +00:00
//! [`Display`] implementation.
2021-12-08 14:44:13 +00:00
use std::collections::{HashMap, HashSet};
use color_eyre::Result;
2024-01-14 21:04:10 +00:00
/// Type alias for [`HashSet`] with [`char`]s.
2021-12-08 14:44:13 +00:00
pub type CharSet = HashSet<char>;
2024-01-14 21:04:10 +00:00
/// The display with a given size and set of characters.
2021-12-08 14:44:13 +00:00
#[derive(Debug)]
pub struct Display(pub isize, pub CharSet);
impl Display {
2024-01-14 21:04:10 +00:00
/// Parse a [`Display`] from a string.
2021-12-08 14:44:13 +00:00
pub fn parse(s: &str) -> Self {
Self(s.len() as isize, CharSet::from_iter(s.chars()))
}
2024-01-14 21:04:10 +00:00
/// Calculate the remaining displays based on the existing other ones.
2021-12-08 14:44:13 +00:00
pub fn figure_out_from_others(
others: Vec<Self>,
) -> Result<HashMap<isize, CharSet>> {
let mut displays = HashMap::<isize, CharSet>::new();
// Loop over all displays infinitely and stop when we've figured out all 10.
for display in others.iter().cycle() {
if displays.len() == 10 {
break;
}
let length = display.0;
let charset = display.1.clone();
// First check for the known unique segment lengths and insert them.
if let Some(key) = match length {
2 => Some(1),
4 => Some(4),
3 => Some(7),
7 => Some(8),
_ => None,
} {
displays.insert(key, charset);
continue;
}
// Without the 1 and 4 segments we can't do anything else so grab those
// or continue the loop.
let (one, four) = match (displays.get(&1), displays.get(&4)) {
(Some(one), Some(four)) => (one, four),
_ => continue,
};
// Create the subset we'll use to figure out 0 and 5 using the difference
// between 4 and 1.
let five_zero_subset = CharSet::from_iter(four.difference(one).copied());
// The segments for 2, 3 and 5 all have a length of 5.
if length == 5 {
// Only 3 has 1 as its subset.
if one.is_subset(&charset) {
displays.insert(3, charset);
continue;
}
// Then only 5 will have the subset created earlier.
if five_zero_subset.is_subset(&charset) {
displays.insert(5, charset);
continue;
}
// Then to figure out 2 we need to know both 3 and 5 so continue if we
// haven't gotten them yet.
let (three, five) = match (displays.get(&3), displays.get(&5)) {
(Some(three), Some(five)) => (three, five),
_ => continue,
};
// Then 2 will simply be the remaining set of length 5 as long as it
// doesn't equal 3 or 5.
if ![three, five].contains(&&charset) {
displays.insert(2, charset);
continue;
}
}
// The segments for 0, 6 and 9 all have length 6.
if length == 6 {
// Only 6 *does not* have the subset for 1.
if !one.is_subset(&charset) {
displays.insert(6, charset);
continue;
}
// Then 0 *does not* have this subset from earlier.
if !five_zero_subset.is_subset(&charset) {
displays.insert(0, charset);
continue;
}
let (zero, six) = match (displays.get(&0), displays.get(&6)) {
(Some(zero), Some(six)) => (zero, six),
_ => continue,
};
// And again to figure out the remaining segment we use the other 2
// we know now.
if ![zero, six].contains(&&charset) {
displays.insert(9, charset);
continue;
}
}
}
Ok(displays)
}
}