Skip to main content

aoc/year2021/
day08.rs

1//! # Seven Segment Search
2//!
3//! Listing each digit and the number of segments that are lit when that digit is displayed:
4//!
5//! | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
6//! |---|---|---|---|---|---|---|---|---|---|
7//! | 6 | 2 | 5 | 5 | 4 | 5 | 6 | 3 | 7 | 6 |
8//!
9//! shows that 3 digits share 5 segments and another 3 share 6 segments so we don't have enough
10//! information just yet. Listing the total occurrences of each segment summing across all 10
11//! digits:
12//!
13//! | a | b | c | d | e | f | g |
14//! |---|---|---|---|---|---|---|
15//! | 8 | 6 | 8 | 7 | 4 | 9 | 7 |
16//!
17//! shows that 2 segments share 7 occurrences and 2 share 8 occurrences so this is still not quite
18//! enough information. However, if we combine these 2 tables by *summing* the segment occurrences
19//! for each digit, for example `1` has segments `c` and `f` for a total of 17, then the table looks
20//! like:
21//!
22//! | 0  |  1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 |
23//! |----|----|----|----|----|----|----|----|----|----|
24//! | 42 | 17 | 34 | 39 | 30 | 37 | 41 | 25 | 49 | 45 |
25//!
26//! Now each digit can be uniquely identified. Our algorithm is as follows:
27//! * Calculate the occurrences of each scrambled segment letter before the `|` symbol. Since the
28//!   cardinality of the set is fixed, we can use an array instead of a `HashMap` for speed.
29//! * Add the occurrences of each scrambled segment for each digit after the `|` symbol, then lookup
30//!   the total and map directly to the unscrambled digit.
31use crate::util::iter::*;
32
33type Input = Vec<[u32; 4]>;
34
35pub fn parse(input: &str) -> Input {
36    input.lines().map(descramble).collect()
37}
38
39pub fn part1(input: &Input) -> usize {
40    input.iter().flatten().filter(|&&d| matches!(d, 1 | 4 | 7 | 8)).count()
41}
42
43pub fn part2(input: &Input) -> u32 {
44    input.iter().map(|[a, b, c, d]| 1000 * a + 100 * b + 10 * c + d).sum()
45}
46
47fn descramble(line: &str) -> [u32; 4] {
48    let mut freq = [0_u8; 104];
49    let bytes = line.as_bytes();
50    bytes[..58].iter().for_each(|&b| freq[b as usize] += 1);
51    bytes[61..]
52        .split(|&b| b == b' ')
53        .map(|scrambled| to_digit(scrambled.iter().map(|&b| freq[b as usize]).sum()))
54        .chunk::<4>()
55        .next()
56        .unwrap()
57}
58
59fn to_digit(total: u8) -> u32 {
60    match total {
61        42 => 0,
62        17 => 1,
63        34 => 2,
64        39 => 3,
65        30 => 4,
66        37 => 5,
67        41 => 6,
68        25 => 7,
69        49 => 8,
70        45 => 9,
71        _ => unreachable!(),
72    }
73}