private static void ConnectOpenRegionsToUnited(Matrix <bool> matrix, List <RegionDraft> openRegions, List <RegionDraft> unitedRegions) { 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); } }
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 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); }
//TODO Постараться объединить GetStartProvinceCoords и GetHomeProvinceCoords private TerrainCell GetHomeProvinceCoords(Globe globe, TerrainCell startProvince) { // Выбираем основной город, являющийся стартовым. // По городу определяем провинцию. var currentCubeCoords = HexHelper.ConvertToCube(startProvince.Coords); var availableLocalities = globe.Localities .Where(x => x.Cell != startProvince) .Where(x => HexHelper.ConvertToCube(x.Cell.Coords).DistanceTo(currentCubeCoords) > 2) .Where(x => HexHelper.ConvertToCube(x.Cell.Coords).DistanceTo(currentCubeCoords) <= 5) .ToArray(); Locality selectedLocality; if (availableLocalities.Any()) { var localityIndex = _dice.Roll(0, availableLocalities.Count() - 1); selectedLocality = availableLocalities[localityIndex]; } else { selectedLocality = globe.Localities.LastOrDefault(); } if (selectedLocality == null) { //TODO Создать отдельный класс исключений GlobeGenerationException. throw new InvalidOperationException("Не удалось выбрать локацию для дома."); } return(selectedLocality.Cell); }
public HexNode(int x, int y, bool isObstacle) { OffsetX = x; OffsetY = y; IsObstacle = isObstacle; CubeCoords = HexHelper.ConvertToCube(x, y); }
public HexNode(OffsetCoords coords) { OffsetCoords = coords; var x = coords.X; var y = coords.Y; CubeCoords = HexHelper.ConvertToCube(x, y); }
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 ConvertToCubeTest(int offsetX, int offsetY, int cubeX, int cubeY, int cubeZ) { // ARRANGE var expectedCubeCoords = new CubeCoords(cubeX, cubeY, cubeZ); // ACT var factCubeCoords = HexHelper.ConvertToCube(offsetX, offsetY); // ASSERT factCubeCoords.Should().BeEquivalentTo(expectedCubeCoords); }
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); } } }
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 FillMap(ISectorMap map, int mapWidth, int mapHeight, int mapSize, HexNode centerNode) { for (var x = 0; x < mapWidth; x++) { for (var y = 0; y < mapHeight; y++) { var testOffsetCoords = new OffsetCoords(x, y); var testCubeCoords = HexHelper.ConvertToCube(testOffsetCoords); var distanceToCenter = centerNode.CubeCoords.DistanceTo(testCubeCoords); if (distanceToCenter > 0 && distanceToCenter <= mapSize) { var node = new HexNode(testOffsetCoords); map.AddNode(node); } } } }
private static IEnumerable <OffsetCoords> GetAvailableCoords(OffsetCoords[] regionDraftCoords) { var coordHash = new HashSet <OffsetCoords>(regionDraftCoords); var neighborCubeOffsets = HexHelper.GetOffsetClockwise(); foreach (var coords in regionDraftCoords) { var cube = HexHelper.ConvertToCube(coords); var isValid = HasAllHeighbors(coordHash, neighborCubeOffsets, cube); if (isValid) { yield return(coords); } } }
public void CubeDrawLine_DifferentPoints_ReverseEquals(int sOffsetX, int sOffsetY, int offsetX, int offsetY) { // ARRANGE var startCubeCoords = HexHelper.ConvertToCube(sOffsetX, sOffsetY); var finishCubeCoords = HexHelper.ConvertToCube(offsetX, offsetY); // ACT var line = CubeCoordsHelper.CubeDrawLine(startCubeCoords, finishCubeCoords); var reverseLine = CubeCoordsHelper.CubeDrawLine(finishCubeCoords, startCubeCoords); // ASSERT for (int i = 0; i < line.Length; i++) { var linePoint = line[i]; var reversePoint = reverseLine[reverseLine.Length - i - 1]; reversePoint.Should().Be(linePoint); } }
public static IEnumerable <GlobeRegionNode> GetNeighborBorderNodes(GlobeRegionNode currentTerrainNode, TerrainCell currentTerrainCell, IEnumerable <GlobeRegionNode> targetRegionBorderNodes, TerrainCell targetNeighborTerrainCell) { if (targetRegionBorderNodes == null) { throw new ArgumentNullException(nameof(targetRegionBorderNodes)); } if (currentTerrainCell == null) { throw new ArgumentNullException(nameof(currentTerrainCell)); } if (targetNeighborTerrainCell == null) { throw new ArgumentNullException(nameof(targetNeighborTerrainCell)); } const int REGION_SIZE = 20; var regionNodeOffsetX = targetNeighborTerrainCell.Coords.X - currentTerrainCell.Coords.X; var regionNodeOffsetY = targetNeighborTerrainCell.Coords.Y - currentTerrainCell.Coords.Y; var targetRegionBorderNodeOffsetCoords = targetRegionBorderNodes.Select(node => new { RegionOffsetCoords = new OffsetCoords( regionNodeOffsetX >= 0 ? node.OffsetX + regionNodeOffsetX * REGION_SIZE : -(REGION_SIZE - node.OffsetX), regionNodeOffsetY >= 0 ? node.OffsetY + regionNodeOffsetY * REGION_SIZE : -(REGION_SIZE - node.OffsetY) ), Node = node }); var transitionNodeCoords = targetRegionBorderNodeOffsetCoords.Where(coords => HexHelper.ConvertToCube(coords.RegionOffsetCoords.X, coords.RegionOffsetCoords.Y) .DistanceTo(currentTerrainNode.CubeCoords) <= 1); return(transitionNodeCoords.Select(x => x.Node)); }
public void CubeDrawLine_DifferentPoints_LineIsSolid(int sOffsetX, int sOffsetY, int offsetX, int offsetY) { // ARRANGE var startCubeCoords = HexHelper.ConvertToCube(sOffsetX, sOffsetY); var finishCubeCoords = HexHelper.ConvertToCube(offsetX, offsetY); // ACT var line = CubeCoordsHelper.CubeDrawLine(startCubeCoords, finishCubeCoords); // ASSERT foreach (var coord in line) { var sameCoords = line.Where(x => x == coord); sameCoords.Count().Should().Be(1); // Проверяем, что у каждой точки линии есть соседи, // т.е. нет изолированных разорванных точк. var neibourOffsets = HexHelper.GetOffsetClockwise(); var hasNeighbor = false; foreach (var neibourOffset in neibourOffsets) { var neighborCoord = coord + neibourOffset; var foundCoords = line.Where(x => x == neighborCoord); var hasNeighborInThisDirection = foundCoords.Any(); if (hasNeighborInThisDirection) { hasNeighbor = true; } } hasNeighbor.Should().Be(true); } }
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()); }
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()); }
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); } } }
public HexNode(int x, int y) { OffsetX = x; OffsetY = y; CubeCoords = HexHelper.ConvertToCube(x, y); }