Exemplo n.º 1
0
    public CharacterBase CheckIfTileOccupied(INodeSearchable node)
    {
        Tile tile = node as Tile;

        if (tile.occupier != null)
        {
            return(tile.occupier);
        }
        return(null);
    }
Exemplo n.º 2
0
    //An implementation of a best first search which uses a single heuristic.
    //A priority queue with a self-balancing binary tree would be better optimization.
    //For now, a list that sorts after an insert will work for a quick/dirty implementaion.
    INodeSearchable BestFirst(INodeSearchable startNode, INodeSearchable targetNode, EHeuristic heuristic)
    {
        //Queue<INodeSearchable> nodeQueue = new Queue<INodeSearchable>();
        //nodeQueue.Enqueue(startNode);
        List <INodeSearchable> nodeList = new List <INodeSearchable>();

        nodeList.Add(startNode);

        INodeSearchable currentNode;

        //INodeSearchable bestNode = null;
        //float bestDistance = 0;

        while (nodeList.Count > 0)
        {
            //currentNode = nodeQueue.Dequeue();
            currentNode = nodeList[0];
            nodeList.RemoveAt(0);

            if (currentNode == targetNode)
            {
                return(currentNode);
            }
            else
            {
                currentNode.searched = true;

                foreach (var child in currentNode.children)
                {
                    if (child == null)
                    {
                        continue;
                    }
                    if (!child.searched && !nodeList.Contains(child))
                    {
                        child.parent = currentNode;
                        CalculateHeuristic(targetNode, child, heuristic);

                        nodeList.Add(child);
                        TileDistanceComparison compare = new TileDistanceComparison();
                        nodeList.Sort(compare);
                    }
                }

                //if(bestNode != null)
                //{
                //    nodeQueue.Enqueue(bestNode);
                //    bestNode = null;
                //}
            }
        }
        //Queue empty, target not found: return null as a fail state
        return(null);
    }
Exemplo n.º 3
0
    //Finds the current depth of the given node during or after a recent search.
    int SearchDepth(INodeSearchable node)
    {
        INodeSearchable givenNode = node;
        int             depth     = 0;

        while (givenNode.parent != null)
        {
            depth    += 1;
            givenNode = givenNode.parent;
        }
        return(depth);
    }
Exemplo n.º 4
0
    //A Dijkstra Search implementation focused on finding the most efficent way to move to a target tile given the environemental cost to get there.
    INodeSearchable DijkstraSearch(INodeSearchable startNode, INodeSearchable targetNode, List <INodeSearchable> searchSet, ECostType costType)
    {
        INodeSearchable currentNode;

        currentNode = startNode;

        foreach (var node in searchSet)
        {
            node.DijkstraCost = null;
        }
        //Ensures we don't check our starting node twice, and that our starting node is at the front of our "queue"
        searchSet.Remove(currentNode);
        searchSet.Insert(0, currentNode);
        currentNode.DijkstraCost = 0;

        while (searchSet.Count > 0)
        {
            currentNode = searchSet[0];
            searchSet.RemoveAt(0);
            searchSet.TrimExcess();

            if (currentNode == targetNode)
            {
                return(targetNode);
            }
            else if (currentNode.DijkstraCost == null)
            {
                //Remaining nodes are assumed unreachable, no path to target, return null as a fail state
                return(null);
            }
            else
            {
                foreach (var child in currentNode.children)
                {
                    if (child == null)
                    {
                        continue;
                    }
                    bool reassigned = CalculateDijkstra(currentNode, child, costType);
                    if (reassigned)
                    {
                        child.parent = currentNode;
                    }
                    child.TotalCost = child.DijkstraCost;
                }
            }

            IComparer <INodeSearchable> nullback = new SortNullToBackByTotalCost();
            searchSet.Sort(nullback);
        }

        return(null);
    }
Exemplo n.º 5
0
    //For path part of pathfinding

    public Stack <INodeSearchable> CreatePath(INodeSearchable endingNode)
    {
        Stack <INodeSearchable> nodePath = new Stack <INodeSearchable>();

        nodePath.Push(endingNode);
        INodeSearchable currentNode = endingNode;

        while (currentNode.parent != null)
        {
            nodePath.Push(currentNode.parent);
            currentNode = currentNode.parent;
        }
        return(nodePath);
    }
