Guest User

Untitled

a guest
Dec 17th, 2024
25
0
136 days
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Haskell 5.09 KB | Gaming | 0 0
  1. {-# LANGUAGE DeriveGeneric #-}
  2.  
  3. module DaySixteen (scorePath, realWorld, partTwo, distance, wander, parseGrid, sampleWorld, rotations, Direction(..)) where
  4.  
  5. import Debug.Trace
  6. import GHC.Generics (Generic)
  7. import Data.Graph.AStar (aStar)
  8. import Data.Tree (unfoldTree, flatten)
  9. import Data.Maybe (mapMaybe, fromMaybe)
  10. import Data.Hashable
  11. import qualified Data.Set as S
  12. import Data.HashSet
  13.  
  14. newtype Wall = Wall (Int, Int) deriving (Eq, Ord, Show)
  15.  
  16. type Location = (Int, Int)
  17.  
  18. type SearchState = (Location, Direction)
  19. data Direction = North | East  | South | West deriving (Eq, Ord, Show, Generic, Enum)
  20. instance Hashable Direction
  21.  
  22. type BudgetedSearchState = (SearchState, Int)
  23.  
  24. data World = World (S.Set Wall) Location Location deriving (Eq, Ord, Show)
  25.  
  26.  
  27. close North East = True
  28. close North West = True
  29. close South East = True
  30. close South West = True
  31. close East North = True
  32. close East South = True
  33. close West North = True
  34. close West South = True
  35. close _ _ = False
  36.  
  37. rotations :: SearchState -> [ SearchState]
  38. rotations searchState@(currentLocation, currentDirection) = [(currentLocation, dir) | dir <- enumFrom North, close currentDirection dir]
  39.  
  40. inc :: Int -> HashSet Int
  41. inc x = singleton $ x + 1
  42.  
  43. moveTowards (r,c) East = (r, c+1)
  44. moveTowards (r,c) West = (r, c-1)
  45. moveTowards (r,c) North = (r - 1, c)
  46. moveTowards (r,c) South = (r+1, c)
  47.  
  48. parseGrid :: [String] -> World
  49. parseGrid inputLines = World (S.fromList features) startLocation endLocation
  50.         where parseLine :: (String, Int) -> [Wall]
  51.               parseLine (contents, lineNumber) = mapMaybe parseChar $ zip contents [0..]
  52.                 where parseChar ('#', col) = Just $ Wall (lineNumber, col)
  53.                       parseChar _ = Nothing    
  54.               features = concatMap parseLine $ zip inputLines [0..]
  55.               maxColIdx = length (head inputLines) - 1
  56.               maxRowIdx = length inputLines - 1
  57.               startLocation = ((maxRowIdx - 1), 1)
  58.               endLocation = (1, (maxColIdx - 1))
  59.  
  60.  
  61. optimizedNeighbors :: World -> SearchState -> HashSet SearchState
  62. optimizedNeighbors world@(World walls startLoc endLoc) state@(location, direction) = fromList $ [r  | r <- rotations state, not (rotationIsObviouslyBad r)]  ++ steps
  63.   where naiveStep = moveTowards location direction
  64.         rotationIsObviouslyBad rotated = Wall ((uncurry moveTowards) rotated) `S.member` walls
  65.         steps
  66.           | Wall naiveStep `S.member` walls = []
  67.           | otherwise = [(naiveStep, direction)]
  68.  
  69.  
  70. distance :: SearchState -> SearchState -> Int
  71. distance left@(_, ldir) right@(_, rdir)
  72.          | ldir == rdir = 1
  73.          | otherwise = 1000
  74.      
  75. wanderFrom :: World -> SearchState -> Maybe [SearchState]
  76. wanderFrom world startState = fmap addStart $ aStar (optimizedNeighbors world) distance manhattan isGoal startState
  77.   where targetCoords (World _ _ tc) = tc
  78.         manhattan (loc, _) = manhattanDistance loc (targetCoords world)
  79.         manhattanDistance (a,b) (c,d) = (abs (a-b)) + (abs (c-d))
  80.         isGoal (loc, _) = loc == (targetCoords world)
  81.         addStart x = startState : x
  82.  
  83. wander :: World -> Maybe [SearchState]
  84. wander world = wanderFrom world startState
  85.   where startCoords (World _ sc _) = sc
  86.         startState = (startCoords world, East)
  87.         addStart x = startState : x
  88.  
  89. canReachInBudget :: World -> SearchState -> Int -> Bool
  90. canReachInBudget world currentState maxBudget = (maxBudget > 0) && (bestBudget `maybeLt` (Just maxBudget)) == Just True
  91.   where bestBudget = do
  92.                        path <- wanderFrom world currentState
  93.                        return $ scorePath path
  94.         maybeLt = liftA2 (<=)
  95.  
  96. scorePath path = sum $ fmap (uncurry distance) $  zip path (tail path)
  97.  
  98.  
  99.  
  100. unfolder :: World -> BudgetedSearchState -> (BudgetedSearchState, [BudgetedSearchState])
  101. unfolder world cb@(currentState@(loc, direction), budget) = (cb, budgetedNeighbors)
  102.   where nbrs = toList $ optimizedNeighbors world (traceShowId currentState)
  103.         budgetedNeighbors
  104.           | length nbrs == 1 = [(neighbor, (budget - (distance currentState neighbor))) | neighbor <- nbrs]
  105.           | otherwise = [(neighbor, (budget - (distance currentState neighbor))) | neighbor <- nbrs, canReachInBudget world neighbor (budget - (distance currentState neighbor))]
  106.        
  107.        
  108. partTwo world budget = S.size $ S.fromList $ fmap (fst . fst) $ flatten $ buildTree world budget
  109.         where buildTree world budget = unfoldTree (unfolder world) (startState, budget)
  110.                 where startCoords (World _ sc tc) = sc
  111.                       startState = (startCoords world, East)
  112.  
  113.  
  114.  
  115.  
  116. sampleWorld = parseGrid sampleInput
  117. realWorld = parseGrid realInput
  118. sampleInput = ["#################",
  119.   "#...#...#...#..E#",
  120.   "#.#.#.#.#.#.#.#^#",
  121.   "#.#.#.#...#...#^#",
  122.   "#.#.#.#.###.#.#^#",
  123.   "#>>v#.#.#.....#^#",
  124.   "#^#v#.#.#.#####^#",
  125.   "#^#v..#.#.#>>>>^#",
  126.   "#^#v#####.#^###.#",
  127.   "#^#v#..>>>>^#...#",
  128.   "#^#v###^#####.###",
  129.   "#^#v#>>^#.....#.#",
  130.   "#^#v#^#####.###.#",
  131.   "#^#v#^........#.#",
  132.   "#^#v#^#########.#",
  133.   "#S#>>^..........#",
  134.   "#################"]
  135.  
  136.  
  137. realInput = ["secret"]
  138.  
Advertisement
Add Comment
Please, Sign In to add comment