advent-of-code/2023/haskell/solutions/Day03.hs

97 lines
2.8 KiB
Haskell
Raw Normal View History

2023-12-03 13:12:15 +01:00
{-# LANGUAGE OverloadedStrings #-}
module Day03 (parse, solveA, solveB) where
2023-12-03 19:34:55 +01:00
import Data.Bifunctor (first, second)
2023-12-03 13:12:15 +01:00
import Data.Void (Void)
import Data.Char (digitToInt, isSpace)
import Data.Text (Text)
import Text.Megaparsec (errorBundlePretty, Parsec, runParser, many, eof, choice, satisfy, manyTill)
import Text.Megaparsec.Char (digitChar, char)
2023-12-03 19:34:55 +01:00
import Data.Maybe (mapMaybe)
2023-12-03 13:12:15 +01:00
data Item
= Digit Int
| Symbol Char
| Empty
deriving (Show, Eq)
2023-12-03 19:34:55 +01:00
type Grid = [[Item]]
type Row = Int
type Col = Int
2023-12-03 13:12:15 +01:00
type Parser = Parsec Void Text
2023-12-03 19:34:55 +01:00
parse :: Text -> Either String Grid
2023-12-03 13:12:15 +01:00
parse = first errorBundlePretty . runParser full ""
where
item :: Parser Item
item = choice
[ Digit . digitToInt <$> digitChar
, Empty <$ char '.'
, Symbol <$> satisfy (not . isSpace)
]
line :: Parser [Item]
line = item `manyTill` satisfy isSpace
full :: Parser [[Item]]
full = many line <* eof
2023-12-03 19:34:55 +01:00
findSpans :: (a -> a -> Bool) -> [a] -> [[a]]
findSpans = helper []
where
helper :: [[a]] -> (a -> a -> Bool) -> [a] -> [[a]]
helper acc _ [] = reverse . map reverse $ acc
helper [] p (x:xs) = helper [[x]] p xs
helper acc@(a:as) p (x:xs) =
if p (head a) x
then helper ((x:a):as) p xs
else helper ([x]:acc) p xs
withCoords :: [[a]] -> [((Row, Col), a)]
withCoords grid = [((r, c), a) | (r, row) <- zip [0..] grid, (c, a) <- zip [0..] row]
getSymbols :: Grid -> [(Row, Col)]
getSymbols = mapMaybe isSymbol . withCoords
where
isSymbol :: ((Row, Col), Item) -> Maybe (Row, Col)
isSymbol (rc, item) = case item of
Symbol _ -> Just rc
_ -> Nothing
getNumbers :: Grid -> [(Int, (Row, Col, Col))]
getNumbers = concat . zipWith processRow [0..]
where
getDigit :: (Col, Item) -> Maybe (Col, Int)
getDigit (i, item) = case item of
Digit d -> Just (i, d)
_ -> Nothing
isNext :: (Col, Int) -> (Col, Int) -> Bool
isNext a b = fst a + 1 == fst b
merge :: Row -> [(Col, Int)] -> (Int, (Row, Col, Col))
merge r items =
let n = foldl (\acc (_, d) -> 10 * acc + d) 0 items
s = minimum $ map fst items
e = maximum $ map fst items
in (n, (r, s, e))
processRow :: Row -> [Item] -> [(Int, (Row, Col, Col))]
processRow r = map (merge r) . findSpans isNext . mapMaybe getDigit . zip [0..]
getNeigbors :: (Row, Col, Col) -> [(Row, Col)]
getNeigbors (r, s, e) = [(r, s-1), (r, e+1)]
<> [(r-1, c) | c <- [s-1..e+1]]
<> [(r+1, c) | c <- [s-1..e+1]]
2023-12-03 13:12:15 +01:00
solveA :: [[Item]] -> Int
2023-12-03 19:34:55 +01:00
solveA grid =
let symbols = getSymbols grid
numbers = getNumbers grid
in sum . map fst . filter (hasSymbol symbols . snd) . map (second getNeigbors) $ numbers
where
hasSymbol :: [(Row, Col)] -> [(Row, Col)] -> Bool
hasSymbol symbols = any (`elem` symbols)
2023-12-03 13:12:15 +01:00
solveB :: [[Item]] -> Int
2023-12-03 19:34:55 +01:00
solveB = undefined
2023-12-03 13:12:15 +01:00