Exemplo n.º 6
0
    public static int BreadthFirstAllySearch(INodeSearchable startNode)
    {
        int numberOfAllys = 0;
        Queue <INodeSearchable> nodeQueue = new Queue <INodeSearchable>();

        nodeQueue.Enqueue(startNode);

        INodeSearchable currentNode;

        while (nodeQueue.Count > 0)
        {
            currentNode = nodeQueue.Dequeue();

            currentNode.searched = true;

            if ((currentNode as Tile).occupier is EnemyCharacter)
            {
                numberOfAllys++;
            }
            else
            {
                if (currentNode.children.Contains(startNode) || currentNode == startNode)
                {
                    foreach (var child in currentNode.children)
                    {
                        if (child == null)
                        {
                            continue;
                        }
                        if (!child.searched && !nodeQueue.Contains(child))
                        {
                            //child.parent = currentNode;
                            nodeQueue.Enqueue(child);
                        }
                    }
                }
            }
        }
        //Queue empty, target not found: return null as a fail state
        return(numberOfAllys);
    }
Exemplo n.º 7
0
    public void CalculateHeuristic(INodeSearchable targetNode, INodeSearchable child, EHeuristic heuristic)
    {
        switch (heuristic)
        {
        case EHeuristic.Distance:
            //Checking what the node is: We could attach an enum to the interface which allows us
            //to know what it is. Maybe? Could be bad coding practice; do research when possible.
            //Add error checking. eg: expected object tile got object {x}
            Tile targetTile = targetNode as Tile;
            Tile childTile  = child as Tile;

            Vector3 childPos   = childTile.transform.position;
            Vector3 targetPos  = targetTile.transform.position;
            Vector3 difference = targetPos - childPos;

            float distance = Vector3.Distance(childPos, targetPos);

            child.HeuristicCost = distance;

            //if(distance < bestDistance || bestDistance == 0)
            //{
            //    bestNode = child;
            //    bestDistance = distance;
            //}

            //nodeList.Add(childTile);
            //TileDistanceComparison compare = new TileDistanceComparison();
            //nodeList.Sort(compare);
            break;

        case EHeuristic.Manhattan:
            throw new System.NotImplementedException("Manhattan Style Distance Calculation not yet implemented.");

        //break;
        default:
            //bestNode = null;
            Debug.LogError("No heuristic provided for Pathfinding Agent func BestFirst search with target: " + targetNode);
            break;
        }
    }
Exemplo n.º 8
0
    //A generic implementation of a breadth first search algorithm.
    //Anything that inherits from INodeSearchable should be able to use this.
    //example: Tile inherits from INodeSearchable. If you were to replace the word "node" with "tile"
    //you should see how this works.
    INodeSearchable BreadthFirstBasic(INodeSearchable startNode, INodeSearchable targetNode)
    {
        Queue <INodeSearchable> nodeQueue = new Queue <INodeSearchable>();

        nodeQueue.Enqueue(startNode);

        INodeSearchable currentNode;

        while (nodeQueue.Count > 0)
        {
            currentNode = nodeQueue.Dequeue();

            if (currentNode == targetNode)
            {
                return(currentNode);
            }
            else
            {
                currentNode.searched = true;
                foreach (var child in currentNode.children)
                {
                    if (child == null)
                    {
                        continue;
                    }
                    if (!child.searched && !nodeQueue.Contains(child))
                    {
                        child.parent = currentNode;
                        nodeQueue.Enqueue(child);
                    }
                }
            }
        }
        //Queue empty, target not found: return null as a fail state
        return(null);
    }
