diff --git a/fcore/enemy-behaviour.sml b/fcore/enemy-behaviour.sml index 53522fb..af5ff6c 100644 --- a/fcore/enemy-behaviour.sml +++ b/fcore/enemy-behaviour.sml @@ -740,18 +740,12 @@ struct fun canJumpOnPlatform (player, platforms, enemy: enemy, platformTree) = let + (* todo: possibly get pID and eID of player/enemy in a different way *) val pID = getPlatformBelowPlayer (player, platformTree, platforms) - val {x, y, ...} = enemy - val eID = getPlatformBelowEnemy (enemy, platformTree, platforms) - val _ = print "start best path:\n" - val bestPath = PathFinding.start (pID, eID, platforms, platformTree) - val _ = List.map (fn c => print (Int.toString c ^ "\n")) bestPath - - val _ = print "finished best path\n\n" val distance = Constants.moveEnemyBy * Constants.jumpLimit diff --git a/fcore/game-type.sml b/fcore/game-type.sml index a4dae89..acd8760 100644 --- a/fcore/game-type.sml +++ b/fcore/game-type.sml @@ -166,7 +166,7 @@ struct val plat3 = {id = 3, x = 355, y = 659, width = 555} val plat4 = {id = 4, x = 155, y = 855, width = 199} val plat5 = {id = 5, x = 155, y = 759, width = 199} - val plat6 = {id = 6, x = 155, y = 610, width = 199} + val plat6 = {id = 6, x = 155, y = 710, width = 199} val platforms = Vector.fromList [plat1, plat2, plat3, plat4, plat5, plat6] val platformTree = Platform.generateTree platforms diff --git a/fcore/path-finding.sml b/fcore/path-finding.sml index b38055c..749098a 100644 --- a/fcore/path-finding.sml +++ b/fcore/path-finding.sml @@ -2,241 +2,231 @@ structure PathFinding = struct (* functor for adding reachable platforms to queue *) structure FindReachable = - struct - open GameType + struct + open GameType - 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 + } - type state = ValSet.elem vector * DistHeap.t + type state = ValSet.elem vector * DistHeap.t - fun isBetween (p1, check, p2) = check >= p1 andalso check <= p2 + 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 + 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 + 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 + 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 + 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 + 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 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 curFinishX = curX + curWidth - val diffApexY = enemyApexY - curY - val diffApexX = enemyApexX - curFinishX - in - diffApexX <= diffApexY orelse diffApexY <= 0 - end - end + 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 + 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 + (* 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 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 + 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 q = - DistHeap.insert - ( { distance = dist - , id = foldPlatID - , comesFrom = fromPlatID - } - , q - ) - in - (eVals, q) - end - end - else - (* key not explored, so add to queue *) - let - val q = - DistHeap.insert - ( { distance = dist - , id = foldPlatID - , comesFrom = fromPlatID - } - , q - ) - in - (eVals, q) - 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 q = + DistHeap.insert + ( {distance = dist, id = foldPlatID, comesFrom = fromPlatID} + , q + ) + in + (eVals, q) + end + end + else + (* key not explored, so add to queue *) + let + val q = + DistHeap.insert + ({distance = dist, id = foldPlatID, comesFrom = fromPlatID}, q) + 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 + 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 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 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 - Int.min (min, d4) - 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 min = Int.min (d1, d2) + val min = Int.min (min, d3) + in + Int.min (min, d4) + 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) + 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) + (* 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 + (* 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 - end - val dist = dist + distSoFar - in - insertIfNotExistsOrShorter - (dist, eKeys, eVals, foldPlatID, q, curPlatID) - end - else - (eVals, q) - end - end + (* square root to find diagonal length *) + val dg = Real.fromInt hypsq + val dg = Math.sqrt dg + in + Real.toInt IEEEReal.TO_NEAREST dg + end + val dist = dist + distSoFar + in + insertIfNotExistsOrShorter + (dist, eKeys, eVals, foldPlatID, q, curPlatID) + end + else + (eVals, q) + end + end fun filterMinDuplicates (q, eKeys) = let @@ -262,7 +252,6 @@ struct val pos = IntSet.findInsPos (curID, eKeys) val {from, distance, ...} = ValSet.sub (eVals, pos) - val _ = print ("266 distance = " ^ Int.toString distance ^ "\n") in helpGetPathList (from, eID, eKeys, eVals, acc) end @@ -271,19 +260,20 @@ struct 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 + 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