private static int CountAliveNeighbours(Matrix <bool> matrix, int x, int y)
        {
            var aliveCount = 0;

            var cubeCoords      = HexHelper.ConvertToCube(x, y);
            var offsetsImplicit = HexHelper.GetOffsetClockwise();
            var offsetsDiagonal = HexHelper.GetDiagonalOffsetClockwise();
            var offsets         = offsetsImplicit.Union(offsetsDiagonal);

            foreach (var offset in offsets)
            {
                var neighbour = cubeCoords + offset;

                var offsetCoords = HexHelper.ConvertToOffset(neighbour);

                var nX = offsetCoords.X;
                var nY = offsetCoords.Y;

                // Границу мертвым живым соседом.
                // Сделано, чтобы углы не заполнялись.

                if (nX >= 0 && nY >= 0 && nX < matrix.Width && nY < matrix.Height)
                {
                    if (matrix.Items[nX, nY])
                    {
                        aliveCount++;
                    }
                }
            }

            return(aliveCount);
        }
Beispiel #2
0
        /// <summary>
        /// Соединяет две комнаты коридором.
        /// </summary>
        /// <param name="map"> Карта, над которой идёт работа. </param>
        /// <param name="room"> Комната, которую соединяем. </param>
        /// <param name="selectedRoom"> Целевая комната для соединения. </param>
        /// <param name="edgeHash"> Хэш рёбер (для оптимизации). </param>
        protected static void ConnectRoomsWithCorridor(IMap map, Room room, Room selectedRoom, HashSet <string> edgeHash)
        {
            if (room is null)
            {
                throw new System.ArgumentNullException(nameof(room));
            }

            if (selectedRoom is null)
            {
                throw new System.ArgumentNullException(nameof(selectedRoom));
            }

            var currentNode = GetRoomCenterNode(room);
            var targetNode  = GetRoomCenterNode(selectedRoom);

            var points = CubeCoordsHelper.CubeDrawLine(currentNode.CubeCoords, targetNode.CubeCoords);

            foreach (var point in points)
            {
                var offsetCoords = HexHelper.ConvertToOffset(point);

                // Это происходит, потому что если при нулевом Х для обеих комнат
                // попытаться отрисовать линию коридора, то она будет змейкой заходить за 0.
                // Нужно искать решение получше.
                offsetCoords = new OffsetCoords(offsetCoords.X < 0 ? 0 : offsetCoords.X,
                                                offsetCoords.Y < 0 ? 0 : offsetCoords.Y);

                var node = RoomHelper.CreateCorridorNode(map, edgeHash, currentNode, offsetCoords.X, offsetCoords.Y);
                currentNode = node;
            }
        }
Beispiel #3
0
        private static Locality GetNeighborLocality(Agent agent, Globe globe, int coordRollIndex)
        {
            Locality targetLocality  = null;
            var      nextCoords      = GetRandomCoords(coordRollIndex);
            var      agentCubeCoords = HexHelper.ConvertToCube(agent.Location.Coords.X, agent.Location.Coords.Y);

            for (var i = 0; i < nextCoords.Length; i++)
            {
                var scanCubeCoords   = agentCubeCoords + nextCoords[i];
                var scanOffsetCoords = HexHelper.ConvertToOffset(scanCubeCoords);

                var freeX = scanOffsetCoords.X;
                var freeY = scanOffsetCoords.Y;

                if (freeX < 0)
                {
                    continue;
                }

                if (freeX >= globe.Terrain.Length)
                {
                    continue;
                }

                if (freeY < 0)
                {
                    continue;
                }

                if (freeY >= globe.Terrain[freeX].Length)
                {
                    continue;
                }

                var freeLocaltion1 = globe.Terrain[freeX][freeY];

                if (globe.LocalitiesCells.TryGetValue(freeLocaltion1, out var otherCheckLocality))
                {
                    // Захватываем только города, которые не принадлежат нашему государству.
                    if (otherCheckLocality.Owner != agent.Realm)
                    {
                        // Захватываем город, если он не последний у вражеского государства
                        // Это нужно будет переделать, потому что это разрушает идею о возникновении и падении империй.
                        var otherRealmLocalities = globe.Localities.Where(x => x.Owner == otherCheckLocality.Owner);
                        if (otherRealmLocalities.Count() > 1)
                        {
                            targetLocality = otherCheckLocality;
                            break;
                        }
                    }
                }
            }

            return(targetLocality);
        }
