1
Fork 0

Solve day 8!

This commit is contained in:
Bauke 2021-12-08 15:44:13 +01:00
parent 509f796a52
commit 84dc9854c9
Signed by: Bauke
GPG Key ID: C1C0F29952BCF558
3 changed files with 185 additions and 0 deletions

111
source/day_08/display.rs Normal file
View File

@ -0,0 +1,111 @@
use std::collections::{HashMap, HashSet};
use color_eyre::Result;
pub type CharSet = HashSet<char>;
#[derive(Debug)]
pub struct Display(pub isize, pub CharSet);
impl Display {
pub fn parse(s: &str) -> Self {
Self(s.len() as isize, CharSet::from_iter(s.chars()))
}
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)
}
}

72
source/day_08/mod.rs Normal file
View File

@ -0,0 +1,72 @@
use color_eyre::{eyre::eyre, Result};
mod display;
use display::{CharSet, Display};
pub fn solve() -> Result<()> {
let input_data = include_str!("../../data/day_08.txt").trim();
println!("Day 08 Part 1: {}", part_1(input_data)?);
println!("Day 08 Part 2: {}", part_2(input_data)?);
Ok(())
}
fn part_1(input: &str) -> Result<usize> {
Ok(
input
.lines()
.map(|line| {
line
.split(" | ")
.nth(1)
.ok_or(eyre!("Invalid input: {}", line))
})
.collect::<Result<Vec<_>>>()?
.into_iter()
.flat_map(|line| line.split(" ").map(Display::parse))
.filter(|display| [2, 4, 3, 7].contains(&display.0))
.count(),
)
}
fn part_2(input: &str) -> Result<isize> {
let mut sum = 0;
for line in input.lines() {
let mut split = line.split(" | ");
// Figure out the displays from the signal side.
let displays = Display::figure_out_from_others(
split
.next()
.ok_or(eyre!("Invalid input: {}", line))?
.split(" ")
.map(Display::parse)
.collect::<Vec<_>>(),
)?;
// Get all the CharSets from the encoded side.
let encoded = split
.next()
.ok_or(eyre!("Invalid input: {}", line))?
.split(" ")
.map(str::chars)
.map(CharSet::from_iter)
.collect::<Vec<_>>();
// Loop through the encoded numbers backwards so we can use the loop index
// to multiply it by 10 to the power of the index.
// So 123 would be (3 * 1) + (2 * 10) + (1 * 100).
for (index, set) in encoded.into_iter().rev().enumerate() {
let decoded_number = displays
.iter()
.find(|display| display.1 == &set)
.map(|display| display.0)
.ok_or(eyre!("Impossible to decode {:?}", set))?;
sum += 10_isize.pow(index as u32) * decoded_number;
}
}
Ok(sum)
}

View File

@ -9,6 +9,7 @@ mod day_04;
mod day_05;
mod day_06;
mod day_07;
mod day_08;
fn main() -> Result<()> {
color_eyre::install()?;
@ -23,6 +24,7 @@ fn main() -> Result<()> {
day_05::solve,
day_06::solve,
day_07::solve,
day_08::solve,
];
for day in days {