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