implement day 8

This commit is contained in:
Jan Christian Grünhage 2021-12-09 08:11:59 +01:00
parent dbd5139991
commit e20149a5c4
4 changed files with 353 additions and 0 deletions

92
src/day_08/mod.rs Normal file
View file

@ -0,0 +1,92 @@
mod model;
mod parsing;
use aoc_runner_derive::{aoc, aoc_generator};
pub use model::{Input, Ssd};
pub use parsing::parse_input as parse_input_tokens;
use yap::IntoTokens;
#[aoc_generator(day8)]
pub fn parse_input(input: &str) -> Vec<Input> {
parse_input_tokens(&mut input.into_tokens())
}
#[aoc(day8, part1)]
pub fn part1(input: &[Input]) -> usize {
let mut trivials = 0;
for input in input {
let mut ssd = Ssd::default();
for number in &input.output_values {
match ssd.try_decode(number) {
Some('1') | Some('4') | Some('7') | Some('8') => trivials += 1,
_ => (),
}
}
}
trivials
}
#[aoc(day8, part2)]
pub fn part2(input: &[Input]) -> usize {
input
.iter()
.map(|input| {
let mut ssd = Ssd::default();
let unresolved = input.unique_combinations.clone();
for num in [2, 3, 4, 7, 6, 5].iter() {
unresolved
.iter()
.filter(|pattern| pattern.len() == *num)
.for_each(|pattern| {
ssd.try_decode(pattern).unwrap();
});
}
dbg!(&ssd);
let value: String = input
.output_values
.iter()
.map(|val| ssd.try_decode(val).unwrap())
.collect();
value.parse::<usize>().unwrap()
})
.sum()
}
#[cfg(test)]
mod test {
const EXAMPLE_INPUT: &str =
"be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe
edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec | fcgedb cgb dgebacf gc
fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef | cg cg fdcagb cbg
fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega | efabcd cedba gadfec cb
aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga | gecf egdcabf bgf bfgea
fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf | gebdcfa ecba ca fadegcb
dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf | cefg dcbef fcge gbcadfe
bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd | ed bcgafe cdgba cbgef
egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg | gbdfcae bgc cg cgb
gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc | fgae cfgab fg bagce";
const RESULT_PART_1: usize = 26;
const RESULT_PART_2: usize = 61229;
#[test]
fn part1_example() {
let result = super::part1(&super::parse_input(EXAMPLE_INPUT));
assert_eq!(result, RESULT_PART_1);
}
#[test]
fn part2_example() {
let result = super::part2(&super::parse_input(EXAMPLE_INPUT));
assert_eq!(result, RESULT_PART_2);
}
#[test]
fn parse_example() {
let example = super::parse_input(EXAMPLE_INPUT);
assert_eq!(example.len(), 10);
}
}

223
src/day_08/model.rs Normal file
View file

