/// <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); }
/// <summary> /// Ticks the AI's brain. It will move one square each tick currently. /// </summary> /// <param name="graph">The scene graph</param> /// <param name="state">The state of the level</param> public void Tick(LogicalCellGraph graph, ILevelState state) { // So on a bigger game I probably wouldn't do this, // But it seems Djikstra's is performant enough to // Recompute each tick!! (keep in mind, the AI's tick // is only approximately once every second). If this // weren't the case, I would probably go with A*. RebuildGraph(graph); if (enemyPawn == null || enemyWeightGraph == null) { return; } var goal = findAIGoal(graph, state); if (goal == null) { return; } var(accessible, path) = enemyWeightGraph.LookupShortestPath(goal); if (!accessible) { return; } if (!path.Path.Any()) { return; } var nextCell = path.Path.Skip(1).Take(1).FirstOrDefault(); if (nextCell != null) { var truncatedPath = new LogicalPath(); truncatedPath.PrependPath(nextCell); enemyPawn.PushMotionPath(truncatedPath); } }