Beispiel #4
0
        public void ConvertToOffsetTest(int offsetX, int offsetY, int cubeX, int cubeY, int cubeZ)
        {
            // ARRANGE
            var cubeCoords     = new CubeCoords(cubeX, cubeY, cubeZ);
            var expectedOffset = new OffsetCoords(offsetX, offsetY);

            // ACT
            var factOffsetCoords = HexHelper.ConvertToOffset(cubeCoords);

            // ASSERT
            factOffsetCoords.Should().BeEquivalentTo(expectedOffset);
        }
Beispiel #5
0
        private IEnumerable <IGraphNode> GetNextFromMatrix(int localOffsetX, int localOffsetY, int segmentX,
                                                           int segmentY, IGraphNode[,] matrix)
        {
            var directions        = HexHelper.GetOffsetClockwise();
            var currentCubeCoords = HexHelper.ConvertToCube(localOffsetX, localOffsetY);

            for (var i = 0; i < 6; i++)
            {
                var dir = directions[i];
                var neighborLocalCube = new CubeCoords(dir.X + currentCubeCoords.X,
                                                       dir.Y + currentCubeCoords.Y,
                                                       dir.Z + currentCubeCoords.Z);

                var neighborLocalOffset = HexHelper.ConvertToOffset(neighborLocalCube);

                var neighborSegmentX = segmentX;
                var neighborSegmentY = segmentY;

                if (neighborLocalOffset.X < 0)
                {
                    neighborSegmentX--;
                }
                else if (neighborLocalOffset.X >= _segmentSize)
                {
                    neighborSegmentX++;
                }

                if (neighborLocalOffset.Y < 0)
                {
                    neighborSegmentY--;
                }
                else if (neighborLocalOffset.Y >= _segmentSize)
                {
                    neighborSegmentY++;
                }

                IGraphNode currentNeibour;
                if (neighborSegmentX == segmentX &&
                    neighborSegmentY == segmentY)
                {
                    currentNeibour = matrix[neighborLocalOffset.X, neighborLocalOffset.Y];

                    if (currentNeibour == null)
                    {
                        continue;
                    }

                    yield return(currentNeibour);
                }
            }
        }
        ///<summary>
        /// Для препятсвий выбираются только те узлы, для которых есть все соседи.
        ///</summary>
        private static bool HasAllHeighbors(HashSet <OffsetCoords> coordHash, CubeCoords[] neighborCubeOffsets, CubeCoords cube)
        {
            foreach (var neighborCubeOffset in neighborCubeOffsets)
            {
                var neighborCube         = cube + neighborCubeOffset;
                var neighborOffsetCoords = HexHelper.ConvertToOffset(neighborCube);
                if (!coordHash.Contains(neighborOffsetCoords))
                {
                    return(false);
                }
            }

            return(true);
        }
Beispiel #7
0
        private void ConnectRoomsWithCorridor(IMap map, HashSet <string> edgeHash, Room room, Room selectedRoom)
        {
            var currentNode = room.Nodes.First();
            var targetNode  = selectedRoom.Nodes.First();

            var points = CubeCoordsHelper.CubeDrawLine(currentNode.CubeCoords, targetNode.CubeCoords);

            foreach (var point in points)
            {
                var offsetCoords = HexHelper.ConvertToOffset(point);

                var node = CreateCorridorNode(map, edgeHash, currentNode, offsetCoords.X, offsetCoords.Y);
                currentNode = node;
            }
        }
Beispiel #8
0
        /// <summary>
        /// Проверяет, доступен ли целевой узел из стартового узла.
        /// </summary>
        /// <param name="currentNode">Стартовый узел.</param>
        /// <param name="targetNode">Целевой проверяемый узел.</param>
        /// <returns>
        /// Возвращает true, если узел доступен. Иначе, false.
        /// </returns>
        public bool TargetIsOnLine(IGraphNode currentNode, IGraphNode targetNode)
        {
            if (currentNode is null)
            {
                throw new System.ArgumentNullException(nameof(currentNode));
            }

            if (targetNode is null)
            {
                throw new System.ArgumentNullException(nameof(targetNode));
            }

            var targetHexNode  = (HexNode)targetNode;
            var currentHexNode = (HexNode)currentNode;

            var line = CubeCoordsHelper.CubeDrawLine(currentHexNode.CubeCoords, targetHexNode.CubeCoords);

            for (var i = 1; i < line.Length; i++)
            {
                var prevPoint = line[i - 1];
                var testPoint = line[i];

                var prevOffsetCoords = HexHelper.ConvertToOffset(prevPoint);
                var testOffsetCoords = HexHelper.ConvertToOffset(testPoint);

                var prevNode = GetByCoords(prevOffsetCoords.X, prevOffsetCoords.Y);

                if (prevNode == null)
                {
                    return(false);
                }

                var testNode = GetByCoords(testOffsetCoords.X, testOffsetCoords.Y);

                if (testNode == null)
                {
                    return(false);
                }

                var hasNext = GetNext(prevNode).Contains(testNode);
                if (!hasNext)
                {
                    return(false);
                }
            }

            return(true);
        }
