From bbb957f016c644e77bef3c2859c6552dbc29f5d8 Mon Sep 17 00:00:00 2001 From: Humza Shahid Date: Thu, 30 Jan 2025 13:06:32 +0000 Subject: [PATCH] get path-finding.sml to use function in build-graph.sml to find reachable platforms, and remove now-dead code from path-finding.sml --- fcore/build-graph.sml | 135 +++++++---------- fcore/path-finding.sml | 334 +++++------------------------------------ oms.mlb | 1 + 3 files changed, 98 insertions(+), 372 deletions(-) diff --git a/fcore/build-graph.sml b/fcore/build-graph.sml index e911c36..0817731 100644 --- a/fcore/build-graph.sml +++ b/fcore/build-graph.sml @@ -1,64 +1,60 @@ structure BuildGraph = struct - fun insertIfNotExistsOrShorter - (dist, eKeys, eVals, foldPlatID, q, fromPlatID) = - let - val pos = IntSet.findInsPos (foldPlatID, eKeys) - in - if pos <> ~1 andalso pos <> Vector.length eKeys then - let - val key = IntSet.sub (eKeys, pos) - in - if pos = key then - (* may need to update record in eVals if it is shorter *) - let - val {distance = oldDist, ...} = ValSet.sub (eVals, pos) - in - if dist < oldDist then - (* update values as we found a shorter path *) - let - val eVals = - ValSet.updateAtIdx - (eVals, {distance = dist, from = fromPlatID}, pos) - val _ = print "UPDATE foldPlatID\n" - in - (eVals, q) - end - else - (* return existing *) + fun insertIfNotExistsOrShorter (dist, eKeys, eVals, foldPlatID, q, fromPlatID) = + let + val pos = IntSet.findInsPos (foldPlatID, eKeys) + in + if pos <> ~1 andalso pos <> Vector.length eKeys then + let + val key = IntSet.sub (eKeys, pos) + in + if pos = key then + (* may need to update record in eVals if it is shorter *) + let + val {distance = oldDist, ...} = ValSet.sub (eVals, pos) + in + if dist < oldDist then + (* update values as we found a shorter path *) + let + val eVals = + ValSet.updateAtIdx + (eVals, {distance = dist, from = fromPlatID}, pos) + in (eVals, q) - end - else - (* key not explored, so add to queue *) - let - val insRecord = - {distance = dist, id = foldPlatID, comesFrom = fromPlatID} - val insPos = DistVec.findInsPos (insRecord, q) - val q = DistVec.insert (q, insRecord, insPos) - val _ = print "INSERT NEW PLAT\n" - in + end + else + (* return existing *) (eVals, q) - end - end - else - (* key not explored, so add to queue *) - let - val insRecord = - {distance = dist, id = foldPlatID, comesFrom = fromPlatID} - val insPos = DistVec.findInsPos (insRecord, q) - val q = DistVec.insert (q, insRecord, insPos) - val _ = print "UPDATE foldPlatID\n" - in - (eVals, q) - end - end + end + else + (* key not explored, so add to queue *) + let + val insRecord = + {distance = dist, id = foldPlatID, comesFrom = fromPlatID} + val insPos = DistVec.findInsPos (insRecord, q) + val q = DistVec.insert (q, insRecord, insPos) + in + (eVals, q) + end + end + else + (* key not explored, so add to queue *) + let + val insRecord = + {distance = dist, id = foldPlatID, comesFrom = fromPlatID} + val insPos = DistVec.findInsPos (insRecord, q) + val q = DistVec.insert (q, insRecord, insPos) + in + (eVals, q) + end + end - type env = - { platforms: GameType.platform vector - , currentPlat: GameType.platform - , eKeys: IntSet.elem vector - , distSoFar: int - } + type env = + { platforms: GameType.platform vector + , currentPlat: GameType.platform + , eKeys: IntSet.elem vector + , distSoFar: int + } (* adds platforms to queue if they have not been explored * or, if they have been explored and distance is smaller than previous, @@ -69,7 +65,7 @@ struct structure Vertical = MakeQuadTreeFold (struct - type env = env + type env = env type state = ValSet.elem vector * DistVec.elem vector @@ -77,28 +73,15 @@ struct let val {platforms, currentPlat, eKeys, distSoFar} = env - val _ = print ("foldPlatID = " ^ Int.toString foldPlatID ^ "\n") - val {y = foldPlatY, ...} = Platform.find (foldPlatID, platforms) val {y = currentPlatY, id = fromPlatID, ...} = currentPlat val newDist = abs (foldPlatY - currentPlatY) + distSoFar in insertIfNotExistsOrShorter - (newDist, eKeys, eVals, foldPlatID, q, fromPlatID) + (newDist, eKeys, eVals, foldPlatID, q, fromPlatID) end end) - fun helpPrint (pos, vec) = - if pos = Vector.length vec then - () - else - let - val {id, distance, comesFrom} = DistVec.sub (vec, pos) - val _ = print ("contains (id = " ^ Int.toString id ^")\n") - in - helpPrint (pos + 1, vec) - end - fun start (currentPlat: GameType.platform, env: env, state, platformTree) = let val {x, y, width, ...} = currentPlat @@ -107,14 +90,8 @@ struct val searchY = y - Constants.jumpLimit val height = Constants.worldHeight - searchY - val _ = print "start fold\n" - val (eVals, q) = - Vertical.foldRegion (x, searchY, width, height, env, state, platformTree) - val _ = print "finish fold\n" - - val _ = print "BuildGraph q contains IDs:\n" - val _ = helpPrint (0, q) - val _ = print "\n" + val (eVals, q) = Vertical.foldRegion + (x, searchY, width, height, env, state, platformTree) in (eVals, q) end diff --git a/fcore/path-finding.sml b/fcore/path-finding.sml index 8c29995..587081f 100644 --- a/fcore/path-finding.sml +++ b/fcore/path-finding.sml @@ -1,235 +1,5 @@ structure PathFinding = struct - (* functor for adding reachable platforms to queue *) - structure FindReachable = - struct - open GameType - - type env = - { platforms: GameType.platform vector - , currentPlat: GameType.platform - , eKeys: IntSet.elem vector - , distSoFar: int - } - - type state = ValSet.elem vector * DistVec.elem vector - - fun isBetween (p1, check, p2) = check >= p1 andalso check <= p2 - - fun canJumpUpTo (prevPlat: platform, currentPlat: platform) = - let - val {x = prevX, y = prevY, width = prevWidth, ...} = prevPlat - val {x = curX, y = curY, width = curWidth, ...} = currentPlat - - val prevFinishX = prevX + prevWidth - val curFinishX = curX + curWidth - in - (isBetween (prevX, curX, prevFinishX) - orelse - isBetween (prevX, curFinishX, prevFinishX) andalso prevY + 155 >= curY) - end - - fun canDropDownTo (prevPlat: platform, currentPlat: platform) = - let - val {x = prevX, y = prevY, width = prevWidth, ...} = prevPlat - val {x = curX, y = curY, width = curWidth, ...} = currentPlat - - val prevFinishX = prevX + prevWidth - val curFinishX = curX + curWidth - in - (isBetween (prevX, curX, prevFinishX) - orelse isBetween (prevX, curFinishX, prevFinishX) andalso prevY <= curY) - end - - fun isReachableFromLeft (prevPlat, currentPlat) = - (* prev = right/from, current = left/to *) - let - val {x = prevX, y = prevY, width = prevWidth, ...} = prevPlat - val {x = curX, y = curY, width = curWidth, ...} = currentPlat - - val enemyX = prevX - val xDiff = prevX - curX - in - if xDiff <= Constants.jumpLimit then - true - else - let - val enemyApexX = enemyX - Constants.jumpLimit - val enemyApexY = prevY + Constants.jumpLimit - - val curFinishX = curX + curWidth - - val diffApexY = enemyApexY - curY - val diffApexX = enemyApexX - curFinishX - in - diffApexX <= diffApexY orelse diffApexY <= 0 - end - end - - fun isReachableFromRight (prevPlat, currentPlat) = - (* prev = left/from, current = right/to *) - let - val {x = prevX, y = prevY, width = prevWidth, ...} = prevPlat - val {x = curX, y = curY, width = curWidth, ...} = currentPlat - - (* last x coordinate where enemy can fully fit on prevPlat *) - val enemyX = prevX + prevWidth - Constants.enemySize - - val xDiff = curX - prevX - in - if xDiff <= Constants.jumpLimit then - (* platform is possible to jump to without falling *) - true - else - let - val enemyApexX = enemyX + Constants.jumpLimit - val enemyApexY = prevY + Constants.jumpLimit - - val diffApexY = enemyApexY - curY - val diffApexX = enemyApexX - curX - in - diffApexY <= 0 orelse diffApexX <= diffApexY - end - end - - fun insertIfNotExistsOrShorter - (dist, eKeys, eVals, foldPlatID, q, fromPlatID) = - let - val pos = IntSet.findInsPos (foldPlatID, eKeys) - in - if pos <> ~1 andalso pos <> Vector.length eKeys then - let - val key = IntSet.sub (eKeys, pos) - in - if pos = key then - (* may need to update record in eVals if it is shorter *) - let - val {distance = oldDist, ...} = ValSet.sub (eVals, pos) - in - if dist < oldDist then - (* update values as we found a shorter path *) - let - val eVals = - ValSet.updateAtIdx - (eVals, {distance = dist, from = fromPlatID}, pos) - in - (eVals, q) - end - else - (* return existing *) - (eVals, q) - end - else - (* key not explored, so add to queue *) - let - val insRecord = - {distance = dist, id = foldPlatID, comesFrom = fromPlatID} - val insPos = DistVec.findInsPos (insRecord, q) - val q = DistVec.insert (q, insRecord, insPos) - in - (eVals, q) - end - end - else - (* key not explored, so add to queue *) - let - val insRecord = - {distance = dist, id = foldPlatID, comesFrom = fromPlatID} - val insPos = DistVec.findInsPos (insRecord, q) - val q = DistVec.insert (q, insRecord, insPos) - in - (eVals, q) - end - end - - fun fState ((eVals, q), env, foldPlatID) = - let - val {platforms, currentPlat, eKeys, distSoFar} = env - val curPlatID = #id currentPlat - val foldPlat = Platform.find (foldPlatID, platforms) - in - if - canJumpUpTo (currentPlat, foldPlat) - orelse canDropDownTo (currentPlat, foldPlat) - then - let - (* only need to calculate vertical distance *) - val {y = py, ...} = currentPlat - val {y = cy, ...} = foldPlat - val dist = abs (py - cy) - - val dist = dist + distSoFar - in - insertIfNotExistsOrShorter - (dist, eKeys, eVals, foldPlatID, q, curPlatID) - end - else if - isReachableFromLeft (currentPlat, foldPlat) - orelse isReachableFromRight (currentPlat, foldPlat) - then - let - val {x = px, y = py, width = pw, ...} = currentPlat - val {x = cx, y = cy, width = cw, ...} = foldPlat - - val pFinishX = px + pw - val cFinishX = cx + cw - - val dist = - if py = cy then - let - (* if on same y coordinate, - * only need to calculate horizontal distance *) - val d1 = abs (px - cx) - val d2 = abs (px - cFinishX) - val d3 = abs (pFinishX - cx) - val d4 = abs (pFinishX - cFinishX) - - val min = Int.min (d1, d2) - val min = Int.min (min, d3) - in - (* divide by 2 so we prefer straight horizontal paths over - * diagonal ones by giving horizontal ones a lower cost *) - Int.min (min, d4) + distSoFar - end - else - let - (* if they have different y coordinate, - * need to calculate diagonal length/hypotenuse by pythagoras - * *) - val x1 = abs (px - cx) - val x2 = abs (px - cFinishX) - val x3 = abs (pFinishX - cx) - val x4 = abs (pFinishX - cFinishX) - - val x = Int.min (x1, x2) - val x = Int.min (x, x3) - val x = Int.min (x, x4) - - (* there is only one y coordinate for platform - * so don't need to 'minimise' it - * *) - val y = abs (py - cy) - - (* pythagoras *) - val xsq = x * x - val ysq = y * y - val hypsq = xsq + ysq - - (* square root to find diagonal length *) - val dg = Real.fromInt hypsq - val dg = Math.sqrt dg - in - Real.toInt IEEEReal.TO_NEAREST dg + distSoFar - end - in - insertIfNotExistsOrShorter - (dist, eKeys, eVals, foldPlatID, q, curPlatID) - end - else - (eVals, q) - end - end - fun filterMinDuplicates (q, eKeys) = if DistVec.isEmpty q then q @@ -265,73 +35,51 @@ struct fun getPathList (pID, eID, eKeys, eVals) = helpGetPathList (pID, eID, eKeys, eVals, []) - fun addPlatforms (pos, (eVals, q), env) = - let - val {platforms, ...} = env - in - if pos = Vector.length platforms then - (eVals, q) - else - let - val foldPlat = Vector.sub (platforms, pos) - val foldPlatID = #id foldPlat - val (eVals, q) = FindReachable.fState ((eVals, q), env, foldPlatID) - in - addPlatforms (pos + 1, (eVals, q), env) - end - end - fun loop (pID, eID, platforms, platformTree, q, eKeys, eVals) = - let - (* filtering duplicates because we have no decrease-key operation *) - val q = filterMinDuplicates (q, eKeys) - in - if IntSet.contains (pID, eKeys) then - (* return path if we explored pid *) - getPathList (pID, eID, eKeys, eVals) - else (* continue dijkstra's algorithm *) if DistVec.isEmpty q then - (* return empty list to signify that there is no path *) - [] - else - (* find reachable values from min in quad tree *) - let - val {distance = distSoFar, id = minID, comesFrom} = DistVec.findMin q - val plat = Platform.find (minID, platforms) + if IntSet.contains (pID, eKeys) then + (* return path if we explored pid *) + getPathList (pID, eID, eKeys, eVals) + else + (* continue dijkstra's algorithm *) + let + (* filtering duplicates because we have no decrease-key operation *) + val q = filterMinDuplicates (q, eKeys) + in + if DistVec.isEmpty q then + (* return empty list to signify that there is no path *) + [] + else + (* find reachable values from min in quad tree *) + let + val {distance = distSoFar, id = minID, comesFrom} = + DistVec.findMin q + val plat = Platform.find (minID, platforms) - (* add explored *) - val insPos = IntSet.findInsPos (minID, eKeys) - val eKeys = IntSet.insert (eKeys, minID, insPos) - val eVals = - ValSet.insert - (eVals, {distance = distSoFar, from = comesFrom}, insPos) + (* add explored *) + val insPos = IntSet.findInsPos (minID, eKeys) + val eKeys = IntSet.insert (eKeys, minID, insPos) + val eVals = + ValSet.insert + (eVals, {distance = distSoFar, from = comesFrom}, insPos) - (* on each loop, increment distSoFar by 15. - * Result: paths that require jumps to fewer platforms are - * incentivised a little bit. *) - val env = - { platforms = platforms - , currentPlat = plat - , eKeys = eKeys - , distSoFar = distSoFar + 15 - } + (* on each loop, increment distSoFar by 15. + * Result: paths that require jumps to fewer platforms are + * incentivised a little bit. *) + val env = + { platforms = platforms + , currentPlat = plat + , eKeys = eKeys + , distSoFar = distSoFar + 15 + } - val state = (eVals, q) - - (* calculate area to fold over quad tree *) - val ww = Constants.worldWidth - val wh = Constants.worldHeight - - val {x, y, width, ...} = plat - val y = y - Constants.jumpLimit - val height = wh - y - - (* fold over quad tree, updating any distances - * we find the shortest path for *) - val (eVals, q) = addPlatforms (0, (eVals, q), env) - in - loop (pID, eID, platforms, platformTree, q, eKeys, eVals) - end - end + (* fold over quad tree, updating any distances + * we find the shortest path for *) + val (eVals, q) = + BuildGraph.start (plat, env, (eVals, q), platformTree) + in + loop (pID, eID, platforms, platformTree, q, eKeys, eVals) + end + end fun start (pID, eID, platforms, platformTree) = let diff --git a/oms.mlb b/oms.mlb index bd04d07..91105b2 100644 --- a/oms.mlb +++ b/oms.mlb @@ -26,6 +26,7 @@ fcore/player-patch.sml fcore/enemy-patch.sml fcore/physics.sml +fcore/build-graph.sml fcore/path-finding.sml fcore/trace-jump.sml