コード例 #1
0
    /// <summary>
    /// Finds something for the AI to seek towards (we'll go with closest)
    /// </summary>
    /// <param name="graph">The scene graph</param>
    /// <param name="state">The state of the level</param>
    /// <returns></returns>
    private LogicalCell findAIGoal(LogicalCellGraph graph, ILevelState state)
    {
        int         closestItemDistance = int.MaxValue;
        LogicalCell closestItemCell     = null;
        var         stats = state.GameStats;

        foreach (var i in state.ActiveItems)
        {
            if ((!stats.ExitUnlocked && i.EndsLevel) || (stats.ExitUnlocked && !i.EndsLevel))
            {
                continue;
            }

            var logicalPosition = i.LogicalLocation;
            var cell            = graph.LookupCell(logicalPosition.x, logicalPosition.y);
            var(accessible, distance) = enemyWeightGraph.LookupDistance(cell);

            if (!accessible)
            {
                continue;
            }

            if (distance < closestItemDistance)
            {
                closestItemDistance = distance;
                closestItemCell     = cell;
            }
        }

        return(closestItemCell);
    }
コード例 #2
0
 /// <summary>
 /// Sets the neighbor linked cells for this cell
 /// </summary>
 /// <param name="top">The first neighboring cell to the top</param>
 /// <param name="left">The first neighboring cell to the left</param>
 /// <param name="right">The first neighboring cell to the right</param>
 /// <param name="bottom">The first neighboring cell to the bottom</param>
 public void SetNeighbors(LogicalCell top, LogicalCell left, LogicalCell right, LogicalCell bottom)
 {
     neighborReferences[0] = top;
     neighborReferences[1] = left;
     neighborReferences[2] = right;
     neighborReferences[3] = bottom;
 }
コード例 #3
0
    /// <summary>
    /// Finds the shortest distance from precalculated starting location to the given target.
    /// Adjacent cells and teleports (per special game rules) count as 1 distance point.
    /// </summary>
    /// <param name="target">The cell to find the distance to</param>
    /// <returns>1. True if cell is accessible, False if blocked 2. The shortest distance</returns>
    public (bool accessible, int distance) LookupDistance(LogicalCell target)
    {
        bool accessible = false;
        int  distance   = 0;

        if (distancesByCell.ContainsKey(target))
        {
            int d = distancesByCell[target];
            if (d != int.MaxValue)
            {
                accessible = true;
                distance   = d;
            }
        }

        return(accessible : accessible, distance : distance);
    }
コード例 #4
0
    /// <summary>
    /// Finds the shortest path from precalculated starting location to the given target
    /// Adjacent cells and teleports (per special game rules) count as 1 distance point.
    /// </summary>
    /// <param name="target">The cell to find the path to</param>
    /// <returns>1. True if cell is accessible, False if blocked 2. The shortest path</returns>
    public (bool accessible, LogicalPath path) LookupShortestPath(LogicalCell target)
    {
        LogicalPath path       = new LogicalPath();
        var         p          = new List <LogicalCell>();
        bool        accessible = false;

        if (distancesByCell[target] != int.MaxValue || target == startingLocation)
        {
            LogicalCell currentCell = target;
            while (currentCell != null)
            {
                accessible = true;
                path.PrependPath(currentCell.Loc);
                currentCell = paths[currentCell];
            }
        }

        return(accessible : accessible, path : path);
    }
コード例 #5
0
 /// <summary>
 /// Extracts the logical space from a cell, likely taken from the cell graph
 /// </summary>
 /// <param name="cell">The cell to extract</param>
 /// <returns>The logical space of the cell</returns>
 public static Vector3Int GetLogicalSpaceFromCell(LogicalCell cell)
 {
     return(new Vector3Int(cell.X, cell.Y, 0));
 }
