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-08 20:10:16 +01:00
type Parsed < ' a > = Vec < ( [ SSD ; 10 ] , [ SSD ; 4 ] ) > ;
2021-12-08 13:42:35 +01:00
2021-12-08 20:10:16 +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-08 20:10:16 +01:00
type SSD = u32 ;
2021-12-08 13:42:35 +01:00
2021-12-08 20:10:16 +01:00
struct Mapping ( [ SSD ; 7 ] ) ;
2021-12-08 13:42:35 +01:00
impl Mapping {
2021-12-08 20:10:16 +01:00
fn translate ( & self , i : SSD ) -> SSD {
1 < < self . 0 [ i as usize ]
2021-12-08 13:42:35 +01:00
}
}
2021-12-08 16:54:07 +01:00
fn parse ( s : & str ) -> SSD {
2021-12-08 20:10:16 +01:00
[ 'g' , 'f' , 'e' , 'd' , 'c' , 'b' , 'a' ] . iter ( ) . map ( | & c | s . contains ( c ) ) . fold ( 0 , | acc , b | ( acc | ( b as SSD ) ) < < 1 ) > > 1
2021-12-08 13:42:35 +01:00
}
2021-12-08 20:10:16 +01:00
fn bit_at ( x : SSD , n : SSD ) -> bool {
( x > > n ) & 1 ! = 0
}
fn difference ( lhs : SSD , rhs : SSD ) -> SSD {
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 ( )
}
fn part1 < ' a > ( parsed : & Parsed < ' a > ) -> usize {
2021-12-08 20:10:16 +01:00
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
}
fn part2 < ' a > ( parsed : & Parsed < ' a > ) -> usize {
parsed
. iter ( )
2021-12-08 20:10:16 +01:00
. map ( | ( input , raw_output ) | {
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
// Contrary to the name, these two values are both c_or_f,
2021-12-08 14:03:12 +01:00
// so we know c and f are these two, but we don’t know which is which.
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 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 14:03:12 +01:00
// Now e and g have to be in the remaining two positions.
let ( e , g ) = ( 0 .. 7 ) . filter ( | n | ! [ a , b , c , d , f ] . contains ( n ) ) . next_tuple ( ) . unwrap ( ) ;
2021-12-08 13:42:35 +01:00
// Now there are 8 possible combinations from multiplying the 3 x_or_y we constructed above.
2021-12-08 20:10:16 +01:00
// This is a manual implementation of itertools::iproduct specialized for 3 small
// arrays because it’s much faster this way.
let mapping = [ [ c , f ] , [ f , c ] ]
. into_iter ( )
. flat_map ( | [ c , f ] | [ [ c , f , b , d ] , [ c , f , d , b ] ] )
. flat_map ( | [ c , f , b , d ] | [ [ c , f , b , d , e , g ] , [ c , f , b , d , g , e ] ] )
. map ( | [ c , f , b , d , e , g ] | {
let mut m = [ 0 ; 7 ] ;
let mut cur = 0 ;
for i in [ a , b , c , d , e , f , g ] {
m [ i ] = cur ;
cur + = 1 ;
}
2021-12-08 14:03:12 +01:00
Mapping ( m )
2021-12-08 13:42:35 +01:00
} )
2021-12-08 20:10:16 +01:00
. find ( | m | input . iter ( ) . all ( | & i | VALID_DISPLAYS . contains ( & ( 0 .. 7 ) . map ( | n | ( bit_at ( i , n ) as SSD ) * m . translate ( n ) ) . sum ( ) ) ) )
2021-12-08 14:03:12 +01:00
. unwrap ( ) ;
2021-12-08 13:42:35 +01:00
raw_output
. iter ( )
2021-12-08 20:10:16 +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 ) ;
}