Skip to content

Commit

Permalink
Day11 part1
Browse files Browse the repository at this point in the history
  • Loading branch information
cdimitroulas committed Dec 11, 2023
1 parent bf3c30a commit a002b1c
Show file tree
Hide file tree
Showing 11 changed files with 445 additions and 26 deletions.
21 changes: 19 additions & 2 deletions advent-of-code2023.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ library
Solutions.Day07
Solutions.Day08
Solutions.Day09
Solutions.Day11
other-modules:
Lib.AOC
Lib.Common
Expand All @@ -37,6 +38,7 @@ library
Lib.Matrix
Lib.Parsing
Lib.Range
Lib.SearchAlgorithms
Main
Solutions.DayX
Paths_advent_of_code2023
Expand Down Expand Up @@ -69,13 +71,17 @@ library
TypeOperators
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints
build-depends:
attoparsec
array
, attoparsec
, base >=4.7 && <5
, clock
, containers
, formatting
, hashable
, heap
, regex-tdfa
, text
, unordered-containers
, vector
default-language: Haskell2010

Expand All @@ -89,6 +95,7 @@ executable advent-of-code2023
Lib.Matrix
Lib.Parsing
Lib.Range
Lib.SearchAlgorithms
Solutions.Day01
Solutions.Day02
Solutions.Day03
Expand All @@ -98,6 +105,7 @@ executable advent-of-code2023
Solutions.Day07
Solutions.Day08
Solutions.Day09
Solutions.Day11
Solutions.DayX
Paths_advent_of_code2023
autogen-modules:
Expand Down Expand Up @@ -129,13 +137,17 @@ executable advent-of-code2023
TypeOperators
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints
build-depends:
attoparsec
array
, attoparsec
, base >=4.7 && <5
, clock
, containers
, formatting
, hashable
, heap
, regex-tdfa
, text
, unordered-containers
, vector
default-language: Haskell2010

Expand All @@ -152,6 +164,7 @@ test-suite unit-test
Day07
Day08
Day09
Day11
Paths_advent_of_code2023
autogen-modules:
Paths_advent_of_code2023
Expand Down Expand Up @@ -185,14 +198,18 @@ test-suite unit-test
tasty-discover:tasty-discover
build-depends:
advent-of-code2023
, array
, attoparsec
, base >=4.7 && <5
, clock
, containers
, formatting
, hashable
, heap
, regex-tdfa
, tasty
, tasty-hunit
, text
, unordered-containers
, vector
default-language: Haskell2010
140 changes: 140 additions & 0 deletions data/day11.txt

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,16 @@ default-extensions:

dependencies:
- base >= 4.7 && < 5
- array
- attoparsec
- clock
- containers
- formatting
- hashable
- heap
- regex-tdfa
- text
- unordered-containers
- vector

ghc-options:
Expand Down Expand Up @@ -74,6 +78,7 @@ library:
- Solutions.Day07
- Solutions.Day08
- Solutions.Day09
- Solutions.Day11

tests:
unit-test:
Expand Down
6 changes: 4 additions & 2 deletions src/Lib/AOC.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ runSolution day parser part1 part2 = do

putStrLn "Part 1:"
p1Start <- getTime Monotonic
print $ part1 input
putStr $ show $ part1 input
putStrLn ""
p1End <- getTime Monotonic
fprint (timeSpecs % "\n") p1Start p1End

putStrLn "Part 2:"
p2Start <- getTime Monotonic
print $ part2 input
putStr $ show $ part2 input
putStrLn ""
p2End <- getTime Monotonic
fprint (timeSpecs % "\n") p2Start p2End

Expand Down
7 changes: 6 additions & 1 deletion src/Lib/List.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module Lib.List (safeHead, setAt, (!?)) where
module Lib.List (safeHead, setAt, (!?), pairs) where

safeHead :: [a] -> Maybe a
safeHead [] = Nothing
Expand All @@ -15,3 +15,8 @@ xs !? n

setAt :: [a] -> Int -> a -> [a]
setAt xs i x = take i xs ++ [x] ++ drop (i + 1) xs

pairs :: Eq a => [a] -> [(a, a)]
pairs [] = []
pairs [_] = error "Odd length list provided to pairs"
pairs (x1:x2:xs) = (x1, x2) : pairs xs
47 changes: 38 additions & 9 deletions src/Lib/Matrix.hs
Original file line number Diff line number Diff line change
@@ -1,21 +1,50 @@
module Lib.Matrix (Matrix, (!), (!?), height, width) where
module Lib.Matrix (Matrix, (!), (!?), height, width, insertCol, insertRow, getRow, getCol, getCols, findColIndices, findRowIndices, Lib.Matrix.toList, findLocations) where

import Data.Vector (Vector)
import qualified Data.Vector as V
import Data.Foldable (toList)
import Data.List (findIndices)
import Data.Sequence (Seq)
import qualified Data.Sequence as S
import Prelude hiding (elem)

type Matrix a = Vector (Vector a)
type Matrix a = Seq (Seq a)

type Position = (Int, Int) -- x, y

