I hate day 8 so much

This commit is contained in:
Joseph Montanaro 2021-12-12 15:53:05 -08:00
parent 3dc38678ff
commit 24b88815f2

View File

@ -6,7 +6,7 @@ use eyre::eyre;
use crate::lib::{ParseLines, IterExt};
#[derive(Debug)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum Segment {
A = 1,
B = 2,
@ -40,6 +40,19 @@ impl Segment {
}
}
fn from_ord(n: u8) -> eyre::Result<Self> {
match n {
0 => Ok(A),
1 => Ok(B),
2 => Ok(C),
3 => Ok(D),
4 => Ok(E),
5 => Ok(F),
6 => Ok(G),
_ => Err(eyre!("Invalid ordinal for segment: {}", n)),
}
}
fn from_value(n: u8) -> eyre::Result<Self> {
match n {
1 => Ok(A),
@ -61,10 +74,6 @@ struct SegmentSet {
}
impl SegmentSet {
fn new_filled() -> Self {
SegmentSet {data: 255}
}
fn contains(&self, segment: Segment) -> bool {
self.data & (segment as u8) > 0
}
@ -73,20 +82,12 @@ impl SegmentSet {
self.data |= segment as u8;
}
fn remove(&mut self, segment: Segment) {
self.data ^= segment as u8;
}
fn len(&self) -> u8 {
(0..7).fold(0, |total, i| total + ((self.data >> i) & 1))
}
fn union(&self, other: &Self) -> Self {
SegmentSet {data: self.data | other.data}
}
fn intersect(&self, other: &Self) -> Self {
SegmentSet {data: self.data & other.data}
fn exclusion(&self, other: &Self) -> Self {
SegmentSet {data: self.data ^ other.data}
}
fn iter(&self) -> SegmentSetIter {
@ -149,25 +150,117 @@ impl FromStr for Screen {
}
// mapping of Segment -> SegmentSet, represents
struct Candidates {
signal: Segment,
possible: SegmentSet
fn segment_counts(screen: &Screen) -> [usize; 7] {
let mut counts = [0; 7];
for i in 0..7 {
let segment = Segment::from_ord(i).unwrap();
counts[i as usize] = screen.patterns.iter()
.filter(|p| p.contains(segment))
.count()
}
counts
}
const DIGIT_SEGMENTS: [SegmentSet; 10] = [ // magic numbers, fun!
SegmentSet {data: 0b01110111},
SegmentSet {data: 0b00100100},
SegmentSet {data: 0b01011101},
SegmentSet {data: 0b01101101},
SegmentSet {data: 0b00101110},
SegmentSet {data: 0b01101011},
SegmentSet {data: 0b01111011},
SegmentSet {data: 0b00100101},
SegmentSet {data: 0b01111111},
SegmentSet {data: 0b01101111},
];
fn find_a(screen: &Screen) -> Segment {
// figure out which segment is currently attached to the "A" wire
let mut seven = None;
let mut one = None;
for p in screen.patterns {
if p.len() == 2 {
one = Some(p); // the only digit that uses 2 segments is 1
}
else if p.len() == 3 {
seven = Some(p); // similarly for 7
}
}
let result = seven.unwrap().exclusion(&one.unwrap());
if result.len() != 1 {
panic!("This should never happen");
}
result.iter().next().unwrap()
}
fn solve(screen: &Screen) -> [Segment; 7] {
let mut result = [A; 7]; // A is just a placeholder value
let four_pattern = screen.patterns.iter().find(|p| p.len() == 4).unwrap();
for (i, count) in segment_counts(screen).iter().enumerate() {
let possible = match count {
4 => Some(E),
6 => Some(B),
7|8 => None,
9 => Some(F),
_ => unreachable!(),
};
if let Some(segment) = possible {
result[i] = segment;
}
else if *count == 7 { // 7 appearances means either D or G
let segment = Segment::from_ord(i as u8).unwrap();
if four_pattern.contains(segment) {
result[i] = D;
}
else {
result[i] = G;
}
}
else if *count == 8 { // similarly 8 should be either A or C
let segment = Segment::from_ord(i as u8).unwrap();
if segment == find_a(screen) {
result[i] = A;
}
else {
result[i] = C;
}
}
}
result
}
fn decode(output: [SegmentSet; 4], map: [Segment; 7]) -> usize {
let mut result = 0;
for (i, segments_enc) in output.iter().enumerate() {
let mut segments_dec = SegmentSet::default();
for segment_enc in segments_enc.iter() {
let segment_dec = map[segment_enc.to_ord()];
segments_dec.insert(segment_dec)
}
let digit = match segments_dec.data {
0b01110111 => 0,
0b00100100 => 1,
0b01011101 => 2,
0b01101101 => 3,
0b00101110 => 4,
0b01101011 => 5,
0b01111011 => 6,
0b00100101 => 7,
0b01111111 => 8,
0b01101111 => 9,
_ => {
dbg!(segments_dec);
panic!("Invalid segment sequence");
},
};
result += digit * 10usize.pow(3 - (i as u32));
}
result
}
fn part2(screens: &[Screen]) -> usize {
let mut result = 0;
for screen in screens {
let map = solve(screen);
result += decode(screen.output, map);
}
result
}
fn part1(screens: &[Screen]) -> usize {
@ -183,7 +276,5 @@ pub fn run(data: &str) -> eyre::Result<(usize, usize)> {
.parse_lines::<Screen>()
.collect::<Result<Vec<_>, _>>()?;
Ok((part1(&screens), 0))
Ok((part1(&screens), part2(&screens)))
}