@ -0,0 +1,223 @@
use std::collections::HashSet;
pub struct Input {
pub unique_combinations: Vec<String>,
pub output_values: Vec<String>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct Ssd {
pub a: HashSet<char>,
pub b: HashSet<char>,
pub c: HashSet<char>,
pub d: HashSet<char>,
pub e: HashSet<char>,
pub f: HashSet<char>,
pub g: HashSet<char>,
}
impl Default for Ssd {
fn default() -> Self {
let possible_pins: HashSet<char> = "abcdefg".chars().collect();
Self {
a: possible_pins.clone(),
b: possible_pins.clone(),
c: possible_pins.clone(),
d: possible_pins.clone(),
e: possible_pins.clone(),
f: possible_pins.clone(),
g: possible_pins,
}
}
}
impl Ssd {
pub fn try_decode(&mut self, number: &str) -> Option<char> {
let number: HashSet<char> = number.chars().collect();
match number.len() {
2 => {
self.a.retain(|c| !number.contains(c));
self.b.retain(|c| !number.contains(c));
self.c.retain(|c| number.contains(c));
self.d.retain(|c| !number.contains(c));
self.e.retain(|c| !number.contains(c));
self.f.retain(|c| number.contains(c));
self.g.retain(|c| !number.contains(c));
Some('1')
}
3 => {
self.a.retain(|c| number.contains(c));
self.b.retain(|c| !number.contains(c));
self.c.retain(|c| number.contains(c));
self.d.retain(|c| !number.contains(c));
self.e.retain(|c| !number.contains(c));
self.f.retain(|c| number.contains(c));
self.g.retain(|c| !number.contains(c));
Some('7')
}
4 => {
self.a.retain(|c| !number.contains(c));
self.b.retain(|c| number.contains(c));
self.c.retain(|c| number.contains(c));
self.d.retain(|c| number.contains(c));
self.e.retain(|c| !number.contains(c));
self.f.retain(|c| number.contains(c));
self.g.retain(|c| !number.contains(c));
Some('4')
}
5 => {
let full: HashSet<char> = "abcdefg".chars().collect();
let missing = full.difference(&number).copied().collect::<HashSet<char>>();
let four = self.get_number_set(4);
let one = self.get_number_set(1);
let missing_six = full
.difference(&self.get_number_set(6))
.copied()
.collect::<HashSet<char>>();
let missing_nince = full
.difference(&self.get_number_set(9))
.copied()
.collect::<HashSet<char>>();
let missing_five = missing_six
.union(&missing_nince)
.copied()
.collect::<HashSet<char>>();
if missing.is_disjoint(&one) {
self.a.retain(|c| number.contains(c));
self.b.retain(|c| !number.contains(c));
self.c.retain(|c| number.contains(c));
self.d.retain(|c| number.contains(c));
self.e.retain(|c| !number.contains(c));
self.f.retain(|c| number.contains(c));
self.g.retain(|c| number.contains(c));
Some('3')
} else if missing.is_subset(&four) {
self.a.retain(|c| number.contains(c));
self.b.retain(|c| !number.contains(c));
self.c.retain(|c| number.contains(c));
self.d.retain(|c| number.contains(c));
self.e.retain(|c| number.contains(c));
self.f.retain(|c| !number.contains(c));
self.g.retain(|c| number.contains(c));
Some('2')
} else if missing == missing_five {
self.a.retain(|c| number.contains(c));
self.b.retain(|c| number.contains(c));
self.c.retain(|c| !number.contains(c));
self.d.retain(|c| number.contains(c));
self.e.retain(|c| !number.contains(c));
self.f.retain(|c| number.contains(c));
self.g.retain(|c| number.contains(c));
Some('5')
} else {
None
}
}
6 => {
let full: HashSet<char> = "abcdefg".chars().collect();
let missing = full.difference(&number).copied().collect::<HashSet<char>>();
let four = self.get_number_set(4);
let one = self.get_number_set(1);
if missing.is_disjoint(&four) {
self.a.retain(|c| number.contains(c));
self.b.retain(|c| number.contains(c));
self.c.retain(|c| number.contains(c));
self.d.retain(|c| number.contains(c));
self.e.retain(|c| !number.contains(c));
self.f.retain(|c| number.contains(c));
self.g.retain(|c| number.contains(c));
Some('9')
} else if missing.is_disjoint(&one) {
self.a.retain(|c| number.contains(c));
self.b.retain(|c| number.contains(c));
self.c.retain(|c| number.contains(c));
self.d.retain(|c| !number.contains(c));
self.e.retain(|c| number.contains(c));
self.f.retain(|c| number.contains(c));
self.g.retain(|c| number.contains(c));
Some('0')
} else {
self.a.retain(|c| number.contains(c));
self.b.retain(|c| number.contains(c));
self.c.retain(|c| !number.contains(c));
self.d.retain(|c| number.contains(c));
self.e.retain(|c| number.contains(c));
self.f.retain(|c| number.contains(c));
self.g.retain(|c| number.contains(c));
Some('6')
}
}
7 => Some('8'),
_ => None,
}
}
fn get_number_set(&self, num: usize) -> HashSet<char> {
Self::merge_sets(self.get_number_sets(num))
}
fn get_number_sets(&self, num: usize) -> Vec<HashSet<char>> {
match num {
1 => vec![&self.c, &self.f],
2 => vec![&self.a, &self.c, &self.d, &self.e, &self.g],
3 => vec![&self.a, &self.c, &self.d, &self.f, &self.g],
4 => vec![&self.b, &self.c, &self.d, &self.f],
5 => vec![&self.a, &self.b, &self.d, &self.f, &self.g],
6 => vec![&self.a, &self.b, &self.d, &self.e, &self.f, &self.g],
7 => vec![&self.a, &self.c, &self.f],
8 => vec![
&self.a, &self.b, &self.c, &self.d, &self.e, &self.f, &self.g,
],
9 => vec![&self.a, &self.b, &self.c, &self.d, &self.f, &self.g],
0 => vec![&self.a, &self.b, &self.c, &self.e, &self.f, &self.g],
_ => panic!(),
}
.iter()
.map(|set| set.iter().copied().collect())
.collect()
}
fn merge_sets(sets: Vec<HashSet<char>>) -> HashSet<char> {
sets.iter().flat_map(HashSet::iter).copied().collect()
}
}
#[cfg(test)]
mod test {
use super::Ssd;
#[test]
fn try_decode_trivials() {
let mut ssd = Ssd::default();
dbg!(&ssd);
assert_eq!(ssd.try_decode("cf"), Some('1'));
dbg!(&ssd);
assert_eq!(ssd.try_decode("acf"), Some('7'));
dbg!(&ssd);
assert_eq!(ssd.try_decode("bcdf"), Some('4'));
dbg!(&ssd);
assert_eq!(ssd.try_decode("abcdefg"), Some('8'));
}
#[test]
fn try_decode() {
let mut ssd = Ssd::default();
dbg!(&ssd);
assert_eq!(ssd.try_decode("gf"), Some('1'));
dbg!(&ssd);
assert_eq!(ssd.try_decode("fgd"), Some('7'));
dbg!(&ssd);
assert_eq!(ssd.try_decode("fgec"), Some('4'));
dbg!(&ssd);
assert_eq!(ssd.try_decode("bdegcaf"), Some('8'));
dbg!(&ssd);
assert_eq!(ssd.try_decode("aegbdf"), Some('0'));
dbg!(&ssd);
assert_eq!(ssd.try_decode("ecdfab"), Some('6'));
dbg!(&ssd);
assert_eq!(ssd.try_decode("gdcebf"), Some('9'));
dbg!(&ssd);
assert_eq!(ssd.try_decode("dbcfg"), Some('3'));
dbg!(&ssd);
assert_eq!(ssd.try_decode("dacgb"), Some('2'));
dbg!(&ssd);
assert_eq!(ssd.try_decode("fbedc"), Some('5'));
}
}

37
src/day_08/parsing.rs Normal file
View file

@ -0,0 +1,37 @@
use crate::parsing::{newline, space};
use yap::Tokens;
use super::Input;
pub fn parse_input_line(tokens: &mut impl Tokens<Item = char>) -> Option<Input> {
let unique_combinations: Vec<String> = tokens
.sep_by(|t| parse_ssd_combo(t), |t| space(t))
.collect();
tokens.tokens(" | ".chars());
let output_values: Vec<String> = tokens
.sep_by(|t| parse_ssd_combo(t), |t| space(t))
.collect();
if unique_combinations.len() == 10 && output_values.len() == 4 {
Some(super::Input {
output_values,
unique_combinations,
})
} else {
None
}
}
pub fn parse_ssd_combo(tokens: &mut impl Tokens<Item = char>) -> Option<String> {
let val: String = tokens.tokens_while(|t| "abcdefg".contains(*t)).collect();
if val.is_empty() {
None
} else {
Some(val)
}
}
pub fn parse_input(tokens: &mut impl Tokens<Item = char>) -> Vec<Input> {
tokens
.sep_by(|t| parse_input_line(t), |t| newline(t))
.collect()
}

View file

@ -9,5 +9,6 @@ pub mod day_04;
pub mod day_05;
pub mod day_06;
pub mod day_07;
pub mod day_08;
aoc_lib! { year = 2021 }