module Commons where import GHC.IO.Handle (isEOF) import Data.Char (isDigit) data Coordinates = Coordinates { x :: Int, y :: Int } deriving (Eq, Show) data NumberPart = NumberPart { value :: Int, numberLength :: Int, neighbors :: [Coordinates] } deriving Show data SymbolPart = SymbolPart { symbol :: Char, coordinates :: Coordinates } deriving Show data Engine = Engine { numbers :: [NumberPart], symbols :: [SymbolPart] } deriving Show parseNumberPartNeighborsIn :: Int -> Int -> Int -> [Coordinates] parseNumberPartNeighborsIn row column length | length > 0 = Coordinates {x = column + length - 1, y = row - 1}: Coordinates {x = column + length - 1, y = row + 1}: parseNumberPartNeighborsIn row column (length - 1) | length == 0 = [] parseNumberPartNeighbors :: Int -> Int -> Int -> [Coordinates] parseNumberPartNeighbors row column length = Coordinates {x = column - 1, y = row - 1}: Coordinates {x = column - 1, y = row}: Coordinates {x = column - 1, y = row+1}: Coordinates {x = column + length, y = row - 1}: Coordinates {x = column + length, y = row }: Coordinates {x = column + length, y = row + 1}: parseNumberPartNeighborsIn row column length parseNumberPartValue :: String -> String parseNumberPartValue (h: t) | isDigit h = h: parseNumberPartValue t parseNumberPartValue _ = [] parseNumberPart :: Int -> Int -> String -> NumberPart parseNumberPart row column rawNumber = let value = parseNumberPartValue rawNumber l = length value in NumberPart {value = read value, numberLength = l, neighbors = parseNumberPartNeighbors row column l} parseLine :: Int -> Int -> String -> Engine -> Engine parseLine _ _ [] engine = engine parseLine row column (h: t) engine | isDigit h = let newNumberPart = parseNumberPart row column (h: t) newNumberLength = numberLength newNumberPart in parseLine row (column + newNumberLength) (drop (newNumberLength - 1) t) engine {numbers = newNumberPart: numbers engine} | h /= '.' = let newSymbolPart = SymbolPart { symbol = h, coordinates = Coordinates {x = column, y = row} } in parseLine row (column + 1) t engine {symbols = newSymbolPart: symbols engine} | otherwise = parseLine row (column + 1) t engine parseEngine :: Int -> Engine -> IO Engine parseEngine row engine = do done <- isEOF if done then return engine else do line <- getLine let newEngine = parseLine row 1 line engine parseEngine (row+1) newEngine parse :: IO Engine parse = parseEngine 1 Engine {numbers = [], symbols = []}