86
day03.hs
Normal file
86
day03.hs
Normal file
@@ -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
|
Reference in New Issue
Block a user