private List<Direction> _PathfinderAStar(Coords start, Coords endTopLeft, Coords endBottomRight, BitArray[] _passabilityMap, hFunction h)
        {
            // NOTE: Should later implemented a collision predictor mechanic to work in tandem
            // with the path-finder to provide better agent behavior.
            // NOTE: Consider returning the number of tiles scanned in case no path is found.
            // This will alert a boxed-in creature of its predicament.
            // NOTE: Introduce a flag for a straight-line initial check(for outdoors environmens and
            // for when the goal is near).

            Int32 rangeX = _passabilityMap.Length;
            Int32 rangeY = _passabilityMap[0].Count;

            NodeAStar?[,] nodeArray = new NodeAStar?[rangeX, rangeY];

            NodeAStar startNode = new NodeAStar();
            startNode.costSoFar = 0;
            startNode.estimatedTotalCost = h(start);

            nodeArray[start.X, start.Y] = startNode;

            List<Coords> ListOpen = new List<Coords>();
            ListOpen.Add(start);
            while (ListOpen.Count > 0)
            {
                // I have to use this bool the way I've implemented the algo. Consider rewriting.
                bool resortList = false;

                Coords currentCoords = ListOpen.First();
                // Check to see if goal is reached.
                //if (currentCoords.Equals(endTopLeft))
                if (StaticMathFunctions.CoordinateIsInBox(currentCoords, endTopLeft, endBottomRight))
                {
                    break;
                }

                NodeAStar currentNode = nodeArray[currentCoords.X, currentCoords.Y].Value;
                for (byte i = 0; i <= 3; ++i)
                {
                    Direction currentDir = (Direction)(2 * i + 1);
                    Coords dirCoords = StaticMathFunctions.DirectionToCoords(currentDir);
                    Coords potential = currentCoords + dirCoords;
                    // check if move in dir is allowed
                    if (potential.X >= 0 && potential.X < rangeX && potential.Y >= 0 && potential.Y < rangeY // bounds check
                        && _passabilityMap[potential.X][potential.Y]) // passability check
                    {
                        // Using the simplest cost function possible. Can be easily updated
                        // once tile walkability coefficients are added.
                        Coords newNodePosition = new Coords(CoordsType.General, currentCoords.X + dirCoords.X, currentCoords.Y + dirCoords.Y);
                        float accruedCost = currentNode.costSoFar + Constants.MovementCost[(byte)currentDir];

                        // Straight line correction
                        if (currentDir == nodeArray[currentCoords.X, currentCoords.Y].Value.connection)
                        {
                            accruedCost -= Constants.PathfinderStraightPathCorrection;
                        }

                        // Check to see if the node under examination is in the closed list.
                        //NodeAStar? oldNode = nodeArray[newNodePosition.X, newNodePosition.Y];
                        if (nodeArray[newNodePosition.X, newNodePosition.Y] != null)
                        {
                            // If node is in closed list, see if it needs updating.
                            if (nodeArray[newNodePosition.X, newNodePosition.Y].Value.costSoFar > accruedCost)
                            {
                                float expectedAdditionalCost =
                                    nodeArray[newNodePosition.X, newNodePosition.Y].Value.estimatedTotalCost -
                                    nodeArray[newNodePosition.X, newNodePosition.Y].Value.costSoFar;
                                NodeAStar nodeToAdd =
                                    new NodeAStar(currentDir, accruedCost, accruedCost + expectedAdditionalCost);
                                nodeArray[newNodePosition.X, newNodePosition.Y] = nodeToAdd;
                                ListOpen.Add(newNodePosition);
                                resortList = true;
                            }
                        }
                        // Node is in open list. Process it.
                        else
                        {
                            float expectedAdditionalCost = h(newNodePosition);
                            NodeAStar nodeToAdd =
                                new NodeAStar(currentDir, accruedCost, accruedCost + expectedAdditionalCost);
                            nodeArray[newNodePosition.X, newNodePosition.Y] = nodeToAdd;
                            ListOpen.Add(newNodePosition);
                            resortList = true;
                        }
                    }
                }

                ListOpen.RemoveAt(0);
                if (resortList)
                {
                    ListOpen.Sort(
                        delegate(Coords c1, Coords c2)
                        {
                            float difference = nodeArray[c1.X, c1.Y].Value.estimatedTotalCost -
                                nodeArray[c2.X, c2.Y].Value.estimatedTotalCost;

                            Int32 returnValue = 0;
                            if (difference > 0)
                            {
                                returnValue = 1;
                            }
                            else if (difference < 0)
                            {
                                returnValue = -1;
                            }
                            return returnValue;
                        }
                    );
                }
            }

            List<Direction> ListRoute = new List<Direction>();

            // Return empty route if the open list is empty, i.e. there is no path to the target
            // Ideally, the game logic should be fixed so that the search isn't even attempted
            // if there is no path between the two points.
            if (ListOpen.Count == 0)
            {
                return ListRoute;
            }

            Coords trackbackCoords = endTopLeft;
            while (trackbackCoords != start)
            {
                Direction newDirection = nodeArray[trackbackCoords.X, trackbackCoords.Y].Value.connection;
                ListRoute.Add(newDirection);
                trackbackCoords = StaticMathFunctions.CoordsNeighboringInDirection(new Coords(CoordsType.Tile, trackbackCoords),
                    StaticMathFunctions.OppositeDirection(newDirection));
            }

            // Might be faster without reversing
            //ListRoute.Reverse();

            // We skip the reversal, so pick directions from the END of the list.
            return ListRoute;
        }
