From fd46f6dfa216f036a4ab2ab836f74798f373eb53 Mon Sep 17 00:00:00 2001 From: RhiobeT Date: Sun, 3 Dec 2023 13:27:50 +0100 Subject: [PATCH] Day 03 --- advent-of-code-2023.cabal | 8 +++++ day03/Commons.hs | 62 +++++++++++++++++++++++++++++++++++++++ day03/Main.hs | 12 ++++++++ day03/Part1.hs | 18 ++++++++++++ day03/Part2.hs | 35 ++++++++++++++++++++++ 5 files changed, 135 insertions(+) create mode 100644 day03/Commons.hs create mode 100644 day03/Main.hs create mode 100644 day03/Part1.hs create mode 100644 day03/Part2.hs diff --git a/advent-of-code-2023.cabal b/advent-of-code-2023.cabal index 955e29b..9fde23c 100644 --- a/advent-of-code-2023.cabal +++ b/advent-of-code-2023.cabal @@ -20,3 +20,11 @@ executable day02 build-depends: base ^>=4.15.1.0, MissingH hs-source-dirs: day02 default-language: Haskell2010 + +executable day03 + main-is: Main.hs + other-modules: Commons Part1 Part2 + + build-depends: base ^>=4.15.1.0 + hs-source-dirs: day03 + default-language: Haskell2010 diff --git a/day03/Commons.hs b/day03/Commons.hs new file mode 100644 index 0000000..e2d01f5 --- /dev/null +++ b/day03/Commons.hs @@ -0,0 +1,62 @@ +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 = []} diff --git a/day03/Main.hs b/day03/Main.hs new file mode 100644 index 0000000..b6cd750 --- /dev/null +++ b/day03/Main.hs @@ -0,0 +1,12 @@ +module Main where + +import Commons +import qualified Part1 +import qualified Part2 + + +main = do engine <- parse + let part1Res = sum (Part1.getActualPartValuesNumbersFromEngine engine) + print part1Res + let part2Res = sum (Part2.getGearRatiosFromEngine engine) + print part2Res diff --git a/day03/Part1.hs b/day03/Part1.hs new file mode 100644 index 0000000..70f62b7 --- /dev/null +++ b/day03/Part1.hs @@ -0,0 +1,18 @@ +module Part1 where + +import Commons + + +isSymbolNeighbor :: [Coordinates] -> [Coordinates] -> Bool +isSymbolNeighbor [] _ = False +isSymbolNeighbor (h: t) symbolsCoordinates = h `elem` symbolsCoordinates || isSymbolNeighbor t symbolsCoordinates + +getActualPartNumbers :: [NumberPart] -> [Coordinates] -> [NumberPart] +getActualPartNumbers [] _ = [] +getActualPartNumbers (h: t) symbolsCoordinates + | isSymbolNeighbor (neighbors h) symbolsCoordinates = h: getActualPartNumbers t symbolsCoordinates + | otherwise = getActualPartNumbers t symbolsCoordinates + +getActualPartValuesNumbersFromEngine :: Engine -> [Int] +getActualPartValuesNumbersFromEngine engine = + map value (getActualPartNumbers (numbers engine) (map coordinates (symbols engine))) diff --git a/day03/Part2.hs b/day03/Part2.hs new file mode 100644 index 0000000..105ca3f --- /dev/null +++ b/day03/Part2.hs @@ -0,0 +1,35 @@ +module Part2 where + +import Commons + + +getNumberNeighbors :: Coordinates -> [NumberPart] -> Int +getNumberNeighbors _ [] = 0 +getNumberNeighbors symbolCoordinates (h: t) + | symbolCoordinates `elem` neighbors h = 1 + getNumberNeighbors symbolCoordinates t + | otherwise = getNumberNeighbors symbolCoordinates t + +isGear :: SymbolPart -> [NumberPart] -> Bool +isGear symbolPart numberParts | symbol symbolPart == '*' = getNumberNeighbors (coordinates symbolPart) numberParts == 2 + | otherwise = False + +getRatioNeighbors :: Coordinates -> [NumberPart] -> Int +getRatioNeighbors _ [] = 1 +getRatioNeighbors symbolCoordinates (h: t) + | symbolCoordinates `elem` neighbors h = value h * getRatioNeighbors symbolCoordinates t + | otherwise = getRatioNeighbors symbolCoordinates t + +getGearRatio :: SymbolPart -> [NumberPart] -> Int +getGearRatio symbolPart numberParts + | isGear symbolPart numberParts = getRatioNeighbors (coordinates symbolPart) numberParts + | otherwise = 0 + +getGearRatios :: [SymbolPart] -> [NumberPart] -> [Int] +getGearRatios [] _ = [] +getGearRatios (h: t) numberParts = let gearRatio = getGearRatio h numberParts + in if gearRatio > 0 + then gearRatio: getGearRatios t numberParts + else getGearRatios t numberParts + +getGearRatiosFromEngine :: Engine -> [Int] +getGearRatiosFromEngine engine = getGearRatios (symbols engine) (numbers engine)