Advertisement
Guest User

SMO low% routes comparison tool

a guest
Mar 22nd, 2019
100
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. data Kingdom = Cap | Cascade | Sand | Lake | Wooded | Cloud | Lost | Metro | Snow | Seaside | Luncheon | Ruined | Bowsers | Moon | Mushroom | DarkSide | DarkerSide
  2.     deriving (Show, Eq)
  3.  
  4. allKingdoms = [Cap , Cascade , Sand , Lake , Wooded , Cloud, Lost , Metro , Snow , Seaside , Luncheon , Ruined , Bowsers , Moon , Mushroom , DarkSide , DarkerSide]
  5.  
  6. data TravelMethod = Odyssey | Painting | Auto
  7.     deriving (Show, Eq)
  8.  
  9. travelAPresses :: TravelMethod -> Int
  10. travelAPresses Odyssey = 2 -- not counting before-postgame cappy dialogue, but that doesn't matter for comparative puropses (TODO: does matter if we decide to do lake odyssey skip)
  11. travelAPresses Painting = 1
  12. travelAPresses Auto = 0
  13.  
  14. type KingdomOrder = (Bool, Bool)
  15.  
  16. lakeOrWoodedFirst :: KingdomOrder -> Kingdom
  17. lakeOrWoodedFirst (True, _) = Lake
  18. lakeOrWoodedFirst (False, _) = Wooded
  19.  
  20. lakeOrWoodedSecond :: KingdomOrder -> Kingdom
  21. lakeOrWoodedSecond (lw, ss) = lakeOrWoodedFirst (not lw, ss)
  22.  
  23. snowOrSeasideFirst :: KingdomOrder -> Kingdom
  24. snowOrSeasideFirst (_, True) = Snow
  25. snowOrSeasideFirst (_, False) = Seaside
  26.  
  27. snowOrSeasideSecond :: KingdomOrder -> Kingdom
  28. snowOrSeasideSecond (lw, ss) = snowOrSeasideFirst (lw, not ss)
  29.  
  30. paintingDest :: KingdomOrder -> Kingdom -> Maybe Kingdom
  31. paintingDest ko k = case k of
  32.     Mushroom -> Just $ snowOrSeasideSecond ko
  33.     Cascade -> Just Bowsers
  34.     Bowsers -> Just $ snowOrSeasideFirst ko
  35.     Sand -> Just Metro
  36.     Metro -> Just $ lakeOrWoodedSecond ko
  37.     Luncheon -> Just Mushroom
  38.     _ -> if k == snowOrSeasideFirst ko then
  39.             Just $ lakeOrWoodedFirst ko
  40.         else if k == snowOrSeasideSecond ko then
  41.             Just Cascade
  42.         else if k == lakeOrWoodedFirst ko then
  43.             Just Sand
  44.         else if k == lakeOrWoodedSecond ko then
  45.             Just Luncheon
  46.         else Nothing
  47.  
  48. -- Which story moons in each kingdom have extra A presses if done before postgame
  49. storyMoonPenalties :: Kingdom -> [Int]  
  50. storyMoonPenalties k = case k of
  51.     Sand -> [4,7]
  52.     Lake -> [1]
  53.     Wooded -> [1]
  54.     Luncheon -> [1,4]
  55.     _ -> []
  56.  
  57. -- after n indicates it's unlocked after n story moons in the above list.
  58. data StoryMoonIndex = After Int | Postgame
  59.  
  60. -- where the art is, where the moon is, and when it's unlocked
  61. type HintArt = (Kingdom, Kingdom, StoryMoonIndex)
  62.  
  63. darkSideArt :: Kingdom -> HintArt
  64. darkSideArt k = (DarkSide, k, Postgame)
  65.  
  66. hintArts :: [HintArt]
  67. hintArts = [
  68.     (Cap, Moon, Postgame),
  69.     (Sand, Bowsers, Postgame),
  70.     (Lake, Cascade, After 1),
  71.     (Wooded, Sand, After 0),
  72.     (Metro, Lake, After 0),
  73.     (Snow, Lost, After 0),
  74.     (Seaside, Metro, After 0),
  75.     (Luncheon, Seaside, After 2),
  76.     (Bowsers, Sand, Postgame),
  77.     (Moon, Wooded, Postgame),
  78.     (Mushroom, Cap, Postgame),
  79.     darkSideArt Cascade,
  80.     darkSideArt Metro,
  81.     darkSideArt Mushroom,
  82.     darkSideArt Cloud,
  83.     darkSideArt Snow,
  84.     darkSideArt Seaside,
  85.     darkSideArt Lost,
  86.     darkSideArt Luncheon,
  87.     darkSideArt Lake,
  88.     darkSideArt Ruined]
  89.  
  90.  
  91. type StoryRequirement = (Kingdom, StoryMoonIndex)
  92. touristOrder :: [StoryRequirement]
  93. touristOrder = [
  94.     (Sand, After 2),
  95.     (Metro, After 0),
  96.     (Cascade, After 0),
  97.     (Luncheon, After 2),
  98.     (Moon, After 0),
  99.     (Mushroom, Postgame),
  100.     (Sand, Postgame)]
  101.  
  102.  
  103. hasPainting :: Kingdom -> Bool
  104. hasPainting k = paintingDest (True, True) k /= Nothing
  105.  
  106. -- for verifying peach is got in all kingdoms before moon
  107. hasPeach :: Kingdom -> Bool
  108. hasPeach k = not (k `elem` [DarkSide, DarkerSide, Moon, Mushroom])
  109.  
  110. type Route = [(TravelMethod, Kingdom)]
  111. type KingdomIndex = Int
  112.  
  113. expect :: String -> Maybe a -> a
  114. expect _ (Just x) = x
  115. expect s Nothing = error ("Expected " ++ s)
  116.  
  117. firstIndexOf :: Kingdom -> Route -> KingdomIndex
  118. firstIndexOf x r = expect (show x) $ foldr (\(_, y) res ->
  119.     if x == y then Just 0 else fmap (+1) res) Nothing r
  120.  
  121. lastIndexOf :: Kingdom -> Route -> KingdomIndex
  122. lastIndexOf x xs = (length xs - 1) - firstIndexOf x (reverse xs) -- ugly
  123.  
  124. lastIndexOfBefore x i xs = lastIndexOf x (take i xs)
  125.  
  126. isPostgame :: KingdomIndex -> Route -> Bool
  127. isPostgame i r = i >= firstIndexOf Mushroom r
  128.  
  129. postgame :: Route -> Route
  130. postgame r = drop (firstIndexOf Mushroom r) r
  131.  
  132. kingdoms :: Route -> [Kingdom]
  133. kingdoms = map snd
  134.  
  135. getKingdomOrder :: Route -> KingdomOrder
  136. getKingdomOrder r = (firstIndexOf Lake r < firstIndexOf Wooded r, firstIndexOf Snow r < firstIndexOf Seaside r)
  137.  
  138. expectedBeforePostgame :: KingdomOrder -> Route
  139. expectedBeforePostgame ko = [
  140.     (Auto, Cap),
  141.     (Auto, Cascade),
  142.     (Odyssey, Sand),
  143.     (Odyssey, lakeOrWoodedFirst ko),
  144.     (Odyssey, lakeOrWoodedSecond ko),
  145.     (Odyssey, Cloud),
  146.     (Auto, Lost),
  147.     (Odyssey, Metro),
  148.     (Odyssey, snowOrSeasideFirst ko),
  149.     (Odyssey, snowOrSeasideSecond ko),
  150.     (Odyssey, Luncheon),
  151.     (Odyssey, Ruined),
  152.     (Odyssey, Bowsers),
  153.     (Odyssey, Moon),
  154.     (Auto, Mushroom)]
  155.  
  156.  
  157. -- TODO: support sand->lake Odyssey Skip
  158. validateBeforePostgame :: Route -> Bool
  159. validateBeforePostgame r = expectedBeforePostgame (getKingdomOrder r) == take ((firstIndexOf Mushroom r) +1) r
  160.  
  161. validatePostgame :: Route -> Bool
  162. validatePostgame r = all (`elem` (kingdoms $ postgame r)) allKingdoms
  163.  
  164. validatePaintingDests :: Route -> Bool
  165. validatePaintingDests r =
  166.         all (\i ->
  167.             let ((_, s), (m, d)) = (r!!i, r!!(i+1)) in
  168.                 m /= Painting || paintingDest ko s == Just d) [0..length r -2]
  169.     where ko = getKingdomOrder r
  170.  
  171. validateAllPaintingsUsed :: Route -> Bool
  172. validateAllPaintingsUsed r =  
  173.     all (\k -> (Painting, k) `elem` r) (filter hasPainting allKingdoms)
  174.  
  175. validateTravel :: Route -> Bool
  176. validateTravel r = all (/= Auto) $ map fst $ drop ((firstIndexOf Mushroom r)+1) r
  177.  
  178. validatePeach :: Route -> Bool
  179. validatePeach r = all (`elem` (kingdoms peachActive)) (filter hasPeach allKingdoms)
  180.     where peachActive = dropWhile (\(m, _) -> m /= Odyssey) mushToMoon
  181.           mushToMoon = postgame $ take (lastIndexOf Moon r) r
  182.  
  183. singleHintArtReqs :: Route -> HintArt -> StoryRequirement
  184. singleHintArtReqs r (s, d, req) =
  185.     let i = lastIndexOfBefore s (lastIndexOf d r) r in
  186.     if isPostgame i r then (s, After 0) else (s, req)
  187.  
  188. hintArtReqs :: Route -> [StoryRequirement]
  189. hintArtReqs r = map (singleHintArtReqs r) hintArts
  190.  
  191. touristReqs :: Route -> [StoryRequirement]
  192. touristReqs r = snd $ foldr (\req@(k, _) (idxNext, reqs) ->
  193.     let idxThis = lastIndexOfBefore k idxNext r in
  194.         (idxThis, if isPostgame idxThis r then reqs else req:reqs)) (length r, []) touristOrder
  195.  
  196. aPressesStoryMoons :: [StoryRequirement] -> Kingdom -> Int
  197. aPressesStoryMoons reqs k = (0:storyMoonPenalties k) !! i
  198.     where i = foldr max 0 reqs'
  199.          reqs' = map (\(_,x) -> case x of
  200.             Postgame -> error ((show k) ++ " needs to be postgame")
  201.             After n -> n) $ filter (\(k',_) -> k==k') reqs
  202.  
  203. validations :: [Route -> Bool]
  204. validations = [validateBeforePostgame, validatePostgame, validatePaintingDests, validateAllPaintingsUsed, validateTravel, validatePeach]
  205.  
  206. countAPresses :: Route -> Int
  207. countAPresses r = if not $ all (\v -> v r) validations
  208.     then error "Route invalid"
  209.     else let reqs = hintArtReqs r ++ touristReqs r in
  210.     sum (map (aPressesStoryMoons reqs) allKingdoms)
  211.     + sum (map (travelAPresses . fst) r)
  212.  
  213. speedrunRoute :: Route
  214. speedrunRoute = [
  215.     (Auto, Cap),
  216.     (Auto, Cascade),
  217.     (Odyssey, Sand),
  218.     (Odyssey, Wooded),
  219.     (Odyssey, Lake),
  220.     (Odyssey, Cloud),
  221.     (Auto, Lost),
  222.     (Odyssey, Metro),
  223.     (Odyssey, Snow),
  224.     (Odyssey, Seaside),
  225.     (Odyssey, Luncheon),
  226.     (Odyssey, Ruined),
  227.     (Odyssey, Bowsers),
  228.     (Odyssey, Moon),
  229.     (Auto, Mushroom),
  230.     (Painting, Seaside),
  231.     (Odyssey, Cap),
  232.     (Odyssey, DarkSide),
  233.     (Odyssey, Moon),
  234.     (Odyssey, Seaside),
  235.     (Painting, Cascade),
  236.     (Painting, Bowsers),
  237.     (Painting, Snow),
  238.     (Painting, Wooded),
  239.     (Painting, Sand),
  240.     (Painting, Metro),
  241.     (Painting, Lake),
  242.     (Painting, Luncheon),
  243.     (Painting, Mushroom),
  244.     (Odyssey, DarkerSide),
  245.     (Odyssey, Bowsers),
  246.     (Odyssey, Ruined),
  247.     (Odyssey, Lost),
  248.     (Odyssey, Cloud),
  249.     (Odyssey, Moon),
  250.     (Odyssey, Mushroom),
  251.     (Odyssey, Sand)]
  252.  
  253.  
  254. aaronSuggestedRoute :: Route
  255. aaronSuggestedRoute = [
  256.     (Auto, Cap),
  257.     (Auto, Cascade),
  258.     (Odyssey, Sand),
  259.     (Odyssey, Wooded),
  260.     (Odyssey, Lake),
  261.     (Odyssey, Cloud),
  262.     (Auto, Lost),
  263.     (Odyssey, Metro),
  264.     (Odyssey, Snow),
  265.     (Odyssey, Seaside),
  266.     (Odyssey, Luncheon),
  267.     (Odyssey, Ruined),
  268.     (Odyssey, Bowsers),
  269.     (Odyssey, Moon),
  270.     (Auto, Mushroom),
  271.     (Odyssey, Cap),
  272.     (Odyssey, DarkSide),
  273.     (Odyssey, Moon),
  274.     (Odyssey, Wooded),
  275.     (Painting, Sand),
  276.     (Painting, Metro),
  277.     (Painting, Lake),
  278.     (Painting, Luncheon),
  279.     (Painting, Mushroom),
  280.     (Painting, Seaside),
  281.     (Painting, Cascade),
  282.     (Painting, Bowsers),
  283.     (Painting, Snow),
  284.     (Painting, Wooded),
  285.     (Odyssey, DarkerSide),
  286.     (Odyssey, Luncheon),
  287.     (Odyssey, Ruined),
  288.     (Odyssey, Lost),
  289.     (Odyssey, Cloud),
  290.     (Odyssey, Moon),
  291.     (Odyssey, Mushroom),
  292.     (Odyssey, Sand)]
  293.  
  294.    
  295. main :: IO ()
  296. main = do
  297.     print $ countAPresses speedrunRoute -- 68
  298.     print $ countAPresses aaronSuggestedRoute -- 56
  299. -- these 12 saved a presses come from being able to do certain story moons in postgame
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement