public IEnumerable <WaveFront> ContractAndCalculate(GameState gameState, int distance, PlayerCalculator calculator, HashSet <CellState> reachableCells) { bool isWesternMostPoint = true; WaveFront newFront = null; Position easternMostPositionOfNewFront = null; foreach (Position position in GetPointsFromWestToEast()) { CellState cellState = gameState[position]; bool isPointOnNewFront = true; // A flag to indicate whether to keep this point on the new front if (calculator.IsCellOccupied(cellState)) { isPointOnNewFront = false; } else { // See if the point has already been visited: int existingDistance = calculator.GetExistingDistance(cellState); if (existingDistance <= distance) { isPointOnNewFront = (existingDistance == distance) && !position.IsPole && ((isWesternMostPoint && IsWesternPointShared) || (position == EasternPoint && IsEasternPointShared) ); /* This will be false except when this is the shared point on the Western-most edge. * If it's a shared point then the adjacent front may have already set the distance. * So we assume that the distance has already been set by the adjacent front. */ } else { calculator.SetDistance(cellState, distance, reachableCells); } } bool isWesternPointShared = false; if (isWesternMostPoint) { isWesternPointShared = IsWesternPointShared; if (isPointOnNewFront) { if (position.IsPole) { // Create a polar wave front going in the opposite direction: WaveFront polarWaveFront = CreateAReflectedPolarWaveFront(position); yield return(polarWaveFront); isPointOnNewFront = false; isWesternPointShared = false; } else if (!IsWesternPointShared) { // Create a new wave front to expand around any barriers: WaveFront adjacentFrontOnWesternSide = CreateAPointWaveOnTheWesternEdge(position); yield return(adjacentFrontOnWesternSide); isWesternPointShared = true; } } } if (isPointOnNewFront) { if (position.IsPole) { /* Note: The following code assumes that this is a diagonal front. * There is a very small chance that this is a polar front that has reached the opposite pole. * Since this is a very rare event, we can afford the cost of testing every single point in the front. * So don't worry about fixing this. * / * * /* End off current front (if there is one): */ if (newFront != null) { newFront.EasternPoint = easternMostPositionOfNewFront; newFront.IsEasternPointShared = false; yield return(newFront); newFront = null; } /* Create a polar wave front going in the opposite direction: */ WaveFront polarWaveFront = CreateAReflectedPolarWaveFront(position); yield return(polarWaveFront); isPointOnNewFront = false; } else { if (newFront == null) { /* Create a new front in the same direction for this next segment of the original wave front: */ newFront = WaveFrontFactory.CreateWaveFront(Direction); newFront.WesternPoint = position; newFront.IsWesternPointShared = isWesternPointShared; isWesternPointShared = false; } easternMostPositionOfNewFront = position; } } else { /* End the front currently being built: */ if (newFront != null) { newFront.EasternPoint = easternMostPositionOfNewFront; newFront.IsEasternPointShared = false; yield return(newFront); newFront = null; } } // No longer the western-most point isWesternMostPoint = false; } if (newFront != null) { bool isEasternPointShared = IsEasternPointShared; // End off the Eastern-most front: newFront.EasternPoint = EasternPoint; newFront.IsEasternPointShared = true; yield return(newFront); if (!IsEasternPointShared) { // Create a new wave front to expand around any barriers: newFront = CreateAPointWaveOnTheEasternEdge(EasternPoint); yield return(newFront); } newFront = null; } }
protected override void CalculateDistancesFromAPlayer(GameState gameState, PlayerType player, HashSet <CellState> reachableCells) { CellState startingCell = null; PlayerCalculator calculator = null; switch (player) { case PlayerType.You: gameState.OpponentIsInSameCompartment = false; // Clear this property, so the algorithm can set it calculator = new YouCalculator(); startingCell = gameState.YourCell; break; case PlayerType.Opponent: startingCell = gameState.OpponentsCell; calculator = new OpponentCalculator(); break; } /* Set up the initial wave fronts, all for a single point (the player's position): */ Position startPoint = startingCell.Position; LinkedList <WaveFront> currentWaveFronts = new LinkedList <WaveFront>(); if (startingCell == gameState.NorthPole) { WaveFront waveFront = new SouthTravellingPolarWaveFront { WesternPoint = startPoint, EasternPoint = startPoint }; currentWaveFronts.AddLast(waveFront); } else if (startingCell == gameState.SouthPole) { WaveFront waveFront = new NorthTravellingPolarWaveFront { WesternPoint = startPoint, EasternPoint = startPoint }; currentWaveFronts.AddLast(waveFront); } else { /* Create 4 wave fronts of a single point each, in each of the diagonal directions: */ WaveDirection[] diagonalDirections = { WaveDirection.NW, WaveDirection.NE, WaveDirection.SE, WaveDirection.SW }; foreach (WaveDirection direction in diagonalDirections) { WaveFront waveFront = WaveFrontFactory.CreateWaveFront(direction); waveFront.WesternPoint = waveFront.EasternPoint = startPoint; waveFront.IsWesternPointShared = true; waveFront.IsEasternPointShared = true; currentWaveFronts.AddLast(waveFront); } } /* Iteratively expand the wave fronts until there are none left to expand: */ int nextDistance = 0; LinkedList <WaveFront> nextWaveFronts; while (currentWaveFronts.Count > 0) { nextDistance++; nextWaveFronts = new LinkedList <WaveFront>(); foreach (WaveFront currentWaveFront in currentWaveFronts) { WaveFront expandedWaveFront = currentWaveFront.Expand(); IEnumerable <WaveFront> newWaveFronts = expandedWaveFront.ContractAndCalculate(gameState, nextDistance, calculator, reachableCells); foreach (WaveFront newWaveFront in newWaveFronts) { nextWaveFronts.AddLast(newWaveFront); } } currentWaveFronts = nextWaveFronts; } /* Update aggregate data: */ switch (player) { case PlayerType.You: gameState.NumberOfCellsReachableByYou = calculator.NumberOfCellsReachable; gameState.TotalDegreesOfCellsReachableByYou = calculator.TotalDegreesOfCellsReachable; gameState.YourDijkstraStatus = DijkstraStatus.FullyCalculated; gameState.YourUpToDateDijkstraDistance = int.MaxValue; break; case PlayerType.Opponent: gameState.NumberOfCellsReachableByOpponent = calculator.NumberOfCellsReachable; gameState.TotalDegreesOfCellsReachableByOpponent = calculator.TotalDegreesOfCellsReachable; gameState.OpponentsDijkstraStatus = DijkstraStatus.FullyCalculated; gameState.OpponentsUpToDateDijkstraDistance = int.MaxValue; break; } }