advent-of-code-2023/src/Day3.hs

126 lines
4.0 KiB
Haskell
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module Day3 where
import Data.Char (isDigit)
import Data.Matrix (Matrix(..))
import Data.Maybe
import qualified Data.Char as C
import qualified Data.Matrix as M
import qualified Data.Vector as V
import qualified Data.Bifunctor as BF
-- import Debug.Trace
main :: IO ()
main = do
m <- M.fromLists . lines <$> readFile "inputs/day3.input"
putStrLn "Part 1 result:"
print $ solvePart1 m (symbolZone m)
putStrLn "Part 2 result:"
print $ solvePart2 m
newtype Symbol = Symbol Bool
deriving Eq
instance Show Symbol where
show (Symbol True) = "X"
show (Symbol False) = ""
solvePart2 :: Matrix Char -> Int
solvePart2 = sum . cogs
countParts :: (Int, Int) -> Matrix Char -> Int
countParts (x,y) = sum
. map tallyRow
. fmap (fmap justDigits)
. flip neighborList (x,y)
justDigits :: Maybe Char -> Maybe Char
justDigits (Just d) | isDigit d = Just d
justDigits _ = Nothing
tallyRow :: [Maybe Char] -> Int
tallyRow [ Just _ , Just _ , Just _ ] = 1
tallyRow [ Nothing , Just _ , Just _ ] = 1
tallyRow [ Just _ , Nothing , Just _ ] = 2
tallyRow [ Nothing , Nothing , Just _ ] = 1
tallyRow [ Just _ , Just _ , Nothing ] = 1
tallyRow [ Nothing , Just _ , Nothing ] = 1
tallyRow [ Just _ , Nothing , Nothing ] = 1
tallyRow [ Nothing , Nothing , Nothing ] = 0
tallyRow _ = error "This should never happen"
neighborList :: Matrix a -> (Int, Int) -> [[Maybe a]]
neighborList m (x,y) =
[ [ M.safeGet (x-1) (y-1) m , M.safeGet (x-1) y m , M.safeGet (x-1) (y+1) m ]
, [ M.safeGet x (y-1) m , M.safeGet x y m , M.safeGet x (y+1) m ]
, [ M.safeGet (x+1) (y-1) m , M.safeGet (x+1) y m , M.safeGet (x+1) (y+1) m ]
]
cogs :: Matrix Char -> Matrix Int
cogs m = M.mapPos go m
where
go p e | e == '*' =
if (== 2) $ countParts p m
then product $ numbersAround p m
else 0
go _ _ = 0
numbersAround :: Read b => (Int, Int) -> Matrix Char -> [b]
numbersAround (x,y) m =
concatMap (map (read . V.toList . snd) . filter (\ (p,_) -> V.any (\ y' -> y' >= y-1 && y' <= y+1) p)
. filter (V.all isDigit . snd)
. collectDigitAndCoord) rows
where
rows = [M.getRow (x-1) m,M.getRow x m,M.getRow (x+1) m]
collectDigitAndCoord v =
map (BF.first (V.map (+ 1)) . V.unzip) $ V.groupBy (\ (_,v1) (_,v2) -> isDigit v1 && isDigit v2) $ V.indexed v
solvePart1 :: Matrix Char -> Matrix Symbol -> Int
solvePart1 m = sum . map read . words
. M.toList
. mask m
. go' . go'
where
mask = M.elementwise (\ em es -> if symbolAndDigit (em,es) then em else ' ')
go' s = M.mapPos (go s) s
go _ _ (Symbol True) = Symbol True
go s' (x,y) _ = Symbol $
any symbolAndDigit (zip (neighborList' (x,y) m) (neighborList' (x,y) s'))
neighborList' (x,y) m' =
catMaybes [ M.safeGet x (y-1) m'
, M.safeGet x (y+1) m'
]
symbolAndDigit (v, Symbol True) | C.isDigit v = True
symbolAndDigit _ = False
_noSymbolAndDigit (v, Symbol False) | C.isDigit v = True
_noSymbolAndDigit _ = False
symbolZone :: Matrix Char -> Matrix Symbol
symbolZone m = M.mapPos go m
where
go (x,y) _ = Symbol $ any isSymbol (neighborList' (x,y))
neighborList' :: (Int,Int) -> [Char]
neighborList' (x,y) = catMaybes [ M.safeGet (x-1) y m
, M.safeGet x y m
, M.safeGet (x+1) y m
, M.safeGet (x-1) (y+1) m
, M.safeGet x (y+1) m
, M.safeGet (x+1) (y+1) m
, M.safeGet (x-1) (y-1) m
, M.safeGet x (y-1) m
, M.safeGet (x+1) (y-1) m
]
isSymbol :: Char -> Bool
isSymbol '#' = True
isSymbol '$' = True
isSymbol '%' = True
isSymbol '&' = True
isSymbol '*' = True
isSymbol '+' = True
isSymbol '-' = True
isSymbol '/' = True
isSymbol '=' = True
isSymbol '@' = True
isSymbol _ = False