Question

I have code that is supposed to find the shortest path from point A to point B. To do this i am using a A-star variation. I am using a 2d array to represent a 2d grid but my path does not take diagonal shortcuts, only left, right, up, and down. So far everything works fine except it does not always find the shortest path possible. I want to know what is going wrong, why it is going wrong, and how I can fix it. Thank you in advance.

Here is a picture to illustrate what exactly is happening:

A-Star gone wrong example

and here is my code (path finding class first, then its helper class):

BTW: Math vector is nothing more than just a geometric point class, and both playerTileLocation and enemyTileLocation are just points that correspond to the start and end nodes on the grid. Also i use the class AStarNode as the nodes for all the tiles on the map, instead of a regular object.

package {
import src.Characters.Character;
import src.InGame.Map;
import src.Maths.MathVector;

public final class BaseAI {
// REPRESENTS UP, DOWN, RIGHT, AND LEFT OF ANY ONE NODE  
    private static const bordersOfNode:Array = new Array(
        new MathVector( -1, 0), new MathVector(1, 0), new MathVector(0, -1), new MathVector(0, 1));


    private var _player:Character;
    private var map:Map;

    private var playerTileLocation:MathVector;



    private var openList:Array;
    private var closedList:Array;
// 2D ARRAY OF MAP TILES (I DON'T USE HERE, BUT I PLAN TO IN FUTURE)  
    private var mapArray:Array;
    private var originNode:AStarNode;
    private var complete:Boolean;


    public function BaseAI(_player:Character,map:Map):void {
        this._player = _player;
        this.map = map;

        openList = new Array();
        closedList = new Array();
        mapArray = map.tiles;
    }
    public function get player():Character {
        return this._player;
    }

    public function calculatePlayerTileLocation():void {
        playerTileLocation = map.worldToTilePoint(player.groundPosition);
    }
//WILL EVENTUAL RETURN A DIRECTION FOR THE ENEMY TO TAKE THAT ITERATION (EVERY 1-2 SECONDS)  
    public function getDirection(enemy:Character):String {
        var enemyTileLocation:MathVector = map.worldToTilePoint(enemy.groundPosition);


        originNode = new AStarNode(enemyTileLocation, playerTileLocation);
        originNode.setAsOrigin();

        openList = [originNode];
        closedList = [];

        complete = false;
        var currentNode:AStarNode;
        var examiningNode:AStarNode;

        while (!complete) {

            openList.sortOn("F", Array.NUMERIC);
            currentNode = openList[0];
            closedList.push(currentNode);
            openList.splice(0, 1);

            for (var i in bordersOfNode) {
                examiningNode = new AStarNode(new MathVector(currentNode.X + bordersOfNode[i].x, currentNode.Y + bordersOfNode[i].y),playerTileLocation);

                if (map.isOpenTile(map.getTile(examiningNode.X, examiningNode.Y)) && !examiningNode.isThisInArray(closedList)) {
                    if (!examiningNode.isThisInArray(openList)) {
                        openList.push(examiningNode);
                        examiningNode.parentNode = currentNode;
                    }else {

                    }
                    if (examiningNode.X == playerTileLocation.x && examiningNode.Y == playerTileLocation.y) {
                        complete = true;
                        var done:Boolean = false;
                        var thisNode:AStarNode;
                        thisNode = examiningNode;
                        while (!done) {
                            if (thisNode.checkIfOrigin()) {
                                done = true;
                            }else {
                                thisNode = thisNode.parentNode;
                            }
                        }
                    }
                }
            }
        }
    }
}

}

package {
import src.Maths.MathVector;

internal final class AStarNode {
    private var _X:int;
    private var _Y:int;

    private var _G:int;
    private var _H:int;
    private var _F:int;
    private var _parentNode:AStarNode;

    private var _isOrigin:Boolean;

    public static const VERTICAL:uint = 10;

    public function AStarNode(thisNodeLocation:MathVector, targetNodeLocation:MathVector) {
        X = thisNodeLocation.x;
        Y = thisNodeLocation.y;
        H = Math.abs(X - targetNodeLocation.x) + Math.abs(Y - targetNodeLocation.y);
        G = 0;
        F = H + G;
    }

    public function set X(newX:int):void {
        this._X = newX;
    }
    public function get X():int {
        return this._X;
    }

    public function set Y(newY:int):void {
        this._Y = newY;
    }
    public function get Y():int {
        return this._Y;
    }

    public function set G(newG:int):void {
        this._G = newG;
    }
    public function get G():int {
        return this._G;
    }

    public function set H(newH:int):void {
        this._H = newH;
    }
    public function get H():int {
        return this._H;
    }

    public function set F(newF:int):void {
        this._F = newF;
    }
    public function get F():int {
        return this._F;
    }

    public function set parentNode(newParentNode:AStarNode):void {
        this._parentNode = newParentNode;
    }
    public function get parentNode():AStarNode {
        return this._parentNode;
    }

    public function setAsOrigin():void {
        _isOrigin = true;
    }
    public function checkIfOrigin():Boolean {
        return _isOrigin;
    }

    public function isThisInArray(arrayToCheck:Array):Boolean {
        for (var i in arrayToCheck) {
            if (arrayToCheck[i].X == this.X && arrayToCheck[i].Y == this.Y) {
                return true
            }
        }
        return false
    }
}
enter code here

}

Was it helpful?

Solution

A quick glance through your code raises the idea of wrong heuristics. Your G value is always 0 in a node, at lease I do not see where it could change. However, in A-star algorithm for your task (finding the shortest path with obstacles) it should represent the number of steps already made to reach the cell. That would allow the algorithm to replace the long path with a shorter one.

OTHER TIPS

The one time I coded an A star 'algorithm' I used a 2-dimensional Array for the grid (as you have). At the start of the search each grid location's 'searched' property was set to false. Each grid location would also have an Array of connecting directions; options that the player could choose to move in - some might be open, some might be blocked and inaccessible.
I would start the search by checking the starting grid position for how many direction options it had. For each option I would push a 'path' Array into a _paths Array. Each 'path' Array would end up containing a sequence of 'moves' (0 for up, 1 for right, 2 for down and 3 for left). So for each initial path, I would push in the corresponding starting move. I would also set the grid position's 'searched' property to true.
I would then iterate through each path, running through that sequence of moves to get to the most recently added location. I would check if that location was the target location. If not I would mark that location as searched then check which directions were available, ignoring locations that had already been searched. If non were available, the path would be closed and 'spliced' from the Array of paths.
Otherwise ByteArray 'deep copies' of the current path Array were made for each available move option, in excess of the first move option. A move in one direction was added to the current path and the new paths, in their respective directions.
If the number of paths ever reaches 0, there is not a path between the 2 locations. I think that was about it. I hope that's helpful.
Note that the search does not need to be 'directed' toward the target; what I've suggested searches all possible paths and just 'happens' to find the most direct route by killing paths that try to check locations that have already been searched (meaning some other path has got there first and is therefore shorter).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top