2021-12-08 13:42:35 +01:00
#![ feature(array_from_fn) ]
2021-12-08 14:07:44 +01:00
#![ feature(array_zip) ]
2021-12-08 13:42:35 +01:00
#![ feature(test) ]
extern crate test ;
use aoc2021 ::common ::* ;
2021-12-08 20:10:16 +01:00
use itertools ::Itertools ;
use std ::array ;
2021-12-08 13:42:35 +01:00
2021-12-08 16:54:07 +01:00
const DAY : usize = 8 ;
2021-12-09 00:47:30 +01:00
type Parsed = Vec < ( [ Ssd ; 10 ] , [ Ssd ; 4 ] ) > ;
2021-12-08 13:42:35 +01:00
2021-12-09 00:47:30 +01:00
const VALID_DISPLAYS : [ Ssd ; 10 ] = [ 119 , 36 , 93 , 109 , 46 , 107 , 123 , 37 , 127 , 111 ] ;
2021-12-08 13:42:35 +01:00
2021-12-09 00:47:30 +01:00
type Ssd = u32 ;
2021-12-08 13:42:35 +01:00
2021-12-09 00:47:30 +01:00
struct Mapping ( [ Ssd ; 7 ] ) ;
2021-12-08 13:42:35 +01:00
impl Mapping {
2021-12-09 00:47:30 +01:00
fn translate ( & self , i : usize ) -> Ssd {
2021-12-08 20:49:05 +01:00
1 < < self . 0 [ i ]
2021-12-08 13:42:35 +01:00
}
}
2021-12-09 00:47:30 +01:00
const INPUT_MASK : [ Ssd ; 8 ] = [ 1 , 2 , 4 , 8 , 16 , 32 , 64 , 128 ] ;
fn parse ( s : & str ) -> Ssd {
s . bytes ( ) . map ( | b | INPUT_MASK [ ( b - b 'a' ) as usize ] ) . sum ( )
2021-12-08 13:42:35 +01:00
}
2021-12-09 00:47:30 +01:00
fn bit_at ( x : Ssd , n : usize ) -> bool {
2021-12-08 20:10:16 +01:00
( x > > n ) & 1 ! = 0
}
2021-12-09 00:47:30 +01:00
fn difference ( lhs : Ssd , rhs : Ssd ) -> Ssd {
2021-12-08 20:10:16 +01:00
lhs & ! rhs
2021-12-08 13:42:35 +01:00
}
fn parse_input ( raw : & str ) -> Parsed {
raw . lines ( )
. map ( | l | l . split_once ( " | " ) . unwrap ( ) )
. map ( | ( input , output ) | {
let mut input = input . split ( ' ' ) . map_into ( ) ;
let mut output = output . split ( ' ' ) . map_into ( ) ;
2021-12-08 20:10:16 +01:00
( array ::from_fn ( | _ | parse ( input . next ( ) . unwrap ( ) ) ) , array ::from_fn ( | _ | parse ( output . next ( ) . unwrap ( ) ) ) )
2021-12-08 13:42:35 +01:00
} )
. collect ( )
}
2021-12-08 20:49:05 +01:00
fn part1 ( parsed : & Parsed ) -> usize {
parsed . iter ( ) . flat_map ( | ( _ , output ) | output ) . filter ( | input | [ 2 , 3 , 4 , 7 ] . contains ( & input . count_ones ( ) ) ) . count ( )
2021-12-08 13:42:35 +01:00
}
2021-12-08 20:49:05 +01:00
fn part2 ( parsed : & Parsed ) -> usize {
2021-12-08 13:42:35 +01:00
parsed
. iter ( )
2021-12-08 20:49:05 +01:00
. map ( | ( input , output ) | {
2021-12-08 20:10:16 +01:00
let [ & one , & four , & seven ] = [ 2 , 4 , 3 ] . map ( | n | input . iter ( ) . find ( | s | s . count_ones ( ) = = n ) . unwrap ( ) ) ;
2021-12-08 13:42:35 +01:00
// We know the position of a for sure because it’s the only difference between 7 and 1
2021-12-08 20:10:16 +01:00
let a = ( 0 .. 7 ) . position ( | n | bit_at ( difference ( seven , one ) , n ) ) . unwrap ( ) ;
2021-12-08 14:03:12 +01:00
// And c and f are these two (both used in 1).
2021-12-08 20:10:16 +01:00
let ( c , f ) = ( 0 .. 7 ) . positions ( | n | bit_at ( one , n ) ) . next_tuple ( ) . unwrap ( ) ;
2021-12-08 20:49:05 +01:00
// Determine which is which by their frequency in the input.
let ( c , f ) = if input . iter ( ) . filter ( | & & i | bit_at ( i , c ) ) . count ( ) = = 8 { ( c , f ) } else { ( f , c ) } ;
2021-12-08 14:03:12 +01:00
// 4 uses b, c, d, f, but we already know c and f from 1, so this leaves b and d.
2021-12-08 20:10:16 +01:00
let ( b , d ) = ( 0 .. 7 ) . positions ( | n | bit_at ( difference ( four , one ) , n ) ) . next_tuple ( ) . unwrap ( ) ;
2021-12-08 20:49:05 +01:00
let ( b , d ) = if input . iter ( ) . filter ( | & & i | bit_at ( i , b ) ) . count ( ) = = 6 { ( b , d ) } else { ( d , b ) } ;
2021-12-08 14:03:12 +01:00
// Now e and g have to be in the remaining two positions.
2021-12-08 20:49:05 +01:00
let ( e , g ) = ( 0 .. 7 ) . filter ( | n | ! [ a , b , c , d , f ] . contains ( n ) ) . map_into ( ) . next_tuple ( ) . unwrap ( ) ;
let ( e , g ) = if input . iter ( ) . filter ( | & & i | bit_at ( i , e ) ) . count ( ) = = 4 { ( e , g ) } else { ( g , e ) } ;
let mut m = [ 0 ; 7 ] ;
let mut cur = 0 ;
2021-12-09 00:47:30 +01:00
#[ allow(clippy::explicit_counter_loop) ] // it’s faster this way
2021-12-08 20:49:05 +01:00
for i in [ a , b , c , d , e , f , g ] {
2021-12-09 00:47:30 +01:00
// We know they’re all in range, and this is actually a few % faster.
unsafe { * m . get_unchecked_mut ( i ) = cur } ;
2021-12-08 20:49:05 +01:00
cur + = 1 ;
}
let mapping = Mapping ( m ) ;
output
2021-12-08 13:42:35 +01:00
. iter ( )
2021-12-09 00:47:30 +01:00
. map ( | & i | ( 0 .. 7 ) . map ( | n | ( bit_at ( i , n ) as Ssd ) * mapping . translate ( n ) ) . sum ( ) )
. map ( | ssd : Ssd | VALID_DISPLAYS . iter ( ) . position ( | d | & ssd = = d ) . unwrap ( ) )
2021-12-08 13:42:35 +01:00
. fold ( 0 , | acc , n | ( acc + n ) * 10 )
/ 10
} )
. sum ( )
}
fn main ( ) {
let raw = read_file ( DAY ) ;
let input = parse_input ( & raw ) ;
println! ( " Part 1: {} " , part1 ( & input ) ) ;
println! ( " Part 2: {} " , part2 ( & input ) ) ;
}
#[ cfg(test) ]
mod tests {
use super ::* ;
use aoc2021 ::* ;
const TEST_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 " ;
2021-12-08 20:10:16 +01:00
#[ test ]
fn test_parse ( ) {
assert_eq! ( parse ( " cgeb " ) , 86 ) ;
}
2021-12-08 13:42:35 +01:00
test! ( part1 ( ) = = 26 ) ;
test! ( part2 ( ) = = 61229 ) ;
bench! ( part1 ( ) = = 239 ) ;
bench! ( part2 ( ) = = 946346 ) ;
bench_input! ( Vec ::len = > 200 ) ;
}