module Dijkstra ( dijkstra, dijkstraAll ) where
import Prelude hiding (null) import Data.List (minimumBy) import Data.Map (Map, alter, delete, empty, findWithDefault, insert, member, null, singleton, toList, (!)) import Data.Ord (comparing)
data Edge pos cost = Edge pos cost deriving Show
dijkstra :: (Ord pos, Num cost, Ord cost) => pos -> pos -> [(pos, cost, pos)] -> (cost, [pos]) dijkstra start goal edges = shortestPaths ! goal where shortestPaths = dijkstraAll start edges
dijkstraAll :: (Ord pos, Num cost, Ord cost) => pos -> [(pos, cost, pos)] -> Map pos (cost, [pos]) dijkstraAll start edges = fst $ until (null . snd) (step edgeMap) initial where initial = (empty, singleton start (0, [start])) edgeMap = genEdgeMap edges
step :: (Ord pos, Num cost, Ord cost) => Map pos [Edge pos cost] -> (Map pos (cost, [pos]), Map pos (cost, [pos])) -> (Map pos (cost, [pos]), Map pos (cost, [pos])) step edgeMap (pathMap, nodeMap) = let (pos, path) = getMinimumCostNode nodeMap edges = findWithDefault [] pos edgeMap in if pos `member` pathMap then (pathMap, delete pos nodeMap) else let nodeMap' = delete pos $ updateNeigbors path edges nodeMap pathMap' = insert pos path pathMap in (pathMap', nodeMap')
getMinimumCostNode :: (Ord pos, Num cost, Ord cost) => Map pos (cost, [pos]) -> (pos, (cost, [pos])) getMinimumCostNode = minimumBy (comparing getCost) . toList where getCost (_, (cost, _)) = cost
updateNeigbors :: (Ord pos, Num cost, Ord cost) => (cost, [pos]) -> [Edge pos cost] -> Map pos (cost, [pos]) -> Map pos (cost, [pos]) updateNeigbors (cost, way) edges nodeMap = foldl updateNeighbor nodeMap edges where updateNeighbor nodeMap (Edge nextPos moveCost) = alter (updateNode (moveCost + cost, nextPos:way)) nextPos nodeMap
updateNode :: (Ord pos, Num cost, Ord cost) => (cost, [pos]) -> Maybe (cost, [pos]) -> Maybe (cost, [pos]) updateNode newPath Nothing = return newPath updateNode newPath (Just path) = return $ min newPath path
genEdgeMap :: (Ord pos, Num cost, Ord cost) => [(pos, cost, pos)] -> Map pos [Edge pos cost] genEdgeMap edges = foldl f empty edges where f edgeMap (from, cost, to) = append (Edge to cost) from edgeMap append val pos map = insert pos (val: findWithDefault [] pos map) map
|