コード例 #6
0
    /// <summary>
    /// Builds a new precalculated Dijkstra graph based on the starting location.
    /// </summary>
    /// <param name="graph">The cell graph we are working from</param>
    /// <param name="startingLocation">The location common to all futher distance calculations</param>
    /// <param name="maxDistance">The maximum distance away from the starting location the graph will store. If left as 0, it counts as infinite.</param>
    /// <param name="allowNeighborTeleportation">This game has special rules to allow traversal on non-adjacent cells. Passing false will disable this rule making it more like the assignment likely intended (but that would be less fun!)</param>
    /// <returns>The baked/calculated Dijkstra graph</returns>
    public static DijkstraWeightGraph BuildDijkstraWeightGraph(LogicalCellGraph graph, LogicalCell startingLocation, int maxDistance = 0, bool allowNeighborTeleportation = false)
    {
        DijkstraWeightGraph g = new DijkstraWeightGraph();

        g.startingLocation = startingLocation;

        var distances      = new Dictionary <LogicalCell, int>();
        var path           = new Dictionary <LogicalCell, LogicalCell>();
        var remainingCells = new HashSet <LogicalCell>();

        foreach (LogicalCell cell in graph.Cells)
        {
            distances[cell] = int.MaxValue;
            path[cell]      = null;
            remainingCells.Add(cell);
        }
        distances[startingLocation] = 0;

        while (remainingCells.Any())
        {
            LogicalCell currentCell         = remainingCells.First();
            int         currentCellDistance = int.MaxValue;
            foreach (LogicalCell cell in remainingCells)
            {
                var d = distances[cell];
                if (d < currentCellDistance)
                {
                    currentCellDistance = d;
                    currentCell         = cell;
                }
            }
            remainingCells.Remove(currentCell);

            if (currentCellDistance != int.MaxValue && (maxDistance == 0 || currentCellDistance + 1 <= maxDistance))
            {
                foreach (var neighbor in currentCell.Neighbors)
                {
                    if (neighbor == null)
                    {
                        continue;
                    }

                    // The special rule for this game allows non-adjacent traversal on matching colors.
                    if (!allowNeighborTeleportation)
                    {
                        bool adjacent = (Math.Abs(neighbor.X - currentCell.X) <= 1 && neighbor.Y == currentCell.Y) ||
                                        (Math.Abs(neighbor.Y - currentCell.Y) <= 1 && neighbor.X == currentCell.X);
                        if (!adjacent)
                        {
                            continue; // Skip
                        }
                    }

                    if (!remainingCells.Contains(neighbor))
                    {
                        continue; // Skip
                    }

                    int newDistance = currentCellDistance + 1;

                    if (newDistance < distances[neighbor])
                    {
                        distances[neighbor] = newDistance;
                        path[neighbor]      = currentCell;
                    }
                }
            }
        }

        g.distancesByCell = distances;
        g.paths           = path;

        return(g);
    }