Exemplo n.º 9
0
    //Return: True if cost is reassigned, False if it is not;
    public bool CalculateDijkstra(INodeSearchable currentNode, INodeSearchable child, ECostType costType)
    {
        float?calculatedCost = currentNode.DijkstraCost;

        switch (costType)
        {
        case ECostType.Movement:

            calculatedCost += CalculateMoveCostToReachChildTile(currentNode, child);

            break;

        default:
            break;
        }

        if ((calculatedCost < child.DijkstraCost || child.DijkstraCost == null) && currentNode.parent != child)
        {
            child.DijkstraCost = calculatedCost;
            //child.parent = currentNode;
            return(true);
        }
        return(false);
    }
Exemplo n.º 10
0
    public List <INodeSearchable> FindNodeSightRange(INodeSearchable startNode, int sightRange)
    {
        Queue <INodeSearchable> nodeQueue         = new Queue <INodeSearchable>();
        List <INodeSearchable>  nodesInSightRange = new List <INodeSearchable>();

        nodeQueue.Enqueue(startNode);

        INodeSearchable currentNode;

        while (nodeQueue.Count > 0)
        {
            currentNode = nodeQueue.Dequeue();

            currentNode.searched = true;
            foreach (var child in currentNode.children)
            {
                if (child == null)
                {
                    continue;
                }

                if (!child.searched && !nodeQueue.Contains(child))
                {
                    child.parent = currentNode;
                    int depth = SearchDepth(child);
                    if (depth <= sightRange)
                    {
                        nodeQueue.Enqueue(child);
                        nodesInSightRange.Add(child);
                    }
                }
            }
        }

        return(nodesInSightRange);
    }
Exemplo n.º 11
0
 private void AssignNeighborToChildren(EDirection direction, INodeSearchable tileNode)
 {
     children[(int)direction] = tileNode;
 }
Exemplo n.º 12
0
    public float?CalculateMoveCostToReachChildTile(INodeSearchable parent, INodeSearchable child)
    {
        Tile           tileChild         = child as Tile;
        Tile           tileParent        = parent as Tile;
        EDirection     directionToParent = EDirection.Error;
        EDirection     directionToChild;
        EWallDirection wallDirectionToChild;
        EWallDirection wallDirectionToParent;
        float?         wallCostFromChild  = null;
        float?         wallCostFromParent = null;
        float?         floorCostFromChild = null;

        for (int i = 0; i < (int)EDirection.Error; i++)
        {
            if (tileChild.neighbors[i] != null)
            {
                if (tileChild.neighbors[i] == tileParent)
                {
                    directionToParent = (EDirection)i;
                    break;
                }
            }
        }

        directionToChild = FlipDirection(directionToParent);

        switch (directionToChild)
        {
        case EDirection.North:
        case EDirection.West:
        case EDirection.South:
        case EDirection.East:
            wallDirectionToChild  = EDirectionToEWallDirection(directionToChild);
            wallDirectionToParent = EDirectionToEWallDirection(directionToParent);

            wallCostFromChild  = tileChild.walls[(int)wallDirectionToParent].moveCost;
            wallCostFromParent = tileParent.walls[(int)wallDirectionToChild].moveCost;
            floorCostFromChild = tileChild.terrainType.moveCost;
            break;

        case EDirection.NorthEast:
            if ((tileChild.walls[(int)EWallDirection.South].moveCost != 0 || tileChild.walls[(int)EWallDirection.West].moveCost != 0) ||
                (tileParent.walls[(int)EWallDirection.North].moveCost != 0 || tileParent.walls[(int)EWallDirection.East].moveCost != 0))
            {
                wallCostFromChild  = 999;
                wallCostFromParent = 999;
            }
            else
            {
                wallCostFromChild  = 0;
                wallCostFromParent = 0;
            }
            floorCostFromChild = tileChild.terrainType.moveCost;
            break;

        case EDirection.SouthEast:
            if ((tileChild.walls[(int)EWallDirection.North].moveCost != 0 || tileChild.walls[(int)EWallDirection.West].moveCost != 0) ||
                (tileParent.walls[(int)EWallDirection.South].moveCost != 0 || tileParent.walls[(int)EWallDirection.East].moveCost != 0))
            {
                wallCostFromChild  = 999;
                wallCostFromParent = 999;
            }
            else
            {
                wallCostFromChild  = 0;
                wallCostFromParent = 0;
            }
            floorCostFromChild = tileChild.terrainType.moveCost;
            break;

        case EDirection.SouthWest:
            if ((tileChild.walls[(int)EWallDirection.North].moveCost != 0 || tileChild.walls[(int)EWallDirection.East].moveCost != 0) ||
                (tileParent.walls[(int)EWallDirection.South].moveCost != 0 || tileParent.walls[(int)EWallDirection.West].moveCost != 0))
            {
                wallCostFromChild  = 999;
                wallCostFromParent = 999;
            }
            else
            {
                wallCostFromChild  = 0;
                wallCostFromParent = 0;
            }
            floorCostFromChild = tileChild.terrainType.moveCost;
            break;

        case EDirection.NorthWest:
            if ((tileChild.walls[(int)EWallDirection.South].moveCost != 0 || tileChild.walls[(int)EWallDirection.East].moveCost != 0) ||
                (tileParent.walls[(int)EWallDirection.North].moveCost != 0 || tileParent.walls[(int)EWallDirection.West].moveCost != 0))
            {
                wallCostFromChild  = 999;
                wallCostFromParent = 999;
            }
            else
            {
                wallCostFromChild  = 0;
                wallCostFromParent = 0;
            }
            floorCostFromChild = tileChild.terrainType.moveCost;
            break;

        case EDirection.Error:
            break;

        default:

            break;
        }

        return(wallCostFromChild + wallCostFromParent + floorCostFromChild);
    }