(!) :: Position -> Matrix a -> a
(!) (x, y) mat = mat V.! y V.! x
(!) :: Matrix a -> Position -> a
mat ! (x, y) = mat `S.index` y `S.index` x

(!?) :: Position -> Matrix a -> Maybe a
(!?) (x, y) mat = mat V.!? y >>= (V.!? x)
(!?) (x, y) mat = mat S.!? y >>= (S.!? x)

height :: Matrix a -> Int
height = V.length
height = S.length

width :: Matrix a -> Int
width = V.length . V.head
width = S.length . (`S.index` 0)

insertCol :: Seq a -> Int -> Matrix a -> Matrix a
insertCol col colIdx = fmap (S.insertAt colIdx (col `S.index` colIdx))

insertRow :: Seq a -> Int -> Matrix a -> Matrix a
insertRow row rowIdx = S.insertAt rowIdx row

getRow :: Int -> Matrix a -> Seq a
getRow = flip S.index

getCol :: Int -> Matrix a -> Seq a
getCol colIdx = fmap (`S.index` colIdx)

getCols :: Matrix a -> [Seq a]
getCols mat = [getCol colIdx mat | colIdx <- [0 .. width mat - 1]]

findColIndices :: (Seq a -> Bool) -> Matrix a -> [Int]
findColIndices predicate mat = findIndices predicate (getCols mat)

findRowIndices :: (Seq a -> Bool) -> Matrix a -> [Int]
findRowIndices = S.findIndicesL

toList :: Matrix a -> [[a]]
toList mat = Data.Foldable.toList $ fmap Data.Foldable.toList mat

findLocations :: (a -> Bool) -> Matrix a -> [(Int, Int)]
findLocations predicate mat = [(x, y) | y <- [0 .. height mat - 1], x <- [0 .. width mat - 1], predicate (mat ! (x, y))]
83 changes: 83 additions & 0 deletions src/Lib/SearchAlgorithms.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}

module Lib.SearchAlgorithms (DijkstraGraph(..), Distance(..), findShortestDistance) where