コード例 #7
0
    /// <summary>
    /// Builds a logical cell grid from the game data
    /// </summary>
    /// <param name="tilemap">The game map</param>
    /// <param name="gateLocations">The locations of all the locked doors/gates</param>
    /// <returns>A logical cell graph which contains useful neighboring data</returns>
    public static LogicalCellGraph BuildCellGraph(IMap tilemap, IEnumerable <Vector3Int> gateLocations)
    {
        LogicalCellGraph graph = new LogicalCellGraph();
        var logicalSize        = tilemap.CellBounds.size / 2;
        var sizeX = logicalSize.x;
        var sizeY = logicalSize.y;

        int[,,] colors       = new int[sizeX, logicalSize.y, 4];
        LogicalCell[,] cells = new LogicalCell[sizeX, logicalSize.y];
        var gateLocationsSet = new HashSet <Vector3Int>();

        foreach (var g in gateLocations)
        {
            gateLocationsSet.Add(g);
        }

        for (int y = 0; y < sizeY; ++y)
        {
            for (int x = 0; x < sizeX; ++x)
            {
                var cell = new LogicalCell {
                    X = x, Y = y
                };
                var logicalSpace = GridSpaceConversion.GetLogicalSpaceFromCell(cell);

                if (gateLocationsSet.Contains(logicalSpace))
                {
                    colors[x, y, 0] = 0; // Reserved for gates
                    colors[x, y, 1] = 0;
                    colors[x, y, 2] = 0;
                    colors[x, y, 3] = 0;
                }
                else
                {
                    var gridSpace = GridSpaceConversion.GetGridSpaceFromLogical(logicalSpace, tilemap);

                    // I wouldn't typically depend on render state for logical stuff,
                    // But this game will be all about color so I think it's OK.
                    colors[x, y, 0] = tilemap.GetColor(new Vector3Int(gridSpace.x, gridSpace.y, 0)).GetHashCode();
                    colors[x, y, 1] = tilemap.GetColor(new Vector3Int(gridSpace.x + 1, gridSpace.y, 0)).GetHashCode();
                    colors[x, y, 2] = tilemap.GetColor(new Vector3Int(gridSpace.x, gridSpace.y + 1, 0)).GetHashCode();
                    colors[x, y, 3] = tilemap.GetColor(new Vector3Int(gridSpace.x + 1, gridSpace.y + 1, 0)).GetHashCode();
                }

                cells[x, y] = cell;
            }
        }

        for (int y = 0; y < sizeY; ++y)
        {
            for (int x = 0; x < sizeX; ++x)
            {
                HashSet <int> colorsInThisCell = new HashSet <int>(new int[] {
                    colors[x, y, 0],
                    colors[x, y, 1],
                    colors[x, y, 2],
                    colors[x, y, 3],
                });

                LogicalCell thisCell = cells[x, y];

                LogicalCell upNeighbor = null;
                for (int up = y + 1; up < sizeY; ++up)
                {
                    if (colorsInThisCell.Contains(colors[x, up, 0]) ||
                        colorsInThisCell.Contains(colors[x, up, 1]) ||
                        colorsInThisCell.Contains(colors[x, up, 2]) ||
                        colorsInThisCell.Contains(colors[x, up, 3]))
                    {
                        upNeighbor = cells[x, up];
                        break;
                    }
                }

                LogicalCell downNeighbor = null;
                for (int down = y - 1; down >= 0; --down)
                {
                    if (colorsInThisCell.Contains(colors[x, down, 0]) ||
                        colorsInThisCell.Contains(colors[x, down, 1]) ||
                        colorsInThisCell.Contains(colors[x, down, 2]) ||
                        colorsInThisCell.Contains(colors[x, down, 3]))
                    {
                        downNeighbor = cells[x, down];
                        break;
                    }
                }

                LogicalCell rightNeighbor = null;
                for (int right = x + 1; right < sizeX; ++right)
                {
                    if (colorsInThisCell.Contains(colors[right, y, 0]) ||
                        colorsInThisCell.Contains(colors[right, y, 1]) ||
                        colorsInThisCell.Contains(colors[right, y, 2]) ||
                        colorsInThisCell.Contains(colors[right, y, 3]))
                    {
                        rightNeighbor = cells[right, y];
                        break;
                    }
                }

                LogicalCell leftNeighbor = null;
                for (int left = x - 1; left >= 0; --left)
                {
                    if (colorsInThisCell.Contains(colors[left, y, 0]) ||
                        colorsInThisCell.Contains(colors[left, y, 1]) ||
                        colorsInThisCell.Contains(colors[left, y, 2]) ||
                        colorsInThisCell.Contains(colors[left, y, 3]))
                    {
                        leftNeighbor = cells[left, y];
                        break;
                    }
                }

                thisCell.SetNeighbors(upNeighbor, leftNeighbor, rightNeighbor, downNeighbor);
            }
        }

        graph.indexedCells = cells;

        return(graph);
    }