Exemplo n.º 13
0
    //@Param startNode: The INodeSearchable object the search should start from.
    //@Param targetNode: The INodeSearchable object the search should be attempting to reach.
    //@Param searchSet: A list containing all nodes to be searched/in the search range.
    public INodeSearchable AStarBasic(INodeSearchable startNode, INodeSearchable targetNode, List <INodeSearchable> searchSet, ECostType costType, EHeuristic heuristic, float dijkstraWeight, float heuristicWeight)
    {
        //A* selects the path that minimizes:
        //f(x) = g(x) + h(x)
        //Where g(x) is the cost of the node, and h(x) is the cost of heursitic

        INodeSearchable currentNode;

        currentNode = startNode;

        foreach (var node in searchSet)
        {
            node.DijkstraCost  = null;
            node.HeuristicCost = null;
            node.TotalCost     = null;
        }
        //Ensures we don't check our starting node twice, and that our starting node is at the front of our "queue"
        searchSet.Remove(currentNode);
        searchSet.Insert(0, currentNode);

        currentNode.DijkstraCost = 0;
        currentNode.TotalCost    = 0;

        while (searchSet.Count > 0)
        {
            currentNode = searchSet[0];
            searchSet.RemoveAt(0);
            searchSet.TrimExcess();

            if (currentNode == targetNode)
            {
                return(targetNode);
            }
            else if (currentNode.DijkstraCost == null)
            {
                //Remaining nodes are assumed unreachable, no path to target, return null as a fail state
                return(null);
            }
            else
            {
                foreach (var child in currentNode.children)
                {
                    if (child == null)
                    {
                        continue;
                    }
                    //Calc the heuristic cost of chosen heuristic measure: h(x)
                    CalculateHeuristic(targetNode, child, heuristic);

                    //Calc the Dijkstra cost of chosen cost measure g(x)
                    CalculateDijkstra(currentNode, child, costType);

                    //Calc the total cost: f(x) = g(x) + h(x)
                    float?total = child.DijkstraCost * dijkstraWeight + child.HeuristicCost * heuristicWeight;



                    if (total < child.TotalCost || child.TotalCost == null)
                    {
                        child.TotalCost = total;
                        child.parent    = currentNode;

                        if (!searchSet.Contains(child))
                        {
                            searchSet.Add(child);
                        }
                    }
                }
            }

            IComparer <INodeSearchable> nullback = new SortNullToBackByTotalCost();
            searchSet.Sort(nullback);
        }

        return(null);
    }
