get path-finding.sml to use function in build-graph.sml to find reachable platforms, and remove now-dead code from path-finding.sml

This commit is contained in:
2025-01-30 13:06:32 +00:00
parent 755e5da7f7
commit bbb957f016
3 changed files with 98 additions and 372 deletions

View File

@@ -1,64 +1,60 @@
structure BuildGraph = structure BuildGraph =
struct struct
fun insertIfNotExistsOrShorter fun insertIfNotExistsOrShorter (dist, eKeys, eVals, foldPlatID, q, fromPlatID) =
(dist, eKeys, eVals, foldPlatID, q, fromPlatID) = let
let val pos = IntSet.findInsPos (foldPlatID, eKeys)
val pos = IntSet.findInsPos (foldPlatID, eKeys) in
in if pos <> ~1 andalso pos <> Vector.length eKeys then
if pos <> ~1 andalso pos <> Vector.length eKeys then let
let val key = IntSet.sub (eKeys, pos)
val key = IntSet.sub (eKeys, pos) in
in if pos = key then
if pos = key then (* may need to update record in eVals if it is shorter *)
(* may need to update record in eVals if it is shorter *) let
let val {distance = oldDist, ...} = ValSet.sub (eVals, pos)
val {distance = oldDist, ...} = ValSet.sub (eVals, pos) in
in if dist < oldDist then
if dist < oldDist then (* update values as we found a shorter path *)
(* update values as we found a shorter path *) let
let val eVals =
val eVals = ValSet.updateAtIdx
ValSet.updateAtIdx (eVals, {distance = dist, from = fromPlatID}, pos)
(eVals, {distance = dist, from = fromPlatID}, pos) in
val _ = print "UPDATE foldPlatID\n"
in
(eVals, q)
end
else
(* return existing *)
(eVals, q) (eVals, q)
end end
else else
(* key not explored, so add to queue *) (* return existing *)
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
(eVals, q) (eVals, q)
end end
end else
else (* key not explored, so add to queue *)
(* key not explored, so add to queue *) let
let val insRecord =
val insRecord = {distance = dist, id = foldPlatID, comesFrom = fromPlatID}
{distance = dist, id = foldPlatID, comesFrom = fromPlatID} val insPos = DistVec.findInsPos (insRecord, q)
val insPos = DistVec.findInsPos (insRecord, q) val q = DistVec.insert (q, insRecord, insPos)
val q = DistVec.insert (q, insRecord, insPos) in
val _ = print "UPDATE foldPlatID\n" (eVals, q)
in end
(eVals, q) end
end else
end (* 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 = type env =
{ platforms: GameType.platform vector { platforms: GameType.platform vector
, currentPlat: GameType.platform , currentPlat: GameType.platform
, eKeys: IntSet.elem vector , eKeys: IntSet.elem vector
, distSoFar: int , distSoFar: int
} }
(* adds platforms to queue if they have not been explored (* adds platforms to queue if they have not been explored
* or, if they have been explored and distance is smaller than previous, * or, if they have been explored and distance is smaller than previous,
@@ -69,7 +65,7 @@ struct
structure Vertical = structure Vertical =
MakeQuadTreeFold MakeQuadTreeFold
(struct (struct
type env = env type env = env
type state = ValSet.elem vector * DistVec.elem vector type state = ValSet.elem vector * DistVec.elem vector
@@ -77,28 +73,15 @@ struct
let let
val {platforms, currentPlat, eKeys, distSoFar} = env val {platforms, currentPlat, eKeys, distSoFar} = env
val _ = print ("foldPlatID = " ^ Int.toString foldPlatID ^ "\n")
val {y = foldPlatY, ...} = Platform.find (foldPlatID, platforms) val {y = foldPlatY, ...} = Platform.find (foldPlatID, platforms)
val {y = currentPlatY, id = fromPlatID, ...} = currentPlat val {y = currentPlatY, id = fromPlatID, ...} = currentPlat
val newDist = abs (foldPlatY - currentPlatY) + distSoFar val newDist = abs (foldPlatY - currentPlatY) + distSoFar
in in
insertIfNotExistsOrShorter insertIfNotExistsOrShorter
(newDist, eKeys, eVals, foldPlatID, q, fromPlatID) (newDist, eKeys, eVals, foldPlatID, q, fromPlatID)
end end
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) = fun start (currentPlat: GameType.platform, env: env, state, platformTree) =
let let
val {x, y, width, ...} = currentPlat val {x, y, width, ...} = currentPlat
@@ -107,14 +90,8 @@ struct
val searchY = y - Constants.jumpLimit val searchY = y - Constants.jumpLimit
val height = Constants.worldHeight - searchY val height = Constants.worldHeight - searchY
val _ = print "start fold\n" val (eVals, q) = Vertical.foldRegion
val (eVals, q) = (x, searchY, width, height, env, state, platformTree)
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"
in in
(eVals, q) (eVals, q)
end end

View File

@@ -1,235 +1,5 @@
structure PathFinding = structure PathFinding =
struct 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) = fun filterMinDuplicates (q, eKeys) =
if DistVec.isEmpty q then if DistVec.isEmpty q then
q q
@@ -265,73 +35,51 @@ struct
fun getPathList (pID, eID, eKeys, eVals) = fun getPathList (pID, eID, eKeys, eVals) =
helpGetPathList (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) = fun loop (pID, eID, platforms, platformTree, q, eKeys, eVals) =
let if IntSet.contains (pID, eKeys) then
(* filtering duplicates because we have no decrease-key operation *) (* return path if we explored pid *)
val q = filterMinDuplicates (q, eKeys) getPathList (pID, eID, eKeys, eVals)
in else
if IntSet.contains (pID, eKeys) then (* continue dijkstra's algorithm *)
(* return path if we explored pid *) let
getPathList (pID, eID, eKeys, eVals) (* filtering duplicates because we have no decrease-key operation *)
else (* continue dijkstra's algorithm *) if DistVec.isEmpty q then val q = filterMinDuplicates (q, eKeys)
(* return empty list to signify that there is no path *) in
[] if DistVec.isEmpty q then
else (* return empty list to signify that there is no path *)
(* find reachable values from min in quad tree *) []
let else
val {distance = distSoFar, id = minID, comesFrom} = DistVec.findMin q (* find reachable values from min in quad tree *)
val plat = Platform.find (minID, platforms) let
val {distance = distSoFar, id = minID, comesFrom} =
DistVec.findMin q
val plat = Platform.find (minID, platforms)
(* add explored *) (* add explored *)
val insPos = IntSet.findInsPos (minID, eKeys) val insPos = IntSet.findInsPos (minID, eKeys)
val eKeys = IntSet.insert (eKeys, minID, insPos) val eKeys = IntSet.insert (eKeys, minID, insPos)
val eVals = val eVals =
ValSet.insert ValSet.insert
(eVals, {distance = distSoFar, from = comesFrom}, insPos) (eVals, {distance = distSoFar, from = comesFrom}, insPos)
(* on each loop, increment distSoFar by 15. (* on each loop, increment distSoFar by 15.
* Result: paths that require jumps to fewer platforms are * Result: paths that require jumps to fewer platforms are
* incentivised a little bit. *) * incentivised a little bit. *)
val env = val env =
{ platforms = platforms { platforms = platforms
, currentPlat = plat , currentPlat = plat
, eKeys = eKeys , eKeys = eKeys
, distSoFar = distSoFar + 15 , distSoFar = distSoFar + 15
} }
val state = (eVals, q) (* fold over quad tree, updating any distances
* we find the shortest path for *)
(* calculate area to fold over quad tree *) val (eVals, q) =
val ww = Constants.worldWidth BuildGraph.start (plat, env, (eVals, q), platformTree)
val wh = Constants.worldHeight in
loop (pID, eID, platforms, platformTree, q, eKeys, eVals)
val {x, y, width, ...} = plat end
val y = y - Constants.jumpLimit end
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
fun start (pID, eID, platforms, platformTree) = fun start (pID, eID, platforms, platformTree) =
let let

View File

@@ -26,6 +26,7 @@ fcore/player-patch.sml
fcore/enemy-patch.sml fcore/enemy-patch.sml
fcore/physics.sml fcore/physics.sml
fcore/build-graph.sml
fcore/path-finding.sml fcore/path-finding.sml
fcore/trace-jump.sml fcore/trace-jump.sml