87 lines
2.6 KiB
Haskell
87 lines
2.6 KiB
Haskell
|
import Data.List
|
||
|
import Data.List.Split
|
||
|
import Debug.Trace
|
||
|
|
||
|
main :: IO ()
|
||
|
main = do
|
||
|
input <- readFile "8/input.txt"
|
||
|
let x = map parse $ lines input
|
||
|
-- Problem 1
|
||
|
print $ f x
|
||
|
-- Problem 2
|
||
|
print $ g x
|
||
|
where
|
||
|
f = length . filter (`elem` [2, 3, 4, 7]) . map length . (>>= snd)
|
||
|
g x = sum $ zipWith (curry decode) (map snd x) (map deduce x)
|
||
|
|
||
|
-- | Parse a line to a tuple of signal values and output values.
|
||
|
-- | The signal values and output values are sorted.
|
||
|
parse :: String -> (Signal, Output)
|
||
|
parse s = let (a, b) = break (== '|') s
|
||
|
signal = map sort $ filter (/= "|") $ words a
|
||
|
output = map sort $ filter (/= "|") $ words b in
|
||
|
(signal, output)
|
||
|
|
||
|
type Signal = [String]
|
||
|
type Output = [String]
|
||
|
|
||
|
type Mapping = [Result]
|
||
|
|
||
|
data Result = Missing String
|
||
|
| Matched Int String
|
||
|
deriving
|
||
|
Show
|
||
|
|
||
|
-- | Extract the segment combination belonging to the given number.
|
||
|
get :: Mapping -> Int -> String
|
||
|
get m i = head [ s | Matched n s <- m, n == i ]
|
||
|
|
||
|
-- | Extract the number belonging to the given segment combination.
|
||
|
seg :: Mapping -> String -> Int
|
||
|
seg m v = head [ n | Matched n s <- m, s == v ]
|
||
|
|
||
|
-- | Deduce what segments correspond to which number. The number
|
||
|
-- | the combination belongs to is its index.
|
||
|
deduce :: (Signal, Output) -> Mapping
|
||
|
deduce (signal, output) = foldl match (map Missing signal) [ 1, 7, 4, 8, 9, 6, 0, 5, 3, 2 ]
|
||
|
|
||
|
-- | Use the given `Mapping` to match an unmatched set of segments
|
||
|
-- | to the given number.
|
||
|
match :: Mapping -> Int -> Mapping
|
||
|
match m n = match' n pred <$> m
|
||
|
where
|
||
|
pred = matcher n m
|
||
|
match' _ _ v @ (Matched _ _) = v
|
||
|
match' n condition (Missing a)
|
||
|
| condition a = Matched n a
|
||
|
| otherwise = Missing a
|
||
|
|
||
|
|
||
|
-- | Map a number to a function that can be used to match that number.
|
||
|
matcher :: Int -> Mapping -> (String -> Bool)
|
||
|
matcher 0 = p
|
||
|
where p _ s = length s == 6
|
||
|
matcher 1 = p
|
||
|
where p _ s = length s == 2
|
||
|
matcher 2 = p
|
||
|
where p _ _ = True
|
||
|
matcher 3 = p
|
||
|
where p m s = s == (s `intersect` get m 9)
|
||
|
matcher 4 = p
|
||
|
where p _ s = length s == 4
|
||
|
matcher 5 = p
|
||
|
where p m s = s == (s `intersect` get m 6)
|
||
|
matcher 6 = p
|
||
|
where p m s = length s == 6 && get m 1 /= (get m 1 `intersect` s)
|
||
|
matcher 7 = p
|
||
|
where p _ s = length s == 3
|
||
|
matcher 8 = p
|
||
|
where p _ s = length s == 7
|
||
|
matcher 9 = p
|
||
|
where p m s = get m 4 == get m 4 `intersect` s
|
||
|
|
||
|
-- | Given a list of output values and a segment mapping,
|
||
|
-- | decode what the output reads.
|
||
|
decode :: (Output, Mapping) -> Int
|
||
|
decode (output, mapping) = read (concatMap (show . seg mapping) output)
|