From 747786ba6f2ff303eb315003aa8cb0e74151679d Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Wed, 5 Apr 2023 00:05:09 +0200 Subject: 2020-16 part 2 in haskell --- 2020/16-Ticket_Translation/example2 | 11 ++++ 2020/16-Ticket_Translation/second.hs | 113 +++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 2020/16-Ticket_Translation/example2 create mode 100644 2020/16-Ticket_Translation/second.hs (limited to '2020') diff --git a/2020/16-Ticket_Translation/example2 b/2020/16-Ticket_Translation/example2 new file mode 100644 index 0000000..3245817 --- /dev/null +++ b/2020/16-Ticket_Translation/example2 @@ -0,0 +1,11 @@ +departure class: 0-1 or 4-19 +departure row: 0-5 or 8-19 +seat: 0-13 or 16-19 + +your ticket: +11,12,13 + +nearby tickets: +3,9,18 +15,1,5 +5,14,9 diff --git a/2020/16-Ticket_Translation/second.hs b/2020/16-Ticket_Translation/second.hs new file mode 100644 index 0000000..12b55fe --- /dev/null +++ b/2020/16-Ticket_Translation/second.hs @@ -0,0 +1,113 @@ +-- requires cabal install --lib megaparsec parser-combinators +module Main (main) where +import Control.Monad (void, when) +import Data.List (foldl', isPrefixOf, transpose) +import Data.Map qualified as M +import Data.Void (Void) +import Text.Megaparsec +import Text.Megaparsec.Char +import System.Exit (die) + +exampleExpectedOutput = 132 + +data Range = Range { field :: String + , ll :: Int + , lh :: Int + , hl :: Int + , hh :: Int + } deriving (Show) +type Ticket = [Int] +data Input = Input { ranges :: [Range] + , myTicket :: Ticket + , nearbyTickets :: [Ticket] + } deriving (Show) + +type Parser = Parsec Void String + +parseInt :: Parser Int +parseInt = do + n <- some digitChar + void $ optional (char ',') + return $ read n + +parseRange :: Parser Range +parseRange = do + f <- some (letterChar <|> char ' ') + void $ string ": " + ll <- parseInt + void $ char '-' + lh <- parseInt + void $ string " or " + hl <- parseInt + void $ char '-' + hh <- parseInt + void $ char '\n' + return $ Range f ll lh hl hh + +parseTicket :: Parser Ticket +parseTicket = some parseInt <* char '\n' + +parseInput' :: Parser Input +parseInput' = do + r <- some parseRange + void $ string "\nyour ticket:\n" + t <- parseTicket + void $ string "\nnearby tickets:\n" + nts <- some parseTicket + void eof + return $ Input r t nts + +parseInput :: String -> IO Input +parseInput filename = do + input <- readFile filename + case runParser parseInput' filename input of + Left bundle -> die $ errorBundlePretty bundle + Right ops -> return ops + +filterInvalid :: Input -> Input +filterInvalid (Input ranges ticket nearbyTickets) = Input ranges ticket (foldl' compute' [] nearbyTickets) + where + compute' :: [Ticket] -> Ticket -> [Ticket] + compute' acc nt + | and $ map isValid nt = nt : acc + | otherwise = acc + isValid :: Int -> Bool + isValid n = or $ map (isValid' n) ranges + isValid' :: Int -> Range -> Bool + isValid' n (Range{ll, lh, hl, hh}) = (n >= ll && n <= lh) || (n >= hl && n <= hh) + +compute' :: Input -> Int +compute' Input{ranges, myTicket, nearbyTickets} = product . map (myTicket !!) . map snd . filter importantRange . snd $ iterate (potentialFieldIDsByRange, []) + where + importantRange :: (Range, Int) -> Bool + importantRange (Range{field}, _) = isPrefixOf "departure" field + nearbyTicketsByField = transpose nearbyTickets + iterate :: ([(Range, [Int])], [(Range, Int)]) -> ([(Range, [Int])], [(Range, Int)]) + iterate ([], res) = ([], res) + iterate ((r, [i]):ps, res) = iterate (map (removeID i) ps, (r, i):res) + iterate (p:ps, res) = iterate (ps ++ [p], res) + removeID :: Int -> (Range, [Int]) -> (Range, [Int]) + removeID i (r, l) = (r, filter (/= i) l) + potentialFieldIDsByRange :: [(Range, [Int])] + potentialFieldIDsByRange = zip ranges $ map (getMatchingFieldIDs myTicket nearbyTicketsByField) ranges + getMatchingFieldIDs :: Ticket -> [Ticket] -> Range -> [Int] + getMatchingFieldIDs [] [] _ = [] + getMatchingFieldIDs (_:ts) (fields:fieldss) range + | and (map (match range) fields) = myId : getMatchingFieldIDs ts fieldss range + | otherwise = getMatchingFieldIDs ts fieldss range + where + myId :: Int + myId = (length myTicket) - (length ts) - 1 + match :: Range -> Int -> Bool + match Range{ll, lh, hl, hh} n = (n >= ll && n <= lh) || (n >= hl && n <= hh) + +compute :: Input -> Int +compute input = compute' $ filterInvalid input + +main :: IO () +main = do + example <- parseInput "example2" + let exampleOutput = compute example + when (exampleOutput /= exampleExpectedOutput) (die $ "example failed: got " ++ show exampleOutput ++ " instead of " ++ show exampleExpectedOutput) + input <- parseInput "input" + print $ compute input -- cgit v1.2.3