Exemplo n.º 1
0
        /// <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());
        }