diff --git a/day03.hs b/day03.hs new file mode 100644 index 0000000..8dd13c7 --- /dev/null +++ b/day03.hs @@ -0,0 +1,86 @@ +import Control.Monad (guard) +import Data.Char (digitToInt, isDigit) +import Data.Text qualified as T +import Lib (readFile') + +-- i hate this problem, let's just do an ugly recursion and be done with it +type Symbol = Int + +data Number = Number + { value :: Int, + start :: Int, + end :: Int + } + deriving (Show) + +parseLines :: [T.Text] -> ([[Symbol]], [[Number]]) +parseLines lines = + foldl + ( \(symbols, numbers) line -> + let (symbols', numbers') = parseLine line + in (symbols' : symbols, numbers' : numbers) + ) + ([], []) + lines + +-- ugly, i know but runs p fast with -O2 +parseLine :: T.Text -> ([Symbol], [Number]) +parseLine line = + let (num, start, end, isNum, symbols, numbers) = T.foldl fn (0, 0, 0, False, [], []) line + in (symbols, if isNum then Number num start (end - 1) : numbers else numbers) + where + fn :: (Int, Int, Int, Bool, [Symbol], [Number]) -> Char -> (Int, Int, Int, Bool, [Symbol], [Number]) + fn (num, start, end, isNum, symbols, numbers) char = + let isNum' = isDigit char + num' = if isNum' then num * 10 + digitToInt char else 0 + start' = if not isNum' then end + 1 else start + symbols' = + if char /= '.' && not isNum' + then end : symbols + else symbols + numbers' = + if isNum && not isNum' + then Number num start (end - 1) : numbers + else numbers + in (num', start', end + 1, isNum', symbols', numbers') + +groups3 :: [a] -> [[a]] +groups3 xs = take 3 xs : groups3 (tail xs) + +first :: [[Symbol]] -> [[Number]] -> [[Int]] +first symbols numbers = + let zipped = zip numbers (init $ groups3 ([] : symbols)) + in do + (numbersRow, neighbours) <- zipped + return $ do + Number value start end <- numbersRow + guard $ or $ do + neighboursRow <- neighbours + return $ or $ do + symbol <- neighboursRow + return $ symbol >= start - 1 && symbol <= end + 1 + return value + +second :: [[Symbol]] -> [[Number]] -> [Int] +second symbols numbers = + let zipped = zip symbols (init $ groups3 ([] : numbers)) + in do + (symbols, neighbours) <- zipped + symbol <- symbols + let values = take 2 $ do + neighboursRow <- neighbours + Number value start end <- neighboursRow + guard $ symbol >= start - 1 + guard $ symbol <= end + 1 + return value + guard $ length values == 2 + return $ product values + +main :: IO () +main = do + input <- T.lines <$> readFile' "day03.in" + let (symbols, numbers) = parseLines input + putStr "Q1: " + print . sum . concat $ first symbols numbers + putStr "Q2: " + print . sum $ second symbols numbers