public (GameState state, bool combatEnds) PlayRound() { bool combatEnds = false; var coordinates = new List <(int x, int y)>(); int height = Grid.GetLength(0); int width = Grid.GetLength(1); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { GridCell cell = Grid[y, x]; if (cell.IsElf || cell.IsGoblin) { coordinates.Add((x, y)); } } } var elfHitPoints = ElfHitPoints; var goblinHitPoints = GoblinHitPoints; GridCell[,] grid = Grid; for (int i = 0; i < coordinates.Count; ++i) { var xy = coordinates[i]; GridCell unitCell = grid[xy.y, xy.x]; if (!(unitCell.IsElf || unitCell.IsGoblin)) { // Already taken out in an earlier turn this round. continue; } if ((unitCell.IsElf && !goblinHitPoints.Any(kv => kv.Value > 0)) || (unitCell.IsGoblin && !elfHitPoints.Any(kv => kv.Value > 0))) { combatEnds = true; break; } int hitPower = unitCell.IsElf ? elfHitPoints[unitCell.ElfId.Value] : goblinHitPoints[unitCell.GoblinId.Value]; // Move if appropriate. var move = GridOperations.CalculateMove(grid, xy.x, xy.y); if (move.HasValue) { bool isElf = unitCell.IsElf; int unitId = isElf ? unitCell.ElfId.Value : unitCell.GoblinId.Value; RemoveItem(height, width, grid, xy); var newCoordinates = (x : xy.x + move.Value.dx, y : xy.y + move.Value.dy); grid[newCoordinates.y, newCoordinates.x] = isElf ? GridCell.Elf(unitId) : GridCell.Goblin(unitId); GridOperations.CalculateCloseness(_gridPair); coordinates[i] = newCoordinates; } // Attack if appropriate. xy = coordinates[i]; var match = GridOperations.FindBest( grid, xy.x, xy.y, c => { bool isTarget = unitCell.IsElf ? c.IsGoblin : c.IsElf; return(isTarget ? NullIfDead(unitCell.IsElf ? goblinHitPoints[c.GoblinId.Value] : elfHitPoints[c.ElfId.Value]) : default); int?NullIfDead(int v) => v > 0 ? v : default; },
private static bool ProcessOneClosenessStep( GridCell[,] lastGrid, GridCell[,] newGrid) { bool changed = false; int height = lastGrid.GetLength(0); int width = lastGrid.GetLength(1); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { GridCell lastCell = lastGrid[y, x]; GridCell newCell = lastCell; if (!lastCell.IsWall && ((!lastCell.IsElf && !lastCell.DistanceToElfInRange.HasValue) || (!lastCell.IsGoblin && !lastCell.DistanceToGoblinInRange.HasValue))) { if (!lastCell.IsElf && !newCell.DistanceToElfInRange.HasValue) { var match = FindNearest( c => c.IsElf ? 0 : c.DistanceToElfInRange, (c, _) => c.IsElf ? (x, y) : c.ElfInRangePosition.Value); if (match.HasValue) { (GridCell elfNearest, int hitX, int hitY) = match.Value; int inRangeX, inRangeY; bool hitWasOnUnitItself = !elfNearest.DistanceToElfInRange.HasValue; if (hitWasOnUnitItself) { inRangeX = x; inRangeY = y; } else { (inRangeX, inRangeY) = lastGrid[hitY, hitX].ElfInRangePosition.Value; } newCell = newCell.WithDistanceToElfInRange( (elfNearest.DistanceToElfInRange ?? 0) + 1, elfNearest.ElfId.Value, inRangeX, inRangeY); changed = true; } } if (!newCell.IsGoblin && !newCell.DistanceToGoblinInRange.HasValue) { var match = FindNearest( c => c.IsGoblin ? 0 : c.DistanceToGoblinInRange, (c, _) => c.IsGoblin ? (x, y) : c.GoblinInRangePosition.Value); if (match.HasValue) { (GridCell goblinNearest, int hitX, int hitY) = match.Value; int inRangeX, inRangeY; bool hitWasOnUnitItself = !goblinNearest.DistanceToGoblinInRange.HasValue; if (hitWasOnUnitItself) { inRangeX = x; inRangeY = y; } else { (inRangeX, inRangeY) = lastGrid[hitY, hitX].GoblinInRangePosition.Value; } newCell = newCell.WithDistanceToGoblinInRange( (goblinNearest.DistanceToGoblinInRange ?? 0) + 1, goblinNearest.GoblinId.Value, inRangeX, inRangeY); changed = true; } } (GridCell cell, int x, int y)? FindNearest( Func <GridCell, int?> distanceSelector, Func <GridCell, (int x, int y), (int x, int y)> orderingSelector) { return(FindBest( lastGrid, x, y, c => { var distance = distanceSelector(c); return distance == 0 ? distance : (c.IsGoblin || c.IsElf) ? default : distance; }, orderingSelector)); } } newGrid[y, x] = newCell; } } return(changed); }