diff options
author | Laura Orvokki Kursula <lav@vampires.gay> | 2024-12-16 23:19:07 +0100 |
---|---|---|
committer | Laura Orvokki Kursula <lav@vampires.gay> | 2024-12-16 23:20:54 +0100 |
commit | b6bf89d7a264b7bb28d336b4912e6cdcb9ecf69c (patch) | |
tree | 2a253c3bd0fe43020bd3e6151769a2ae374022ef | |
parent | 1bde1bda9adb4708dc840aab0e2dfc1b33cdde7b (diff) | |
download | aoc2024-b6bf89d7a264b7bb28d336b4912e6cdcb9ecf69c.tar.gz aoc2024-b6bf89d7a264b7bb28d336b4912e6cdcb9ecf69c.zip |
16-1: mutable vectors for 42-fold speedup
-rw-r--r-- | 16-1.hs | 119 |
1 files changed, 70 insertions, 49 deletions
@@ -1,33 +1,39 @@ +import Control.Monad (foldM) +import Control.Monad.ST +import Control.Monad.Trans.Class +import Control.Monad.Trans.Maybe import Data.Array as A import Data.Bifunctor (second) import Data.List as L (find, uncons) import Data.Maybe (fromJust) -import Data.Tuple (swap) -import Data.Vector as V hiding - ((++), elem, filter, foldl, head, length, map, zip) -import Prelude hiding (replicate) +import Data.Tuple as T (swap) +import Data.Vector.Mutable as V hiding (foldM) +import Prelude +import Prelude as P (length) -data BucketQueue a = BQ Int (V.Vector [a]) deriving Show +data BucketQueue s a = BQ Int (V.MVector s [a]) -insert :: BucketQueue a -> (Int,a) -> BucketQueue a +insert :: BucketQueue s a -> (Int,a) -> ST s (BucketQueue s a) insert (BQ top buckets) (prio,x) = - let top' = min top prio - in BQ top' $ V.accum (flip (:)) buckets [ (prio, x) ] + V.modify buckets (x:) prio >> return (BQ (min top prio) buckets) -view :: BucketQueue a -> Maybe (a, BucketQueue a) +view :: BucketQueue s a -> MaybeT (ST s) (a, BucketQueue s a) view (BQ top buckets) = do - (x, xs) <- buckets V.!? top >>= L.uncons - let buckets' = buckets V.// [ (top, xs) ] - let top' = findTop top buckets' - return (x, BQ top' buckets') - -findTop :: Int -> V.Vector [a] -> Int -findTop n v = case v V.! n of - [] -> if n < length v - 1 then findTop (n+1) v else n - _ -> n - -newBQ :: Int -> BucketQueue a -newBQ n = BQ n $ replicate n [] + (x, xs) <- V.read buckets top >>= hoistMaybe . L.uncons + lift $ V.write buckets top xs + top' <- lift $ findTop top buckets + return (x, BQ top' buckets) +{-# SCC view #-} + +findTop :: Int -> V.MVector s [a] -> ST s Int +findTop n v = do + top <- V.read v n + case top of + [] -> if n < V.length v - 1 then findTop (n+1) v else return n + _ -> return n + +newBQ :: Int -> ST s (BucketQueue s a) +newBQ n = BQ n <$> V.replicate n [] next :: Direction -> (Int,Int) -> (Int,Int) next North (x,y) = (x,y-1) @@ -49,7 +55,7 @@ right West = North type Node = ((Int,Int), Direction) -type Frontier = BucketQueue Node +type Frontier s = BucketQueue s Node data Direction = North | South | East | West deriving (Eq,Ix,Ord,Show) @@ -68,8 +74,8 @@ neighbours (x,y) = [ (North, (x,y-1)) parseMap :: [String] -> Map parseMap s = - let w = length (head s) - 1 - h = length s - 1 + let w = P.length (head s) - 1 + h = P.length s - 1 f p = case g p of '#' -> Wall _ -> Tile . map fst . filter ((/= '#') . g . snd) $ neighbours p @@ -82,16 +88,16 @@ parseMap s = findStart :: [String] -> (Int,Int) findStart ls = let Just p = L.find ((== 'S') . snd) [ ((x,y), ls !! y !! x) - | x <- [0..length (head ls) - 1] - , y <- [0..length ls - 1] + | x <- [0..P.length (head ls) - 1] + , y <- [0..P.length ls - 1] ] in fst p findGoal :: [String] -> (Int,Int) findGoal ls = let Just p = L.find ((== 'E') . snd) [ ((x,y), ls !! y !! x) - | x <- [0..length (head ls) - 1] - , y <- [0..length ls - 1] + | x <- [0..P.length (head ls) - 1] + , y <- [0..P.length ls - 1] ] in fst p @@ -107,9 +113,13 @@ expand m ((pos, dir), Just cost) = ++ ((\x -> ((pos,x), Just $ cost + 1000)) <$> [left dir, right dir]) pathThrough :: Map -> (Node,Maybe Int) -> Node -> (Node,Maybe Int) -pathThrough m through dest = head . filter ((== dest) . fst) $ expand m through +pathThrough m through dest = + head . filter ((== dest) . fst) $ expand m through -betterPath :: Map -> (Node,Maybe Int) -> (Node,Maybe Int) -> (Node,Maybe Int) +betterPath :: Map + -> (Node,Maybe Int) + -> (Node,Maybe Int) + -> (Node,Maybe Int) betterPath m through dest@(node,oldCost) = let n@(_,newCost) = pathThrough m through node in if newCost < oldCost @@ -119,30 +129,39 @@ betterPath m through dest@(node,oldCost) = cost :: Costs -> Node -> (Node, Maybe Int) cost costs node = (node, costs A.! node) -data PathingResult = Success Int | Fail | Working (Frontier,Costs) +data PathingResult s = Success Int | Fail | Working (Frontier s, Costs) heuristic :: (Int,Int) -> (Node,Int) -> (Node,Int) heuristic goal (n@(p,_),f) = (n, f + distance goal p) -pathOnce :: Map -> (Int,Int) -> Frontier -> Costs -> PathingResult -pathOnce m goal frontier costs = maybe Fail continue (view frontier) +pathOnce :: Map + -> (Int,Int) + -> Frontier s + -> Costs + -> ST s (PathingResult s) +pathOnce m goal frontier costs = + runMaybeT (view frontier) >>= maybe (return Fail) continue where + continue :: (Node,Frontier s) -> ST s (PathingResult s) continue (current@(p,_), rest) - | p == goal = Success $ fromJust (costs A.! current) + | p == goal = return . Success $ fromJust (costs A.! current) | otherwise = - let costs' = costs A.// [ betterPath m (cost costs current) n - | n <- expand m (cost costs current) - ] - frontier' = foldl insert rest - . map (swap . heuristic goal . second fromJust) + let costs' = {-# SCC "costs" #-} + costs A.// [ betterPath m (cost costs current) n + | n <- expand m (cost costs current) + ] + frontier' = foldM insert rest + . map (T.swap . heuristic goal . second fromJust) . filter ((== Nothing) . (costs A.!) . fst) $ expand m (cost costs current) - in Working (frontier', costs') + in Working . (, costs') <$> frontier' -findPath :: Map -> (Int,Int) -> (Int,Int) -> Int -findPath m start goal = loop frontier costs +findPath :: Map -> (Int,Int) -> (Int,Int) -> ST s Int +findPath m start goal = frontier >>= flip loop costs where - frontier = insert (newBQ $ 10^6) (0,(start,East)) + frontier = {-# SCC "frontier" #-} + let (_,(w,h)) = bounds m + in newBQ (100*w*h) >>= flip insert (0,(start,East)) costs = let (lower,upper) = bounds m in array ((lower,North),(upper,West)) @@ -153,11 +172,13 @@ findPath m start goal = loop frontier costs , d <- [North, South, East, West] ] - loop :: Frontier -> Costs -> Int - loop frontier costs = case pathOnce m goal frontier costs of - Success x -> x - Fail -> error "no path" - Working (frontier', costs') -> loop frontier' costs' + loop :: Frontier s -> Costs -> ST s Int + loop frontier costs = do + once <- pathOnce m goal frontier costs + case once of + Success x -> return x + Fail -> error "no path" + Working (frontier', costs') -> loop frontier' costs' main :: IO () main = do @@ -165,4 +186,4 @@ main = do let m = parseMap input let start = findStart input let goal = findGoal input - print $ findPath m start goal + print (runST $ findPath m start goal :: Int) |