import Data.Heap (MinPrioHeap)
import qualified Data.Heap as H
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HM
import Data.Hashable (Hashable)
import Data.Maybe (fromMaybe)
import Data.HashSet (HashSet)
import qualified Data.HashSet as HS
import Data.Kind (Type)
import Data.Foldable (foldl')

data Distance a = Dist a | Infinity
deriving (Show, Eq)

instance (Ord a) => Ord (Distance a) where
Infinity <= Infinity = True
Infinity <= Dist _ = False
Dist _ <= Infinity = True
Dist x <= Dist y = x <= y

addDist :: (Num a) => Distance a -> Distance a -> Distance a
addDist (Dist x) (Dist y) = Dist (x + y)
addDist _ _ = Infinity

(!??) :: (Hashable k) => HashMap k (Distance d) -> k -> Distance d
(!??) distanceMap key = fromMaybe Infinity (HM.lookup key distanceMap)

class DijkstraGraph graph where
type DijkstraNode graph :: Type
type DijkstraCost graph :: Type
dijkstraEdges :: graph -> DijkstraNode graph -> [(DijkstraNode graph, DijkstraCost graph)]


data DijkstraState node cost = DijkstraState
{ visitedSet :: HashSet node
, distanceMap :: HashMap node (Distance cost)
, nodeQueue :: MinPrioHeap (Distance cost) node
}

-- Taken from https://mmhaskell.com/blog/2022/8/22/dijkstras-algorithm-in-haskell
findShortestDistance ::
forall g. (Hashable (DijkstraNode g), Num (DijkstraCost g), Ord (DijkstraCost g), DijkstraGraph g) => g -> DijkstraNode g -> DijkstraNode g -> Distance (DijkstraCost g)
findShortestDistance graph src dest = processQueue initialState !?? dest
where
initialVisited = HS.empty
initialDistances = HM.singleton src (Dist 0)
initialQueue = H.fromList [(Dist 0, src)]
initialState = DijkstraState initialVisited initialDistances initialQueue

processQueue :: DijkstraState (DijkstraNode g) (DijkstraCost g) -> HashMap (DijkstraNode g) (Distance (DijkstraCost g))
processQueue ds@(DijkstraState v0 d0 q0 ) = case H.view q0 of
-- if there is nothing left in the queue, we have processed everything and we can just return the distances
Nothing -> d0
Just ((_, node), q1) ->
-- if we have reached the destination node, we have processed everything
if node == dest
then d0
-- if the node is already visited, then we can immediately recurse, except plugging in the updated queue
else if HS.member node v0 then processQueue ds{ nodeQueue = q1}
-- 1. Pull a new node from our heap and consider that node “visited”
-- 2. Get all the “neighbors” of this node
-- 3. Process each neighbor and update its distance
else
let v1 = HS.insert node v0
allNeighbors = dijkstraEdges graph node
unvisitedNeighbors = filter (\(n, _) -> not (HS.member n v1)) allNeighbors
in processQueue $ foldl' (foldNeighbor node) ds{ visitedSet=v1, nodeQueue=q1 } unvisitedNeighbors

-- Take the distance from the source to the current node and add it to the specific edge cost from the current to this new node.
-- Then compare this distance to the existing distance we have to the neighbor in our distance map (or Infinity if it doesn’t exist).
foldNeighbor currentNode ds@(DijkstraState _ d0 q0) (neighborNode, cost) =
let altDistance = addDist (d0 !?? currentNode) (Dist cost)
in if altDistance < d0 !?? neighborNode
-- If the alternative distance is smaller, we update the distance map by associating the neighbor node with the
-- alternative distance and return the new DijkstraState. We also insert the new distance into our queue.
then ds { distanceMap = HM.insert neighborNode altDistance d0, nodeQueue = H.insert (altDistance, neighborNode) q0 }
-- If the alternative distance is not better, we make no changes, and return the original state.
else ds
4 changes: 3 additions & 1 deletion src/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Solutions.Day06 (day06)
import Solutions.Day07 (day07)
import Solutions.Day08 (day08)
import Solutions.Day09 (day09)
import Solutions.Day11 (day11)

main :: IO ()
main = do
Expand All @@ -19,4 +20,5 @@ main = do
-- day06
-- day07
-- day08
day09
-- day09
day11
22 changes: 11 additions & 11 deletions src/Solutions/Day03.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import Data.Char (digitToInt)
import Data.Functor (($>))
import Data.Maybe (catMaybes, fromJust, fromMaybe)
import qualified Data.Text as T
import Data.Vector (Vector)
import qualified Data.Vector as V
import Data.Sequence (Seq)
import qualified Data.Sequence as S
import Lib.AOC (runSolution)
import qualified Lib.Matrix as Matrix
import Lib.Parsing (linesOf)
import Lib.Matrix (Matrix)
import Data.Foldable (toList)

data SchematicItem = Number Int | Symbol Char deriving (Eq, Show)

Expand All @@ -26,20 +28,18 @@ getNumberVal :: SchematicItem -> Int
getNumberVal (Number x) = x
getNumberVal _ = error "Tried to extract number from non-Number SchematicItem"

type Matrix a = Vector (Vector a)

type Input = Matrix (Maybe SchematicItem)

day03 :: IO ()
day03 = runSolution "03" parser (fmap part1) (fmap part2)

parser :: String -> Either String Input
parser = fmap (V.map combineNumbers) . P.parseOnly (V.fromList <$> linesOf (V.fromList <$> P.many1 schematicItemParser)) . T.pack
parser = fmap (fmap combineNumbers) . P.parseOnly (S.fromList <$> linesOf (S.fromList <$> P.many1 schematicItemParser)) . T.pack

part1 :: Input -> Int
part1 input =
let partNumberPositions = getPartNumberPositions input
partNumbers = map getNumberVal $ catMaybes $ removeConseqDups $ map (Matrix.! input) partNumberPositions
partNumbers = map getNumberVal $ catMaybes $ removeConseqDups $ map (input Matrix.!) partNumberPositions
in sum partNumbers

schematicItemParser :: P.Parser (Maybe SchematicItem)
Expand All @@ -49,8 +49,8 @@ schematicItemParser =
<|> (Just . Symbol <$> P.notChar '\n')

-- Combines the individual digits into larger numbers
combineNumbers :: Vector (Maybe SchematicItem) -> Vector (Maybe SchematicItem)
combineNumbers = V.fromList . combineNumbers' . V.toList
combineNumbers :: Seq (Maybe SchematicItem) -> Seq (Maybe SchematicItem)
combineNumbers = S.fromList . combineNumbers' . toList
where
isNumber' (Just x) = isNumber x
isNumber' _ = False
Expand All @@ -65,7 +65,7 @@ combineNumbers = V.fromList . combineNumbers' . V.toList
getSymbolPositions :: Input -> [(Int, Int)]
getSymbolPositions mat = do
yPosition <- [0 .. Matrix.height mat - 1]
xPosition <- V.toList $ V.findIndices (fromMaybe False . (Just isSymbol <*>)) (mat V.! yPosition)
xPosition <- toList $ S.findIndicesL (fromMaybe False . (Just isSymbol <*>)) (mat `S.index` yPosition)
return (xPosition, yPosition)

getPartNumberPositions :: Input -> [(Int, Int)]
Expand Down Expand Up @@ -96,10 +96,10 @@ part2 input = sum gearRatios

potentialGearPositions = do
yPosition <- [0 .. Matrix.height input - 1]
xPosition <- V.toList $ V.findIndices (== Just (Symbol '*')) (input V.! yPosition)
xPosition <- toList $ S.findIndicesL (== Just (Symbol '*')) (input `S.index` yPosition)
return (xPosition, yPosition)

getGearAdjacentNums = map getNumberVal . filter isNumber . catMaybes . removeConseqDups . map (Matrix.! input) . neighborPositions
getGearAdjacentNums = map getNumberVal . filter isNumber . catMaybes . removeConseqDups . map (input Matrix.!) . neighborPositions

isGear :: (Int, Int) -> Bool
isGear gearPos = length (getGearAdjacentNums gearPos) == 2
Loading

0 comments on commit a002b1c

Please sign in to comment.