Ejemplo n.º 1
0
    /// <summary>
    /// Find the vector telling next location to go to.
    /// if fails to find a solution, it returns the target by default
    /// </summary>
    /// <param name='position'>
    /// Position.
    /// </param>
    /// <param name='target'>
    /// Target.
    /// </param>
    /// <param name='mapInfo'>
    /// LevelInfo containing info on the level.
    /// </param>
    public static Vector2 Find(Vector2 position, Vector2 target, LevelInfo mapInfo)
    {
        if (canGoStraightLine(position, target, mapInfo)) return target;

        Vector2 defaultRtrnLoc = target; // by default tell to go straight to target

        if (!mapInfo.inBounds(position)) {
            // this SHOULDN'T happen, you should not be out of bounds of the level
            return defaultRtrnLoc;
        }

        int xPos = (int)position.x;
        int yPos = (int)position.y;
        int xTarget = (int)target.x;
        int yTarget = (int)target.y;

        // use 3th dimension to store c(p) in f(p) = h(p) + c(p)
        PriorityQueue<Vector3> frontier = new PriorityQueue<Vector3>();

        // add start position to frontier
        int cp = 0;
        int fp = getHP(position, target) + cp;
        frontier.enqueue(fp, new Vector3(xPos, yPos, cp));

        // backtracking (cpsc320 anyone?)
        // might not be the optimal method, but it works
        // Vector3, x, y map to coords, z is used for cost
        // z = -1 if visited, non-neg integer if not visited = cost to reach location
        Vector3[,] backtrack = new Vector3[(int)mapInfo.getSize().x, (int)mapInfo.getSize().y];
        // set the backtrack for position to -1,-1
        backtrack[xPos, yPos] = new Vector3(-1, -1, 1);

        for (int i = 0; i < STEP_LIMIT; i++) {
            if (frontier.isEmpty()) {
                return defaultRtrnLoc; // can't reach it...
            }

            // select lowest f(p) = h(p) + c(p)
            Vector3 nextDequeue = frontier.dequeue();
            Vector2 nextLocation = new Vector2(nextDequeue.x, nextDequeue.y);

            // double check that this location has not been visited
            if (backtrack[(int)nextLocation.x, (int)nextLocation.y].z == -1) {
                continue;
            }

            // check if goal
            if ((int)nextLocation.x == xTarget && (int)nextLocation.y == yTarget) {

                // backtrack from this position....
                // find first step to move
                return getBacktrack(backtrack, target, position, mapInfo);
            }
            // mark location as accessed
            backtrack[(int)nextLocation.x, (int)nextLocation.y].z = -1;

            bool verify_diag = false;
            // otherwise add neighbour of those to frontier that:
            for (int deltaX = -1; deltaX <= 1; deltaX++) {
                for (int deltaY = -1; deltaY <=1; deltaY++) {
                    verify_diag = false;
                    if (NO_DIAGONAL_MOVEMENT && (deltaX * deltaY != 0)) {
                        continue;
                    } else {
                        if (deltaX * deltaY != 0) {
                        // verify that we can actually move diagonally
                            verify_diag = true;
                        }
                    }

                    Vector2 neighbour = nextLocation + new Vector2(deltaX, deltaY);

                    int thisCP = (int)nextDequeue.z + Mathf.Abs(deltaX * deltaY) * DIAG_COST
                                    + LINEAR_COST;
                    int thisFP = getHP(neighbour, target) + thisCP;

                    // a) is within boundaries
                    if (!mapInfo.inBounds(neighbour)) { continue; }

                    // b) have not been visited or it's cheaper to go this way
                    Vector3 locInfo = backtrack[(int)neighbour.x, (int)neighbour.y];
                    if (locInfo != Vector3.zero && locInfo.z < thisCP) { continue; }

                    // c) are accessible from this location (walkable as specified by mapInfo)
                    if (mapInfo.isOccupiedBy(neighbour, MapScript.ENEMY_GROUP)) { continue; }
                    if (verify_diag) {
                        if (mapInfo.isOccupiedBy(nextLocation + new Vector2(0, deltaY),
                            MapScript.ENEMY_GROUP)) { continue; }
                        if (mapInfo.isOccupiedBy(nextLocation + new Vector2(deltaX, 0),
                            MapScript.ENEMY_GROUP)) { continue; }
                    }

                    // all criteria satisfied
                    // add to frontier
                    frontier.enqueue(thisFP, new Vector3(neighbour.x, neighbour.y, thisCP));

                    // add to allow backtrack
                    backtrack[(int)neighbour.x, (int)neighbour.y] =
                        new Vector3(nextLocation.x, nextLocation.y, thisCP);
                }
            }
        }
        // outta steps, return the closest one found so far
        if (frontier.isEmpty()) {
            return defaultRtrnLoc; // can't reach it...don't move?
        } else {
            Vector3 nextDequeue = frontier.dequeue();
            Vector2 nextLocation = new Vector2(nextDequeue.x, nextDequeue.y);
            return getBacktrack(backtrack, nextLocation, position, mapInfo);
        }
    }
