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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user