Exemplo n.º 14
0
    //@Desc: A function that finds all tiles a unit can move to with the next move action.
    //@Param - moveRange : The maximum tile distance the given unit can move with a single movement pip.
    //@Param - startNode : The current node that unit occupies.
    //@Return: A list of all nodes that the unit can move to from its current node.
    //Notes: Might be able to change param to egg/unit object for ease of use. IE object could pass self.
    //public List<INodeSearchable> FindMovementRange(INodeSearchable startNode, float moveValue)
    //{
    //    INodeSearchable currentNode;
    //    List<INodeSearchable> moveRange = new List<INodeSearchable>();
    //    Stack<INodeSearchable> nodeStack = new Stack<INodeSearchable>();
    //    startNode.DijkstraCost = 0;
    //    nodeStack.Push(startNode);

    //    while(nodeStack.Count > 0)
    //    {
    //        currentNode = nodeStack.Pop();
    //        foreach(var child in currentNode.children)
    //        {
    //            if (child != null)
    //            {
    //                bool reassigned = CalculateDijkstra(currentNode, child, ECostType.Movement);
    //                if(child.DijkstraCost <= moveValue)
    //                {
    //                    if (!nodeStack.Contains(child) && reassigned)
    //                    {
    //                        nodeStack.Push(child);
    //                    }
    //                    if (!moveRange.Contains(child))
    //                    {
    //                        //TODO: copy? add as function pass? Whatever, add the cost calculation for the tile.
    //                        moveRange.Add(child);
    //                    }
    //                }

    //            }

    //        }
    //    }

    //    return moveRange;
    //}

    //@Desc: A function that finds all tiles a unit can move to with the next move action.
    //@Param - moveRange : The maximum tile distance the given unit can move with a single movement pip.
    //@Param - startNode : The current node that unit occupies.
    //@Return: A list of all nodes that the unit can move to from its current node.
    //Notes: Might be able to change param to egg/unit object for ease of use. IE object could pass self.
    public List <INodeSearchable> FindMovementRange(INodeSearchable startNode, float moveValue, Action <CharacterBase, Tile> mappingFunction = null, CharacterBase characterToCheckFor = null)
    {
        //INodeSearchable[] tsa = GameObject.FindObjectsOfType<Tile>();
        //List<INodeSearchable> ts = tsa.ToList<INodeSearchable>();
        //NodeReset(ts);
        INodeSearchable         currentNode;
        List <INodeSearchable>  moveRange = new List <INodeSearchable>();
        Stack <INodeSearchable> nodeStack = new Stack <INodeSearchable>();

        startNode.DijkstraCost = 0;
        nodeStack.Push(startNode);

        while (nodeStack.Count > 0)
        {
            currentNode = nodeStack.Pop();
            foreach (var child in currentNode.children)
            {
                if (child != null)
                {
                    bool reassigned = CalculateDijkstra(currentNode, child, ECostType.Movement);

                    if (reassigned)
                    {
                        child.parent = currentNode;
                    }
                    var caller = CheckIfTileOccupied(startNode);

                    if (caller is PlayerCharacter)
                    {
                        var temp = CheckIfTileOccupied(child);
                        if (temp is EnemyCharacter)
                        {
                            //Make impassible though high cost
                            child.DijkstraCost += 999;
                        }
                    }
                    else if (caller is EnemyCharacter)
                    {
                        var temp = CheckIfTileOccupied(child);
                        if (temp is PlayerCharacter)
                        {
                            //make impassible though high cost
                            child.DijkstraCost = +999;
                        }
                    }


                    if (child.DijkstraCost <= moveValue)
                    {
                        if (!nodeStack.Contains(child) && reassigned)
                        {
                            nodeStack.Push(child);
                        }
                        if (!moveRange.Contains(child))
                        {
                            //TODO: copy? add as function pass? Whatever, add the cost calculation for the tile.
                            moveRange.Add(child);
                            if (characterToCheckFor != null && mappingFunction != null)
                            {
                                mappingFunction?.Invoke(characterToCheckFor, (Tile)child);
                                //Works for the decision making as the function to calculate the new value holds the top x tiles to move to and then decides from there;
                            }
                        }
                    }
                }
            }
        }

        return(moveRange);
    }