Beispiel #9
0
        public void ThenМонстрIdСтоитВУзле(int monsterId, string isNot, int offsetX, int offsetY)
        {
            var monster      = Context.GetMonsterById(monsterId);
            var node         = (HexNode)monster.Node;
            var cubeCoords   = node.CubeCoords;
            var offsetCoords = HexHelper.ConvertToOffset(cubeCoords);

            if (string.IsNullOrWhiteSpace(isNot))
            {
                offsetCoords.Should().Be(new OffsetCoords(offsetX, offsetY));
            }
            else
            {
                offsetCoords.Should().NotBe(new OffsetCoords(offsetX, offsetY));
            }
        }
        private static Locality GetNeighborLocality(Agent agent, Globe globe)
        {
            Locality targetLocality = null;

            var nextCoords      = HexHelper.GetOffsetClockwise().OrderBy(item => Guid.NewGuid()).ToArray();
            var agentCubeCoords = HexHelper.ConvertToCube(agent.Localtion.Coords.X, agent.Localtion.Coords.Y);

            for (var i = 0; i < nextCoords.Length; i++)
            {
                var scanCubeCoords   = agentCubeCoords + nextCoords[i];
                var scanOffsetCoords = HexHelper.ConvertToOffset(scanCubeCoords);

                var freeX = scanOffsetCoords.X;
                var freeY = scanOffsetCoords.Y;

                if (freeX < 0)
                {
                    continue;
                }

                if (freeX >= globe.Terrain.Length)
                {
                    continue;
                }

                if (freeY < 0)
                {
                    continue;
                }

                if (freeY >= globe.Terrain[freeX].Length)
                {
                    continue;
                }

                var freeLocaltion1 = globe.Terrain[freeX][freeY];

                if (globe.LocalitiesCells.TryGetValue(freeLocaltion1, out var otherCheckLocality))
                {
                    targetLocality = otherCheckLocality;
                    break;
                }
            }

            return(targetLocality);
        }
        private static void DrawLineBetweenNodes(Matrix <bool> matrix, CubeCoords openCubeCoord, CubeCoords unitedCubeCoord)
        {
            var line = CubeCoordsHelper.CubeDrawLine(openCubeCoord, unitedCubeCoord);

            foreach (var lineItem in line)
            {
                var offsetCoords = HexHelper.ConvertToOffset(lineItem);
                matrix[offsetCoords.X, offsetCoords.Y] = true;

                // Коридоры должны быть размером в Size7.
                // Поэтому вокруг каждой точки прорываем соседей.

                var neighborCoords = HexHelper.GetNeighbors(offsetCoords.X, offsetCoords.Y);
                foreach (var coords in neighborCoords)
                {
                    matrix[coords.X, coords.Y] = true;
                }
            }
        }
        private void ConnectRoomsWithCorridor(IMap map, Room room, Room selectedRoom, HashSet <string> edgeHash)
        {
            var currentNode = room.Nodes.First();
            var targetNode  = selectedRoom.Nodes.First();

            var points = CubeCoordsHelper.CubeDrawLine(currentNode.CubeCoords, targetNode.CubeCoords);

            foreach (var point in points)
            {
                var offsetCoords = HexHelper.ConvertToOffset(point);

                // это происходит, потому что если при нулевом Х для обеих комнат
                // попытаться отрисовать линию коридора, то она будет змейкой заходить за 0.
                // Нужно искать решение получше.
                offsetCoords = new OffsetCoords(offsetCoords.X < 0 ? 0 : offsetCoords.X,
                                                offsetCoords.Y < 0 ? 0 : offsetCoords.Y);

                var node = CreateCorridorNode(map, currentNode, offsetCoords.X, offsetCoords.Y, edgeHash);
                currentNode = node;
            }
        }
        public void Use(Agent agent, Globe globe, IDice dice)
        {
            globe.LocalitiesCells.TryGetValue(agent.Location, out var currentLocality);

            var highestBranchs = agent.Skills
                                 .Where(x => /*x.Key != BranchType.Politics &&*/ x.Value >= 1)
                                 .OrderBy(x => x.Value);

            if (!highestBranchs.Any())
            {
                return;
            }

            var firstBranch = highestBranchs.First();


            // Обнаружение свободных узлов для размещения населённого пункта.
            // Свободные узлы ишутся от текущей локации агента.

            TerrainCell freeLocaltion = null;

            var nextCoords      = HexHelper.GetOffsetClockwise();
            var agentCubeCoords = HexHelper.ConvertToCube(agent.Location.Coords.X, agent.Location.Coords.Y);

            for (var i = 0; i < nextCoords.Length; i++)
            {
                var scanCubeCoords   = agentCubeCoords + nextCoords[i];
                var scanOffsetCoords = HexHelper.ConvertToOffset(scanCubeCoords);

                var freeX = scanOffsetCoords.X;
                var freeY = scanOffsetCoords.Y;

                // Убеждаемся, что проверяемый узел находится в границах мира.
                var isPointInside = IsPointInsideWorld(freeX, freeY, globe.Terrain);
                if (!isPointInside)
                {
                    continue;
                }

                // Проверка, есть ли в найденной локации населённые пункты.
                var freeLocaltion1 = globe.Terrain[freeX][freeY];

                if (!globe.LocalitiesCells.TryGetValue(freeLocaltion1, out var freeCheckLocality))
                {
                    freeLocaltion = globe.Terrain[freeX][freeY];
                }
            }

            if (freeLocaltion != null)
            {
                // Свободный узел был найден.
                // Тогда создаём здесь населённый пункт с доминирующей специаьностью агента.
                // Популяция нового нас.пункта минимальна.
                // Одна единица популяци из текущего нас.пункта снимается.
                // Считается, что часть жителей мигрировали для начала строительства нового нас.пункта.

                var localityName = globe.GetLocalityName(dice);

                var createdLocality = new Locality
                {
                    Name     = localityName,
                    Branches = new Dictionary <BranchType, int> {
                        { firstBranch.Key, 1 }
                    },
                    Cell = freeLocaltion,

                    Population = 1,
                    Owner      = currentLocality.Owner
                };

                currentLocality.Population--;

                globe.Localities.Add(createdLocality);
                globe.LocalitiesCells[freeLocaltion] = createdLocality;
                globe.ScanResult.Free.Remove(freeLocaltion);
            }
            else
            {
                // Если не удалось найти свободный узел,
                // то агент перемещается в произвольный населённый пункт своего государства.

                TransportHelper.TransportAgentToRandomLocality(globe, dice, agent, currentLocality);
            }
        }
