diff --git a/2021/src/day8.rs b/2021/src/day8.rs index 20b5f20..32bc3e1 100644 --- a/2021/src/day8.rs +++ b/2021/src/day8.rs @@ -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 { + 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 { 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::() .collect::, _>>()?; - - - Ok((part1(&screens), 0)) + Ok((part1(&screens), part2(&screens))) }