/// <summary>
        /// Returns list of possible moves, sorted by
        /// 1) amount of increase, 2) distance to influence map source
        /// THIS METHOD SHOULD BE IMPROVED
        /// </summary>
        public List <Direction> PossibleMoves(Coords currentPosition)
        {
            List <Direction> dirList = new List <Direction>();

            // Dangerous cast?
            TilePassable currentTile = (TilePassable)this._currentMap.GetTile(currentPosition);

            for (byte i = 1; i <= 8; i++)
            {
                Direction currentDir = (Direction)i;
                if (currentTile.AllowedMovesCheckInDirection(currentDir))
                {
                    dirList.Add(currentDir);
                }
            }

            dirList.Sort(
                delegate(Direction d1, Direction d2)
            {
                Coords c1 = StaticMathFunctions.CoordsNeighboringInDirection(currentPosition, d1);
                Coords c2 = StaticMathFunctions.CoordsNeighboringInDirection(currentPosition, d2);

                Int32 returnVal = (this._influenceMap[c1.X, c1.Y]).CompareTo(this._influenceMap[c2.X, c2.Y]);

                if (returnVal == 0)
                {
                    returnVal = (StaticMathFunctions.DistanceBetweenTwoCoordsEucledean(c1, currentPosition)).CompareTo
                                    (StaticMathFunctions.DistanceBetweenTwoCoordsEucledean(c2, currentPosition));
                }

                return(returnVal);
            }
                );

            return(dirList);
        }
Пример #2
0
        /// <summary>
        ///  Analyzes and remembers tile accessibility. Starts at northwest corner and goes through the array,
        ///  checking east / southeast / south / southwest on the current tile and in case of accessibility
        ///  recording the result in both directions.
        /// </summary>
        public void AnalyzeTileAccessibility()
        {
            Tile currentTile;

            for (UInt16 i = 0; i < this._xMax; i++)
            {
                for (UInt16 j = 0; j < this._yMax; j++)
                {
                    Tile east, southEast, south, southWest;
                    currentTile = this._tiles[i, j];
                    if (currentTile is TileImpassable)
                    {
                        continue;
                    }

                    _passabilityMap[i][j] = true;

                    _visibilityMap[i, j] = (currentTile is TilePassable) ? 1 : 0;

                    // Sort of wasteful, hopefully compiler does this smartly
                    if (i < _xMax - 1)
                    {
                        east = this.GetTile(StaticMathFunctions.CoordsNeighboringInDirection(new Coords(CoordsType.Tile, i, j), Direction.East));
                        if (east is TilePassable)
                        {
                            (currentTile as TilePassable).AllowedMovesSet(Direction.East, true);
                            (east as TilePassable).AllowedMovesSet(Direction.West, true);
                        }
                    }

                    if ((i < _xMax - 1) & (j < _yMax - 1))
                    {
                        southEast = this.GetTile(StaticMathFunctions.CoordsNeighboringInDirection(new Coords(CoordsType.Tile, i, j), Direction.Southeast));
                        if (southEast is TilePassable)
                        {
                            (currentTile as TilePassable).AllowedMovesSet(Direction.Southeast, true);
                            (southEast as TilePassable).AllowedMovesSet(Direction.Northwest, true);
                        }
                    }

                    if (j < _yMax - 1)
                    {
                        south = this.GetTile(StaticMathFunctions.CoordsNeighboringInDirection(new Coords(CoordsType.Tile, i, j), Direction.South));
                        if (south is TilePassable)
                        {
                            (currentTile as TilePassable).AllowedMovesSet(Direction.South, true);
                            (south as TilePassable).AllowedMovesSet(Direction.North, true);
                        }
                    }

                    if ((i > 0) & (j < _yMax - 1))
                    {
                        southWest = this.GetTile(StaticMathFunctions.CoordsNeighboringInDirection(new Coords(CoordsType.Tile, i, j), Direction.Southwest));
                        if (southWest is TilePassable)
                        {
                            (currentTile as TilePassable).AllowedMovesSet(Direction.Southwest, true);
                            (southWest as TilePassable).AllowedMovesSet(Direction.Northeast, true);
                        }
                    }
                }
            }
        }
Пример #3
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 <= 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);
        }
        /// Generates the influence map.
        /// Uses a silly recursive algorithm.
        /// Stopping conditions: Let's use two, to avoid stupid infinite loops.
        /// One is a distance threshold check.
        // Second is a min influence threshold check.

        /// <summary>
        /// Generates the influence map.
        /// Uses a silly recursive algorithm.
        /// Stopping conditions: Let's use two, to avoid stupid infinite loops.
        /// One is a distance threshold check.
        /// Second is a min influence threshold check.
        /// </summary>
        public void GenerateInfluenceMap()
        {
            // boolean array to keep note of which tiles have been processed
            //BitArray[,] takenCareOf = new BitArray[_currentMap.BoundX, _currentMap.BoundY];
            BitArray[] takenCareOf = new BitArray[_currentMap.BoundX];
            for (int i = 0; i < _currentMap.BoundX; ++i)
            {
                takenCareOf[i] = new BitArray(_currentMap.BoundY);
            }
            takenCareOf[Source.X][Source.Y] = true;

            // sets up two queues - one for the current pass, one for the next one
            // distance increments by one at each pass
            // if too slow, the process should be broken up so it does a number of passes each tick
            Queue <Coords> currentQueue = new Queue <Coords>();
            Queue <Coords> nextQueue    = new Queue <Coords>();

            currentQueue.Enqueue(_source);

            UInt32 currentDistance = 0;

            // main loop
            // Stopping conditions: the two queues are exhausted, OR InfluenceMapMaxDistance is reached
            while
            (
                ((currentQueue.Count > 0) & (nextQueue.Count > 0))
                |
                (currentDistance < Constants.InfluenceMapMaxDistance)
            )
            {
                // Checks if it's time to start the next pass
                if (currentQueue.Count == 0)
                {
                    currentQueue = nextQueue;
                    nextQueue    = new Queue <Coords>();
                    currentDistance++;
                    continue;
                }

                Coords       currentCoords = currentQueue.Peek();
                TilePassable currentTile   = (TilePassable)CurrentMap.GetTile(currentCoords);

                // Analyzes the neighbors of the current Tile for possible additions to nextQueue
                for (byte i = 1; i <= 8; i++)
                {
                    Direction currentDir = (Direction)i;
                    if (currentTile.AllowedMovesCheckInDirection(currentDir))
                    {
                        Coords toCheck = StaticMathFunctions.CoordsNeighboringInDirection(currentCoords, currentDir);
                        if (!takenCareOf[toCheck.X][toCheck.Y])
                        {
                            nextQueue.Enqueue(toCheck);
                            takenCareOf[toCheck.X][toCheck.Y] = true;
                        }
                    }
                }

                float newVal = _f(currentDistance);

                // Check to avert infnite / excessively deep loop
                if (newVal > Constants.InfluenceMapMinThreshold)
                {
                    this.SetMapValue(currentCoords, newVal);
                }

                currentQueue.Dequeue();
            }
        }