Beispiel #14
0
        /// <summary>Возвращает узлы, напрямую соединённые с указанным узлом.</summary>
        /// <param name="node">Опорный узел, относительно которого выбираются соседние узлы.</param>
        /// <returns>Возвращает набор соседних узлов.</returns>
        public override IEnumerable <IMapNode> GetNext(IMapNode node)
        {
            var hexCurrent   = (HexNode)node;
            var offsetCoords = new OffsetCoords(hexCurrent.OffsetX, hexCurrent.OffsetY);
            var segmentX     = offsetCoords.X / _segmentSize;

            if (offsetCoords.X < 0)
            {
                segmentX--;
            }

            var segmentY = offsetCoords.Y / _segmentSize;

            if (offsetCoords.Y < 0)
            {
                segmentY--;
            }

            var localOffsetX = NormalizeNeighborCoord(offsetCoords.X % _segmentSize);
            var localOffsetY = NormalizeNeighborCoord(offsetCoords.Y % _segmentSize);

            var segmentKey = new SegmentKey(segmentX, segmentY);
            var matrix     = _segmentDict[segmentKey];

            var directions        = HexHelper.GetOffsetClockwise();
            var currentCubeCoords = HexHelper.ConvertToCube(localOffsetX, localOffsetY);

            for (var i = 0; i < 6; i++)
            {
                var dir = directions[i];
                var neighborLocalCube = new CubeCoords(dir.X + currentCubeCoords.X,
                                                       dir.Y + currentCubeCoords.Y,
                                                       dir.Z + currentCubeCoords.Z);

                var neighborLocalOffset = HexHelper.ConvertToOffset(neighborLocalCube);

                var neighborSegmentX = segmentX;
                var neighborSegmentY = segmentY;

                if (neighborLocalOffset.X < 0)
                {
                    neighborSegmentX--;
                }
                else if (neighborLocalOffset.X >= _segmentSize)
                {
                    neighborSegmentX++;
                }

                if (neighborLocalOffset.Y < 0)
                {
                    neighborSegmentY--;
                }
                else if (neighborLocalOffset.Y >= _segmentSize)
                {
                    neighborSegmentY++;
                }

                IMapNode currentNeibour;
                if (neighborSegmentX == segmentX &&
                    neighborSegmentY == segmentY)
                {
                    currentNeibour = matrix[neighborLocalOffset.X, neighborLocalOffset.Y];

                    if (currentNeibour == null)
                    {
                        continue;
                    }

                    yield return(currentNeibour);
                }
            }
        }
        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());
        }