aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Dessaux2023-04-05 00:05:09 +0200
committerJulien Dessaux2023-04-05 00:20:48 +0200
commit747786ba6f2ff303eb315003aa8cb0e74151679d (patch)
tree765a68abc0c93493aa3bf1f40d8b04623d3d41f0
parent2020-16 part 1 in haskell (diff)
downloadadvent-of-code-747786ba6f2ff303eb315003aa8cb0e74151679d.tar.gz
advent-of-code-747786ba6f2ff303eb315003aa8cb0e74151679d.tar.bz2
advent-of-code-747786ba6f2ff303eb315003aa8cb0e74151679d.zip
2020-16 part 2 in haskell
-rw-r--r--2020/16-Ticket_Translation/example211
-rw-r--r--2020/16-Ticket_Translation/second.hs113
2 files changed, 124 insertions, 0 deletions
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