Advertisement
Guest User

[Language: Haskell] Day 17 :-> 2023

a guest
Dec 18th, 2023
153
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Haskell 3.69 KB | Source Code | 0 0
  1. module Main where
  2.  
  3. import Data.List
  4. import Data.List.Split
  5. import qualified Data.Map as M
  6. import Data.PSQueue (Binding ((:->)))
  7. import qualified Data.PSQueue as PQ
  8.  
  9. type Pos = (Int, Int)
  10. type Prio = (Int, Int, Int)
  11. type State = (Pos, (Int, Dir))
  12.  
  13. data Dir = U | R | D | L | None deriving (Show, Eq, Ord)
  14.  
  15. (#+) :: Pos -> Dir -> Pos
  16. (x, y) #+ dir = case dir of
  17.   U -> (x, y - 1)
  18.   R -> (x + 1, y)
  19.   D -> (x, y + 1)
  20.   L -> (x - 1, y)
  21.   None -> (x, y)
  22.  
  23. opposite :: Dir -> Dir
  24. opposite U = D
  25. opposite D = U
  26. opposite R = L
  27. opposite L = R
  28. opposite None = None
  29.  
  30. readInt :: String -> Int
  31. readInt = read
  32.  
  33. main :: IO ()
  34. main = do
  35.   ls <- lines <$> readFile "input.txt"
  36.   let m = M.fromList . concatMap (\(y, line) -> zipWith (\x c -> ((x, y), readInt [c])) [0 ..] line) . zip [0 ..] $ ls
  37.   let lines = part1 m
  38.   putStrLn $ "Part 1: " ++ (show . part1 $ m)
  39.   putStrLn $ "Part 2: " ++ (show . part2 $ m)
  40.  
  41. shortestPath :: M.Map Pos Int -> Pos -> (State -> Bool) -> (State -> [Dir]) -> (Pos -> Int) -> Int
  42. shortestPath m start isFinished getNextDirs heuristic =
  43.     let initialH = heuristic start
  44.     in sp (PQ.singleton (start, (0, None)) (initialH, 0, initialH)) M.empty
  45.   where
  46.     sp :: PQ.PSQ State Prio -> M.Map State Int -> Int
  47.     sp open closed =
  48.       case PQ.minView open of
  49.         Nothing -> error "Couldn't find a way. :frown:"
  50.         Just (state :-> (f, g, h), restOpen) ->
  51.             if isFinished state
  52.             then g
  53.             else let neighbors = map (posDirToBinding g) . filter (`M.notMember` closed) . filter ((`M.member` m) . fst) $ getNext state
  54.                      newOpen = foldl' updateQueue restOpen neighbors
  55.                 in sp newOpen (M.insert state g closed)
  56.    
  57.    getNext :: State -> [State]
  58.    getNext state@(pos, (n, dir)) = map f . getNextDirs $ state
  59.      where f :: Dir -> (Pos, (Int, Dir))
  60.            f d | d == dir = (pos #+ d, (n + 1, d)) | otherwise = (pos #+ d, (1, d))
  61.  
  62.    updateQueue :: PQ.PSQ State Prio -> PQ.Binding State Prio -> PQ.PSQ State Prio
  63.    updateQueue pq (key :-> prio) = PQ.alter (updateBinding prio) key pq
  64.  
  65.    updateBinding :: Prio -> Maybe Prio -> Maybe Prio
  66.    updateBinding prio Nothing = Just prio
  67.    updateBinding prio@(f, g, h) oldPrio@(Just (oldF, oldG, oldH)) = if g < oldG then Just prio else oldPrio
  68.  
  69.    posDirToBinding :: Int -> State -> PQ.Binding State Prio
  70.    posDirToBinding g pd@(pos, (n, dir)) = pd :-> (g + cost + h, g + cost, h)
  71.        where cost = m M.! pos
  72.              h = heuristic pos
  73.  
  74. manhattan :: Pos -> Pos -> Int
  75. manhattan (x1, y1) (x2, y2) = abs (x2 - x1) + abs (y2 - y1)
  76.  
  77. part1 :: M.Map Pos Int -> Int
  78. part1 m = shortestPath m (0, 0) finishRule getNextDirs heuristic
  79.  where finish :: Pos
  80.        (finish, _) = M.findMax m
  81.        
  82.        heuristic :: Pos -> Int
  83.        heuristic = manhattan finish
  84.  
  85.        finishRule :: State -> Bool
  86.        finishRule (pos, _) = pos == finish
  87.  
  88.        getNextDirs :: State -> [Dir]
  89.        getNextDirs (_, (n, dir))
  90.          | n >= 3     = [U, R, D, L] \\ [opposite dir, dir]
  91.          | otherwise  = [U, R, D, L] \\ [opposite dir]
  92.  
  93. part2 :: M.Map Pos Int -> Int
  94. part2 m = shortestPath m (0, 0) finishRule getNextDirs heuristic
  95.  where finish :: Pos
  96.        (finish, _) = M.findMax m
  97.        
  98.        heuristic :: Pos -> Int
  99.        heuristic = manhattan finish
  100.  
  101.        finishRule :: State -> Bool
  102.        finishRule (pos, (n, _)) = pos == finish && n >= 4
  103.  
  104.        getNextDirs :: State -> [Dir]
  105.        getNextDirs state@(_, (n, dir))
  106.          | n == 0      = [U, R, D, L]
  107.          | n < 4       = [dir]
  108.          | n >= 10     = [U, R, D, L] \\ [opposite dir, dir]
  109.          | otherwise   = [U, R, D, L] \\ [opposite dir]
Tags: aoc aoc2023 Day17
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement