import Data.List.Split import Data.Matrix import Data.Maybe import Data.Foldable import Data.PQueue.Min type Value = Integer type Distance = Integer type Position = (Int, Int) data MElement = MElement { value :: Value, visited :: Bool } deriving (Eq, Show) data QElement = QElement { distance :: Distance, position :: (Int, Int) } deriving (Eq, Show) instance Ord QElement where QElement d1 p1 <= QElement d2 p2 = d1 <= d2 markVisited :: Position -> Matrix MElement -> Matrix MElement markVisited pos m = let MElement value _ = m ! pos in unsafeSet (MElement value True) pos m getNeighbour :: Matrix MElement -> Distance -> Position -> Maybe QElement getNeighbour m dist pos@(i, j) = do MElement val vis <- safeGet i j m if vis then Nothing else return $ QElement (dist + val) pos getNeighbours :: Matrix MElement -> QElement -> [QElement] getNeighbours m (QElement dist (i, j)) = catMaybes [getNeighbour m dist ((i - 1), j), getNeighbour m dist ((i + 1), j), getNeighbour m dist (i, (j - 1)), getNeighbour m dist (i, (j + 1))] dijkstra :: Position -> Matrix MElement -> MinQueue QElement -> Distance dijkstra p m q = let (minElement@(QElement dist pos), newQ) = deleteFindMin q neighbours = getNeighbours m minElement in if pos == p then dist else dijkstra p (foldr update m neighbours) (foldr insert newQ neighbours) where update (QElement _ pos) = markVisited pos findShortestPathLength :: Matrix MElement -> Distance findShortestPathLength m = let MElement value _ = m ! (1, 1) initialMinQ = singleton $ QElement value (1, 1) lastPos = (nrows m, ncols m) in dijkstra lastPos m initialMinQ setInitial :: Matrix MElement -> Matrix MElement setInitial m = let MElement v _ = m ! (1, 1) in unsafeSet (MElement v True) (1, 1) m initElement :: Matrix Integer -> Position -> MElement initElement m p = let value = m ! p in MElement value False toElementMatrix :: Matrix Integer -> Matrix MElement toElementMatrix m = matrix (nrows m) (ncols m) (initElement m) main :: IO () main = do contents <- readFile "p083_matrix.txt" let listsMatrix = fmap (fmap read . (splitOn ",")) $ lines contents :: [[Integer]] valueMatrix = fromLists listsMatrix unvisitedMatrix = toElementMatrix valueMatrix mtrx = setInitial unvisitedMatrix print $ findShortestPathLength mtrx