adventofcode-2021/8/displayfixer.hs

131 lines
4.8 KiB
Haskell
Raw Normal View History

2021-12-11 01:22:44 +00:00
import Data.List (group, minimumBy, sort, sortBy)
import qualified Data.Map.Strict as Map
import Data.Maybe (mapMaybe)
import Data.Ord (comparing)
import qualified Data.Set as Set
import Parsing (splitByString)
type Record = ([Set.Set Char], [Set.Set Char])
main :: IO ()
main = do
input <- getContents
let
records = parseRecords input
(putStrLn . show . solution1) records
(putStrLn . show . solution2) records
solution1 :: [Record] -> Int
solution1 = sum . (map countEasy)
solution2 :: [Record] -> Int
solution2 = sum . (map getNum)
where
getNum :: Record -> Int
getNum (wires, display) = sum (map (uncurry (*)) (zip [1000, 100, 10, 1] digits))
where
digits :: [Int]
digits = map (\d -> (Map.!) mapping d) display
mapping :: Map.Map (Set.Set Char) Int
mapping = findSegments wires
countEasy :: Record -> Int
countEasy record = length (mapMaybe checkDigit (snd record))
where
checkDigit :: Set.Set Char -> Maybe Int
checkDigit d
| len == 2 = Just 1
| len == 4 = Just 4
| len == 3 = Just 7
| len == 7 = Just 8
| otherwise = Nothing
where len = length d
findSegments :: [Set.Set Char] -> Map.Map (Set.Set Char) Int
findSegments w =
let
wires = sortBy (comparing Set.size) w
unknown = (map snd (filter isKnown (zip [0..] wires)))
where
isKnown :: (Int, Set.Set Char) -> Bool
isKnown (i, _)
| i == 0 = False -- 1
| i == 1 = False -- 7
| i == 2 = False -- 4
| i == 9 = False -- 8
| otherwise = True
segmentA = (wires !! 1) `Set.difference` (wires !! 0) -- 7 - 1
segmentB = (wires !! 2) `Set.difference` ((wires !! 0) `Set.union` segmentD) -- 4 - 1 + d
segmentC = least cf
where
abdeg = unionAll [segmentA, segmentB, segmentD, segmentE, segmentG]
cf = filter notTwo (map (`Set.difference` abdeg) unknown)
notTwo wire =
(Set.size wire == 1)
segmentD = head (filter notZero (map (Set.difference (wires !! 9)) unknown))
where
notZero wire =
(Set.size wire == 1)
&& ((wire `Set.difference` segmentE) /= Set.empty)
&& ((wire `Set.difference` (wires !! 0) /= Set.empty))
segmentE = (wires !! 9) `Set.difference`
((wires !! 1) `Set.union`
(wires !! 2) `Set.union`
segmentG) -- 8 - (4 + 7 + a)
segmentF = (wires !! 0) `Set.difference` segmentC
segmentG = (foldr1 Set.intersection unknown) `Set.difference` segmentA
in
Map.fromList [
(unionAll [segmentA, segmentB, segmentC, segmentE, segmentF, segmentG], 0),
(wires !! 0, 1),
(unionAll [segmentA, segmentC, segmentD, segmentE, segmentG], 2),
(unionAll [segmentA, segmentC, segmentD, segmentF, segmentG], 3),
(wires !! 2, 4),
(unionAll [segmentA, segmentB, segmentD, segmentF, segmentG], 5),
(unionAll [segmentA, segmentB, segmentD, segmentE, segmentF, segmentG], 6),
(wires !! 1, 7),
(wires !! 9, 8),
(unionAll [segmentA, segmentB, segmentC, segmentD, segmentF, segmentG], 9)
]
parseRecords :: String -> [Record]
parseRecords = map parseRecord . lines
parseRecord :: String -> Record
parseRecord = splitTuple . splitHalf
where
splitTuple :: (String, String) -> Record
splitTuple (patterns, output) =
(map Set.fromList (splitByString " " patterns), map Set.fromList (splitByString " " output))
splitHalf :: String -> (String, String)
splitHalf string =
let
halves = splitByString " | " string
in
((head halves), (last halves))
least :: Ord a => [a] -> a
least = head . (minimumBy (comparing length)) . group . sort
unionAll :: Ord a => [Set.Set a] -> Set.Set a
unionAll = foldr1 Set.union
-- Tests
testInput1 = "acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab | cdfeb fcadb cdfeb cdbaf"
testInput2 = unlines [
"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"
]
test1 = solution1 (parseRecords testInput2)
test2 = solution2 (parseRecords testInput2)