/// <summary> /// If multiple paths are existing to a square, at a branch, always chose the first one in the X,Y order. /// </summary> public static Path SearchOptimalPath(VisitedSquare to) { var ret = new Path(); var current = to; while (current.VisitedFrom != null) { ret.Steps.AddFirst(current.Coordinate); current = current.VisitedFrom.OrderBy(sq => sq.Coordinate.X).ThenBy(sq => sq.Coordinate.Y).First(); } return(ret); }
/// <summary> /// Searches for the shortest path for each reachable enemy's adjacent square. /// </summary> static public IDictionary <Player, List <Path> > ReachableEnemiesAndShortestPaths(Map map, Player fromPlayer) { bool newSquareVisitedThisStep = true; List <VisitedSquare> visitedSquares = new List <VisitedSquare>() { new VisitedSquare() { Coordinate = fromPlayer.Location, VisitedFrom = null, InStep = 0 } }; int currentStep = 1; var wallLocations = map.Walls.Select(w => w.Location); var playerLocations = map.Players.Select(p => p.Location); // Visit the map starting from the given location // Create a structure where the visited squares are stored // also store that in which step they have been visited and from which square while (newSquareVisitedThisStep) { newSquareVisitedThisStep = false; var squaresFromLastStep = visitedSquares.Where(vs => vs.InStep == currentStep - 1).ToList(); var squaresAlreadyVisitedBeforeCurrentStep = visitedSquares.Select(s => s.Coordinate).ToList(); foreach (var square in squaresFromLastStep) { var possiblenewCoordinates = new List <Coordinate>() { new Coordinate(square.Coordinate.X + 1, square.Coordinate.Y), new Coordinate(square.Coordinate.X - 1, square.Coordinate.Y), new Coordinate(square.Coordinate.X, square.Coordinate.Y + 1), new Coordinate(square.Coordinate.X, square.Coordinate.Y - 1), }; foreach (var newCoordinate in possiblenewCoordinates) { if (wallLocations.Contains(newCoordinate) || playerLocations.Contains(newCoordinate) || squaresAlreadyVisitedBeforeCurrentStep.Contains(newCoordinate)) { continue; } var visitedSquare = visitedSquares.FirstOrDefault(sq => sq.Coordinate == newCoordinate); if (visitedSquare == null) { var squareToAdd = new VisitedSquare() { Coordinate = newCoordinate, InStep = currentStep, }; squareToAdd.VisitedFrom.Add(square); visitedSquares.Add(squareToAdd); } else // square has been already visited from an other square in this step { visitedSquare.VisitedFrom.Add(square); } newSquareVisitedThisStep = true; } } currentStep++; } // Calculate the squares which are adjacent to enemies // and they can be possibly reached (no wall, no other player unless its the current player already standing there) var adjacentPossiblyReachableSquaresToEnemies = new Dictionary <Player, List <Coordinate> >(); var enemies = map.Players.Where(p => p.Team != fromPlayer.Team); foreach (var enemy in enemies) { var enemyLocation = enemy.Location; adjacentPossiblyReachableSquaresToEnemies.Add(enemy, new List <Coordinate>()); var rightSquare = new Coordinate(enemyLocation.X + 1, enemyLocation.Y); if ((!wallLocations.Contains(rightSquare) && !playerLocations.Contains(rightSquare)) || rightSquare == fromPlayer.Location) { adjacentPossiblyReachableSquaresToEnemies[enemy].Add(rightSquare); } var leftSquare = new Coordinate(enemyLocation.X - 1, enemyLocation.Y); if ((!wallLocations.Contains(leftSquare) && !playerLocations.Contains(leftSquare)) || leftSquare == fromPlayer.Location) { adjacentPossiblyReachableSquaresToEnemies[enemy].Add(leftSquare); } var upSquare = new Coordinate(enemyLocation.X, enemyLocation.Y + 1); if ((!wallLocations.Contains(upSquare) && !playerLocations.Contains(upSquare)) || upSquare == fromPlayer.Location) { adjacentPossiblyReachableSquaresToEnemies[enemy].Add(upSquare); } var downSquare = new Coordinate(enemyLocation.X, enemyLocation.Y - 1); if ((!wallLocations.Contains(downSquare) && !playerLocations.Contains(downSquare)) || downSquare == fromPlayer.Location) { adjacentPossiblyReachableSquaresToEnemies[enemy].Add(downSquare); } } // search these adjacent square in the visit structure. // Create the optimal path to each of them. var enemiesWithPathsToAdjacentSquares = new Dictionary <Player, List <Path> >(); foreach (KeyValuePair <Player, List <Coordinate> > enemyWithAdjacentSqures in adjacentPossiblyReachableSquaresToEnemies) { foreach (Coordinate adjSquare in enemyWithAdjacentSqures.Value) { var visited = visitedSquares.FirstOrDefault(vs => vs.Coordinate == adjSquare); if (visited != null) { var paths = SearchOptimalPath(visited); if (!enemiesWithPathsToAdjacentSquares.ContainsKey(enemyWithAdjacentSqures.Key)) { enemiesWithPathsToAdjacentSquares.Add(enemyWithAdjacentSqures.Key, new List <Path>()); } enemiesWithPathsToAdjacentSquares[enemyWithAdjacentSqures.Key].Add(paths); } } } // Out of all reachable adjacent squares of an enemy, search the shortest ones, and return them var enemiesWithPathsToClosestAdjacentSquares = new Dictionary <Player, List <Path> >(); foreach (var item in enemiesWithPathsToAdjacentSquares) { int minLength = item.Value.Min(p => p.Length); enemiesWithPathsToClosestAdjacentSquares.Add(item.Key, item.Value.Where(p => p.Length == minLength).ToList()); } return(enemiesWithPathsToClosestAdjacentSquares); }