private void PlaceGatesAndTowers(IEnumerable <Vector2> allowedTowerPositions, int minNumberOfGates, int maxNumberOfGates, List <Vector2> illegalGatePositions) { var cityPatches = _town.Patches.Where(p => p.WithinCity).ToList(); var outsidePatches = _town.Patches.Except(cityPatches).ToList(); var possibleGates = allowedTowerPositions .Where(v => cityPatches.Count(cp => cp.Shape.Vertices.Contains(v)) > 1) .Except(illegalGatePositions) .OrderByDescending(v => (_town.Castle.Patch.Center - v).Length) .ToList(); var towers = Circumference.ToList(); var gates = new List <Vector2>(); var attempts = 0; while ((gates.Count < minNumberOfGates || attempts < 4) && possibleGates.Any() && gates.Count < maxNumberOfGates) { attempts++; var newGate = possibleGates.First(); try { possibleGates.Remove(newGate); possibleGates.RemoveFirstIfPossible(); possibleGates.RemoveFirstIfPossible(); var outerNeighbours = outsidePatches .Where(p => p.Shape.Vertices.Any(v => v.Equals(newGate))) .ToList(); if (outerNeighbours.Count == 1) { var neighbour = outerNeighbours.Single(); var wallPoint = neighbour.Shape.GetNextVertex(newGate) - neighbour.Shape.GetPreviousVertex(newGate); var outPoint = new Vector2(wallPoint.y, -wallPoint.x); var possibleSplitPoints = neighbour.Shape.Vertices.Except(Circumference).ToList(); var farthest = possibleSplitPoints.OrderByDescending(p => { var dir = p - newGate; return(Vector2.Dot(dir, outPoint) / dir.Length); }).First(); var newPatches = neighbour.Shape.Split(newGate, farthest).Select(p => Patch.FromPolygon(_town, p)).ToList(); if (newPatches.Any(p => p.Shape.Vertices.Count < 3)) { farthest = neighbour.Shape.GetNextVertex(farthest); newPatches = neighbour.Shape.Split(newGate, farthest).Select(p => Patch.FromPolygon(_town, p)).ToList(); if (newPatches.Any(p => p.Shape.Vertices.Count < 3)) { throw new InvalidOperationException( "Splitting patch resulted in polygon with only two points"); } } _town.Patches.Remove(neighbour); _town.Patches.AddRange(newPatches); } gates.Add(newGate); towers.Remove(newGate); } catch (InvalidOperationException) { } } _town.Gates.AddRange(gates); Gates.AddRange(gates); Towers.AddRange(towers); }