/// <summary> /// Find all passable (true) regions. /// </summary> public static IEnumerable <RegionDraft> FindPassableRegionsFor(Matrix <bool> matrix) { var openNodesEnum = AddAllPassableNodesToOpenList(matrix); var openNodes = openNodesEnum.ToList(); var regions = new List <RegionDraft>(); while (openNodes.Any()) { var openNode = openNodes.First(x => MapFactoryHelper.IsAvailableFor7(matrix, x)); var regionCoords = FloodFillRegions(matrix, openNode); var region = new RegionDraft(regionCoords.ToArray()); openNodes.RemoveAll(x => region.Contains(x)); regions.Add(region); } return(regions); }
public RegionCoords(OffsetCoords coords, RegionDraft region) { Coords = coords; Region = region ?? throw new ArgumentNullException(nameof(region)); }
private static void FindClosestNodesBetweenOpenAndUnited(List <RegionDraft> openRegions, OffsetCoords[] unitedRegionCoords, out OffsetCoords?currentOpenRegionCoord, out OffsetCoords?currentUnitedRegionCoord, out RegionDraft nearbyOpenRegion) { var currentDistance = int.MaxValue; currentOpenRegionCoord = null; currentUnitedRegionCoord = null; nearbyOpenRegion = null; foreach (var currentOpenRegion in openRegions) { foreach (var openRegionCoord in currentOpenRegion.Coords) { var openCubeCoord = HexHelper.ConvertToCube(openRegionCoord); foreach (var unitedRegionCoord in unitedRegionCoords) { var unitedCubeCoord = HexHelper.ConvertToCube(unitedRegionCoord); var distance = openCubeCoord.DistanceTo(unitedCubeCoord); if (distance < currentDistance) { currentDistance = distance; currentOpenRegionCoord = openRegionCoord; currentUnitedRegionCoord = unitedRegionCoord; nearbyOpenRegion = currentOpenRegion; } } } } }
private static RegionDraft[] MakeUnitedRegions(Matrix <bool> matrix) { // Формирование регионов. // Регионы, кроме дальнейшего размещения игровых предметов, // в рамках этой генерации будут служить для обнаружения // изолированных регионов. // В секторе не должно быть изолированых регионов, поэтому // дальше все регионы объединяются в единый граф. var openNodes = new List <OffsetCoords>(); AddAllPassableNodesToOpenList(matrix, openNodes); if (!openNodes.Any()) { throw new CellularAutomatonException("Ни одна из клеток не выжила."); } // Разбиваем все проходимые (true) клетки на регионы // через заливку. var regions = new List <RegionDraft>(); while (openNodes.Any()) { var openNode = openNodes.First(x => MapFactoryHelper.IsAvailableFor7(matrix, x)); var regionCoords = FloodFillRegions(matrix, openNode); var region = new RegionDraft(regionCoords.ToArray()); openNodes.RemoveAll(x => region.Contains(x)); regions.Add(region); } // Соединяем все регионы в единый граф. var openRegions = new List <RegionDraft>(regions); var unitedRegions = new List <RegionDraft>(); var startRegion = openRegions[0]; openRegions.RemoveAt(0); unitedRegions.Add(startRegion); while (openRegions.Any()) { var unitedRegionCoords = unitedRegions.SelectMany(x => x.Coords).ToArray(); // Ищем две самые ближние точки между объединённым регионом и // и всеми открытыми регионами. FindClosestNodesBetweenOpenAndUnited( openRegions, unitedRegionCoords, out OffsetCoords? currentOpenRegionCoord, out OffsetCoords? currentUnitedRegionCoord, out RegionDraft nearbyOpenRegion); // Если координаты, которые нужно соединить, найдены, // то прорываем тоннель. if (nearbyOpenRegion != null && currentOpenRegionCoord != null && currentUnitedRegionCoord != null) { var openCubeCoord = HexHelper.ConvertToCube(currentOpenRegionCoord.Value); var unitedCubeCoord = HexHelper.ConvertToCube(currentUnitedRegionCoord.Value); DrawLineBetweenNodes(matrix, openCubeCoord, unitedCubeCoord); openRegions.Remove(nearbyOpenRegion); unitedRegions.Add(nearbyOpenRegion); } } return(regions.ToArray()); }
/// <summary> /// Метод отделяет от существующих регионов ячйки таким образом, /// чтобы суммарно на карте число регионов равнялось числу переходов + 1 (за стартовый). /// Сейчас все отщеплённые регионы в первую отщепляются от произвольных. /// Уже одноклеточные регионы не участвуют в расщеплении. /// </summary> /// <param name="draftRegions"> Текущие регионы на карте. </param> /// <param name="targetRegionCount"> Целевое число регионов. </param> /// <returns> Возвращает новый массив черновиков регионов. </returns> private RegionDraft[] SplitRegionsForTransitions( [NotNull, ItemNotNull] RegionDraft[] draftRegions, int targetRegionCount) { if (draftRegions == null) { throw new ArgumentNullException(nameof(draftRegions)); } if (targetRegionCount <= 0) { throw new ArgumentException("Целевое количество регионов должно быть больше 0.", nameof(targetRegionCount)); } var regionCountDiff = targetRegionCount - draftRegions.Length; if (regionCountDiff <= 0) { return((RegionDraft[])draftRegions.Clone()); } var availableSplitRegions = draftRegions.Where(x => x.Coords.Count() > 1); var availableCoords = from region in availableSplitRegions from coord in region.Coords.Skip(1) select new RegionCoords(coord, region); if (availableCoords.Count() < regionCountDiff) { // Возможна ситуация, когда в принципе клеток меньше, // чем требуется регионов. // Даже если делать по одной клетки на регион. // В этом случае ничего сделать нельзя. // Передаём проблему вызывающему коду. throw new CellularAutomatonException("Невозможно расщепить регионы на достаточное количество. Клеток меньше, чем требуется."); } var openRegionCoords = new List <RegionCoords>(availableCoords); var usedRegionCoords = new List <RegionCoords>(); for (var i = 0; i < regionCountDiff; i++) { var coordRollIndex = _dice.Roll(0, openRegionCoords.Count - 1); var regionCoordPair = openRegionCoords[coordRollIndex]; openRegionCoords.RemoveAt(coordRollIndex); usedRegionCoords.Add(regionCoordPair); } var newDraftRegionList = new List <RegionDraft>(); var regionGroups = usedRegionCoords.GroupBy(x => x.Region) .ToDictionary(x => x.Key, x => x.AsEnumerable()); foreach (var draftRegion in draftRegions) { if (regionGroups.TryGetValue(draftRegion, out var splittedRegionCoords)) { var splittedCoords = splittedRegionCoords.Select(x => x.Coords).ToArray(); var newCoordsOfCurrentRegion = draftRegion.Coords .Except(splittedCoords) .ToArray(); var recreatedRegionDraft = new RegionDraft(newCoordsOfCurrentRegion); newDraftRegionList.Add(recreatedRegionDraft); foreach (var splittedCoord in splittedCoords) { var newRegionDraft = new RegionDraft(new[] { splittedCoord }); newDraftRegionList.Add(newRegionDraft); } } else { newDraftRegionList.Add(draftRegion); } } return(newDraftRegionList.ToArray()); }
private static RegionDraft[] MakeUnitedRegions(Matrix <bool> matrix) { // Формирование регионов. // Регионы, кроме дальнейшего размещения игровых предметов, // в рамках этой генерации будут служить для обнаружения // изолированных регионов. // В секторе не должно быть изолированых регионов, поэтому // дальше все регионы объединяются в единый граф. var openNodes = new List <OffsetCoords>(); for (var x = 0; x < matrix.Width; x++) { for (var y = 0; y < matrix.Height; y++) { if (matrix.Items[x, y]) { openNodes.Add(new OffsetCoords(x, y)); } } } if (!openNodes.Any()) { throw new CellularAutomatonException("Ни одна из клеток не выжила."); } // Разбиваем все проходимые (true) клетки на регионы // через заливку. var regions = new List <RegionDraft>(); while (openNodes.Any()) { var openNode = openNodes.First(); var regionCoords = FloodFillRegions(matrix, openNode); var region = new RegionDraft(regionCoords.ToArray()); openNodes.RemoveAll(x => region.Coords.Contains(x)); regions.Add(region); } // Соединяем все регионы в единый граф. var openRegions = new List <RegionDraft>(regions); var unitedRegions = new List <RegionDraft>(); var startRegion = openRegions[0]; openRegions.RemoveAt(0); unitedRegions.Add(startRegion); while (openRegions.Any()) { var unitedRegionCoords = unitedRegions.SelectMany(x => x.Coords).ToArray(); // Ищем две самые ближние точки между объединённым регионом и // и всеми открытыми регионами. var currentDistance = int.MaxValue; OffsetCoords?currentOpenRegionCoord = null; OffsetCoords?currentUnitedRegionCoord = null; RegionDraft nearbyOpenRegion = null; foreach (var currentOpenRegion in openRegions) { foreach (var openRegionCoord in currentOpenRegion.Coords) { var openCubeCoord = HexHelper.ConvertToCube(openRegionCoord); foreach (var unitedRegionCoord in unitedRegionCoords) { var unitedCubeCoord = HexHelper.ConvertToCube(unitedRegionCoord); var distance = openCubeCoord.DistanceTo(unitedCubeCoord); if (distance < currentDistance) { currentDistance = distance; currentOpenRegionCoord = openRegionCoord; currentUnitedRegionCoord = unitedRegionCoord; nearbyOpenRegion = currentOpenRegion; } } } } // Если координаты, которые нужно соединить, найдены, // то прорываем тоннель. if (nearbyOpenRegion != null && currentOpenRegionCoord != null && currentUnitedRegionCoord != null) { var openCubeCoord = HexHelper.ConvertToCube(currentOpenRegionCoord.Value); var unitedCubeCoord = HexHelper.ConvertToCube(currentUnitedRegionCoord.Value); var line = CubeCoordsHelper.CubeDrawLine(openCubeCoord, unitedCubeCoord); foreach (var lineItem in line) { var offsetCoords = HexHelper.ConvertToOffset(lineItem); matrix.Items[offsetCoords.X, offsetCoords.Y] = true; } openRegions.Remove(nearbyOpenRegion); unitedRegions.Add(nearbyOpenRegion); } } return(regions.ToArray()); }