Пример #1
0
        /// <summary>
        /// Создание карты.
        /// </summary>
        /// <returns>
        /// Возвращает экземпляр карты.
        /// </returns>
        public Task <ISectorMap> CreateAsync(object options)
        {
            if (options is null)
            {
                throw new System.ArgumentNullException(nameof(options));
            }

            var sectorScheme = (ISectorSubScheme)options;

            var map = CreateMapInstance();

            var edgeHash = new HashSet <string>();

            // Генерируем случайные координаты комнат
            var transitions = MapFactoryHelper.CreateTransitions(sectorScheme);

            var rooms = _roomGenerator.GenerateRoomsInGrid(sectorScheme.RegionCount,
                                                           RoomMinSize,
                                                           sectorScheme.RegionSize,
                                                           transitions);

            // Создаём узлы и рёбра комнат
            _roomGenerator.CreateRoomNodes(map, rooms, edgeHash);

            // Соединяем комнаты
            _roomGenerator.BuildRoomCorridors(map, rooms, edgeHash);

            // Указание регионов карты
            var regionIdCounter = 1;

            foreach (var room in rooms)
            {
                var passableRoomNodes = room.Nodes.Where(x => !x.IsObstacle);
                var region            = new MapRegion(regionIdCounter, passableRoomNodes.Cast <IMapNode>().ToArray());
                regionIdCounter++;
                map.Regions.Add(region);

                if (room.IsStart)
                {
                    region.IsStart = true;
                    continue;
                }

                if (room.Transitions?.Any() == true)
                {
                    region.ExitNodes = (from regionNode in region.Nodes
                                        where map.Transitions.Keys.Contains(regionNode)
                                        select regionNode).ToArray();

                    continue;
                }
            }

            return(Task.FromResult(map));
        }
Пример #2
0
        private static bool CheckMapPassable(IEnumerable <OffsetCoords> currentCoords, OffsetCoords targetCoords)
        {
            var matrix = new Matrix <bool>(2002, 2002);

            foreach (var coords in currentCoords)
            {
                try
                {
                    var x = coords.X;
                    var y = coords.Y;
                    matrix.Items[x, y] = true;
                }
                catch (IndexOutOfRangeException)
                {
                    // Do nothing. The solution by using matrix is code smell.
                    // There is the catch block just for debug.
                }
            }

            // Закрываем проверяемый узел
            matrix.Items[targetCoords.X, targetCoords.Y] = false;

            // Не выбираем првоеряемую координату, как стартовую, потому что
            // она уже закрыта. Заливка от неё не пройдёт.
            var availableStartPoints = currentCoords.Where(x => x != targetCoords).ToArray();

            if (!availableStartPoints.Any())
            {
                // Если нет доступных координат для старта,
                // значит стартовая координата была единственной.
                // Её нельзя закрывать препятсвием.
                return(false);
            }

            var startPoint  = availableStartPoints.First(x => MapFactoryHelper.IsAvailableFor(matrix, x));
            var floodPoints = HexBinaryFiller.FloodFill(matrix, startPoint);

            foreach (var point in floodPoints)
            {
                matrix.Items[point.X, point.Y] = false;
            }

            foreach (var node in currentCoords)
            {
                var x = node.X;
                var y = node.Y;
                if (matrix.Items[x, y])
                {
                    return(false);
                }
            }

            return(true);
        }
Пример #3
0
        public async Task <ISector> GenerateAsync(ISectorNode sectorNode)
        {
            var transitions = MapFactoryHelper.CreateTransitions(sectorNode).ToArray();

            var map = await SquareMapFactory.CreateAsync(transitions.Count() + 1);

            CreateTransitions(transitions, map);

            var locationScheme = sectorNode.Biome.LocationScheme;

            var sector = _sectorFactory.Create(map, locationScheme);

            return(sector);
        }
Пример #4
0
        public IEnumerable <RegionDraft> Generate(ref Matrix <bool> matrix, int fillProbability, int totalIterations)
        {
            InitiateMatrix(matrix, fillProbability);

            matrix = SimulateCellularAutomaton(matrix, totalIterations);

            var resizedMatrix = MapFactoryHelper.ResizeMatrixTo7(matrix);

            var matrixWithMargins = resizedMatrix.CreateMatrixWithMargins(2, 2);

            var draftRegions = RegionFinder.FindPassableRegionsFor(matrixWithMargins).ToArray();

            matrix = matrixWithMargins;

            return(draftRegions);
        }
Пример #5
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);
        }