Exemplo n.º 2
0
        private List<Direction> _PathfinderAStar(Coords start, Coords endTopLeft, Coords endBottomRight, BitArray[] _passabilityMap, hFunction h)
        {
            // NOTE: Should later implemented a collision predictor mechanic to work in tandem
            // with the path-finder to provide better agent behavior.
            // NOTE: Consider returning the number of tiles scanned in case no path is found.
            // This will alert a boxed-in creature of its predicament.
            // NOTE: Introduce a flag for a straight-line initial check(for outdoors environmens and
            // for when the goal is near).

            Int32 rangeX = _passabilityMap.Length;
            Int32 rangeY = _passabilityMap[0].Count;

            NodeAStar?[,] nodeArray = new NodeAStar?[rangeX, rangeY];

            NodeAStar startNode = new NodeAStar();
            startNode.costSoFar = 0;
            startNode.estimatedTotalCost = h(start);

            nodeArray[start.X, start.Y] = startNode;

            List<Coords> ListOpen = new List<Coords>();
            ListOpen.Add(start);
            while (ListOpen.Count > 0)
            {
                // I have to use this bool the way I've implemented the algo. Consider rewriting.
                bool resortList = false;

                Coords currentCoords = ListOpen.First();
                // Check to see if goal is reached.
                //if (currentCoords.Equals(endTopLeft))
                if (StaticMathFunctions.CoordinateIsInBox(currentCoords, endTopLeft, endBottomRight))
                {
                    break;
                }

                NodeAStar currentNode = nodeArray[currentCoords.X, currentCoords.Y].Value;
                for (byte i = 0; i <= 5; ++i)
                {
                    Direction currentDir = (Direction)(i);
                    //Coords dirCoords = StaticMathFunctions.DirectionToCoords(currentDir);
                    Coords potential = StaticMathFunctions.CoordsNeighboringInDirection(currentCoords, currentDir);
                    // check if move in dir is allowed
                    if (potential.X >= 0 && potential.X < rangeX && potential.Y >= 0 && potential.Y < rangeY // bounds check
                        && _passabilityMap[potential.X][potential.Y]) // passability check
                    {
                        // Using the simplest cost function possible. Can be easily updated
                        // once tile walkability coefficients are added.
                        //Coords newNodePosition = new Coords(CoordsType.General, currentCoords.X + dirCoords.X, currentCoords.Y + dirCoords.Y);
                        Coords newNodePosition = potential;
                        float accruedCost = currentNode.costSoFar + 1;

                        // Straight line correction
                        if (currentDir == nodeArray[currentCoords.X, currentCoords.Y].Value.connection)
                        {
                            accruedCost -= Constants.PathfinderStraightPathCorrection;
                        }

                        // Check to see if the node under examination is in the closed list.
                        //NodeAStar? oldNode = nodeArray[newNodePosition.X, newNodePosition.Y];
                        if (nodeArray[newNodePosition.X, newNodePosition.Y] != null)
                        {
                            // If node is in closed list, see if it needs updating.
                            if (nodeArray[newNodePosition.X, newNodePosition.Y].Value.costSoFar > accruedCost)
                            {
                                float expectedAdditionalCost =
                                    nodeArray[newNodePosition.X, newNodePosition.Y].Value.estimatedTotalCost -
                                    nodeArray[newNodePosition.X, newNodePosition.Y].Value.costSoFar;
                                NodeAStar nodeToAdd =
                                    new NodeAStar(currentDir, accruedCost, accruedCost + expectedAdditionalCost);
                                nodeArray[newNodePosition.X, newNodePosition.Y] = nodeToAdd;
                                ListOpen.Add(newNodePosition);
                                resortList = true;
                            }
                        }
                        // Node is in open list. Process it.
                        else
                        {
                            float expectedAdditionalCost = h(newNodePosition);
                            NodeAStar nodeToAdd =
                                new NodeAStar(currentDir, accruedCost, accruedCost + expectedAdditionalCost);
                            nodeArray[newNodePosition.X, newNodePosition.Y] = nodeToAdd;
                            ListOpen.Add(newNodePosition);
                            resortList = true;
                        }
                    }
                }

                ListOpen.RemoveAt(0);
                if (resortList)
                {
                    ListOpen.Sort(
                        delegate(Coords c1, Coords c2)
                        {
                            float difference = nodeArray[c1.X, c1.Y].Value.estimatedTotalCost -
                                nodeArray[c2.X, c2.Y].Value.estimatedTotalCost;

                            Int32 returnValue = 0;
                            if (difference > 0)
                            {
                                returnValue = 1;
                            }
                            else if (difference < 0)
                            {
                                returnValue = -1;
                            }
                            return returnValue;
                        }
                    );
                }
            }

            List<Direction> ListRoute = new List<Direction>();

            // Return empty route if the open list is empty, i.e. there is no path to the target
            // Ideally, the game logic should be fixed so that the search isn't even attempted
            // if there is no path between the two points.
            if (ListOpen.Count == 0)
            {
                return ListRoute;
            }

            Coords trackbackCoords = endTopLeft;
            while (trackbackCoords != start)
            {
                Direction newDirection = nodeArray[trackbackCoords.X, trackbackCoords.Y].Value.connection;
                ListRoute.Add(newDirection);
                trackbackCoords = StaticMathFunctions.CoordsNeighboringInDirection(new Coords(CoordsType.Tile, trackbackCoords),
                    StaticMathFunctions.OppositeDirection(newDirection));
            }

            // Might be faster without reversing
            //ListRoute.Reverse();

            // We skip the reversal, so pick directions from the END of the list.
            return ListRoute;
        }