Ejemplo n.º 2
0
    /***
     * Flee A* implementation
     * - what this does is similiar to A*. It searches and returns the location with the highest "distance"
     *   from the player. It has a limit to the distance it is willing to search (o), and it has a distance
     *   that it (P) refuses to come close to (x) the target (T)
     *
     *   Think of it as a donut shape centered around the target. The width of the donut depends on the distance
     *   between the position and the target
     *    --------------------------
     *    |  ooooooooo              |
     *    | oooooxooooo             |
     *    |ooPooxxxooooo            |
     *    |ooooxxTxxooooo           |
     *    |oooooxxxooooo            |
     *    | oooooxooooo             |
     *    |  ooooooooo              |
     *     -------------------------
     *
     * */
    public static Vector2 Flee(Vector2 position, Vector2 target, LevelInfo mapInfo)
    {
        float minDistancePercentage = 50.0f; //%
        float maxDistancePercentage = 350.0f; //%
        // if position and target are "r" units apart
        // will find cheapest path to farthest location that does not get within r*50% of
        // target, and no farther than r*350% of target
        // (of course, if this is repeatedly called, as you get farther from target
        // your search range increases

        // Alternatively, think of it as how "panic'd" the AI is.
        // if a AI running away from the target is really close
        // it will "panic" and simply tries to get slightly farther away
        // as it reaches farther away, it "relaxes" and makes a better analysis of the
        // possible paths.

        float distance = getDistance(position, target);
        int minDistance = (int)(distance * minDistancePercentage / 100.0f) - 1;
        int maxDistance = (int)(distance * maxDistancePercentage / 100.0f) + 1;

        Vector2 defaultRtrnLoc = position;//position + (position - target); // by default tell to go away

        if (!mapInfo.inBounds(position)) {
            // this SHOULDN'T happen, you should not be out of bounds of the level
            return defaultRtrnLoc;
        }

        int xPos = (int)position.x;
        int yPos = (int)position.y;

        // never go farther than twice the distance (computational resource reasons)

        // use 3rd dimension to store c(p) in f(p) = h(p) + c(p)
        PriorityQueue<Vector3> frontier = new PriorityQueue<Vector3>();

        // farthest location so far
        Vector2 farthestPoint = position;
        int farthestPointFP = int.MaxValue;

        // add start position to frontier
        int cp = 0;
        int fp = getFleeHP(position, target) + cp;
        frontier.enqueue(fp, new Vector3(xPos, yPos, cp));

        // backtracking (cpsc320 anyone?)
        // might not be the optimal method, but it works
        // Vector3, x, y map to coords, z is used for cost
        // z = -1 if visited, non-neg integer if not visited = cost to reach location
        Vector3[,] backtrack = new Vector3[(int)mapInfo.getSize().x, (int)mapInfo.getSize().y];
        // set the backtrack for position to -1,-1
        backtrack[xPos, yPos] = new Vector3(-1, -1, 1);

        for (int i = 0; i < STEP_LIMIT; i++) {
            if (frontier.isEmpty()) {
                //return defaultRtrnLoc; // can't reach it...
                return getBacktrack(backtrack, farthestPoint);
            }

            // select lowest f(p) = h(p) + c(p)
            Vector3 nextDequeue = frontier.dequeue();
            Vector2 nextLocation = new Vector2(nextDequeue.x, nextDequeue.y);

            // double check that this location has not been visited
            if (backtrack[(int)nextLocation.x, (int)nextLocation.y].z == -1) {
                continue;
            }

            // mark location as accessed
            backtrack[(int)nextLocation.x, (int)nextLocation.y].z = -1;

            bool verify_diag = false;
            // otherwise add neighbour of those to frontier that:
            for (int deltaX = -1; deltaX <= 1; deltaX++) {
                for (int deltaY = -1; deltaY <=1; deltaY++) {
                    verify_diag = false;
                    if (NO_DIAGONAL_MOVEMENT && (deltaX * deltaY != 0)) {
                        continue;
                    } else {
                        if (deltaX * deltaY != 0) {
                        // verify that we can actually move diagonally
                            verify_diag = true;
                        }
                    }

                    Vector2 neighbour = nextLocation + new Vector2(deltaX, deltaY);
                    float neighbourDistance = getDistance(neighbour, target);

                    int thisCP = (int)nextDequeue.z + Mathf.Abs(deltaX * deltaY) * DIAG_COST
                                    + LINEAR_COST;
                    // increase the cost if it means we have to go CLOSER to the target (than originally)
        //					if (neighbourDistance > distance && neighbourDistance > 3) {
        //						thisCP += (int)(neighbourDistance - distance);
        //					}
                    int thisFP = getFleeHP(neighbour, target) + thisCP;

                    // a) is within boundaries
                    if (!mapInfo.inBounds(neighbour)) { continue; }

                    // b) have not been visited or it's cheaper to go this way
                    Vector3 locInfo = backtrack[(int)neighbour.x, (int)neighbour.y];
                    if (locInfo != Vector3.zero && locInfo.z < thisCP) { continue; }

                    // c) are accessible from this location (walkable as specified by mapInfo)
                    if (mapInfo.isOccupiedBy(neighbour, MapScript.ENEMY_GROUP)) { continue; }
                    if (verify_diag) {
                        if (mapInfo.isOccupiedBy(nextLocation + new Vector2(0, deltaY),
                            MapScript.ENEMY_GROUP)) { continue; }
                        if (mapInfo.isOccupiedBy(nextLocation + new Vector2(deltaX, 0),
                            MapScript.ENEMY_GROUP)) { continue; }
                    }

                    // d) within our search bounds
                    if (neighbourDistance < minDistance || neighbourDistance > maxDistance) {
                        continue;
                    }

                    // UPDATE farthestPOINT!
                    if (thisFP < farthestPointFP) {
                        farthestPoint = neighbour;
                        farthestPointFP = thisFP;
                    }

                    // all criteria satisfied
                    // add to frontier
                    frontier.enqueue(thisFP, new Vector3(neighbour.x, neighbour.y, thisCP));

                    // add to allow backtrack
                    backtrack[(int)neighbour.x, (int)neighbour.y] =
                        new Vector3(nextLocation.x, nextLocation.y, thisCP);
                }
            }
        }
        // outta steps, return the closest one found so far
        if (frontier.isEmpty()) {
            return getBacktrack(backtrack, farthestPoint); // can't reach it, return farthest found so far
        } else {
            Vector3 nextDequeue = frontier.dequeue();
            Vector2 nextLocation = new Vector2(nextDequeue.x, nextDequeue.y);
            return getBacktrack(backtrack, nextLocation);
        }
    }