Пример #6
0
        /// <inheritdoc />
        public async Task <ISector> GenerateAsync(ISectorNode sectorNode)
        {
            var transitions = MapFactoryHelper.CreateTransitions(sectorNode);

            var sectorFactoryOptions =
                new SectorMapFactoryOptions(sectorNode.SectorScheme.MapGeneratorOptions, transitions);

            var map = await _mapFactory.CreateAsync(sectorFactoryOptions).ConfigureAwait(false);

            var actorManager        = new ActorManager();
            var staticObjectManager = new StaticObjectManager();

            var sector = new Sector(map,
                                    actorManager,
                                    staticObjectManager,
                                    _dropResolver,
                                    _schemeService,
                                    _equipmentDurableService);

            return(sector);
        }
Пример #7
0
        public void ResizeMatrixTo7Test()
        {
            // ARRANGE

            var matrix = new Matrix <bool>(1, 1);

            matrix[0, 0] = true;

            // ACT

            var resizedMatrix = MapFactoryHelper.ResizeMatrixTo7(matrix);

            // ASSERT

            resizedMatrix[1, 1].Should().BeTrue();
            var n = HexHelper.GetNeighbors(1, 1);

            foreach (var c in n)
            {
                resizedMatrix[c.X, c.Y].Should().BeTrue();
            }
        }
        /// <summary>
        /// Создаёт карту сектора.
        /// </summary>
        /// <param name="options">Настройки генерации.
        /// Должны быть типа ISectorSubScheme с заданным значением MapGeneratorOptions.
        /// Значение MapGeneratorOptions должно быть типа ISectorCellularAutomataMapFactoryOptionsSubScheme.</param>
        /// <returns></returns>
        public Task <ISectorMap> CreateAsync(object options)
        {
            if (options is null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            var sectorScheme = (ISectorSubScheme)options;
            var transitions  = MapFactoryHelper.CreateTransitions(sectorScheme);

            var cellularAutomatonOptions = (ISectorCellularAutomataMapFactoryOptionsSubScheme)sectorScheme.MapGeneratorOptions;

            if (cellularAutomatonOptions == null)
            {
                throw new ArgumentException($"Для {nameof(options)} не задано {nameof(ISectorSubScheme.MapGeneratorOptions)} равно null.");
            }

            var matrixWidth  = cellularAutomatonOptions.MapWidth;
            var matrixHeight = cellularAutomatonOptions.MapHeight;

            var chanceToStartAlive = cellularAutomatonOptions.ChanceToStartAlive;

            var targetRegionDraftCount = transitions.Count() + 1;
            var createResult           = CreateInner(
                targetRegionDraftCount,
                chanceToStartAlive,
                matrixWidth,
                matrixHeight);

            var matrix       = createResult.Matrix;
            var draftRegions = createResult.Regions;

            var map = CreateSectorMap(matrix, draftRegions, transitions);

            return(Task.FromResult(map));
        }
        //TODO Придумать название метода получше
        private CreateMatrixResult CreateInner(int targetRegionDraftCount, int chanceToStartAlive, int matrixWidth, int matrixHeight)
        {
            var matrix = new Matrix <bool>(matrixWidth, matrixHeight);

            for (var retry = 0; retry < RETRY_LIMIT; retry++)
            {
                InitStartAliveMatrix(matrix, chanceToStartAlive);

                // Несколько шагов симуляции
                for (var i = 0; i < SIMULATION_COUNT; i++)
                {
                    var newMap = DoSimulationStep(matrix);
                    matrix = new Matrix <bool>(newMap.Items, matrix.Width, matrix.Height);
                }

                var resizedMatrix = MapFactoryHelper.ResizeMatrixTo7(matrix);

                var matrixWithMargins = resizedMatrix.CreateMatrixWithMargins(2, 2);

                RegionDraft[] draftRegions;
                try
                {
                    draftRegions = MakeUnitedRegions(matrixWithMargins);
                }
                catch (CellularAutomatonException)
                {
                    // Это означает, что при текущих стартовых данных невозможно создать подходящую карту.
                    // Запускаем следующую итерацю.
                    continue;
                }

                // Обрабатываем ситуацию, когда на карте тегионов меньше, чем переходов.
                // На карте должно быть минимум столько регионов, сколько переходов.
                // +1 - это регион старта.
                if (draftRegions.Length < targetRegionDraftCount)
                {
                    try
                    {
                        var splittedDraftRegions = SplitRegionsForTransitions(draftRegions, targetRegionDraftCount);

                        // Разделение успешно выполнено.
                        // Пропускаем карту дальше.
                        var result = new CreateMatrixResult(matrixWithMargins, splittedDraftRegions);
                        return(result);
                    }
                    catch (CellularAutomatonException)
                    {
                        // Это означает, что при текущих стартовых данных невозможно создать подходящую карту.
                        // Запускаем следующую итерацю.
                        continue;
                    }
                }
                else
                {
                    var result = new CreateMatrixResult(matrixWithMargins, draftRegions);
                    return(result);
                }
            }

            // Если цикл закончился, значит вышел лимит попыток.
            throw new InvalidOperationException("Не удалось создать карту за предельное число попыток.");
        }
        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());
        }