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