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)
|
|
|
|
}
|
|
|
|
}
|