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); }
/// <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; } }
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); }
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); }
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); }
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; } }
/// <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); }
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); } }
/// <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()); }