Solve day 8!
This commit is contained in:
parent
509f796a52
commit
84dc9854c9
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue