public void CreateHolesAndBalls(int numHoles = 4, MinMax ballDistance = null) { // divide entire grid up into 9 zones in a 3x3 formation, // remove the zones with the start and end point // select `numSwitches` zones to put switches in int leftZoneBoundaryX = width / 3; int rightZoneBoundaryX = 2 * width / 3; int topZoneBoundaryY = 2 * height / 3; int bottomZoneBoundaryY = height / 3; int getZone(Vector3Int v) { int z = 0; if (v.x > rightZoneBoundaryX) { z += 2; } else if (v.x > leftZoneBoundaryX) { z += 1; } if (v.y > topZoneBoundaryY) { z += 6; // 3 * 2 } else if (v.y > bottomZoneBoundaryY) { z += 3; } return(z); }; int startZone = getZone(StartingPoint.Coordinates.GetRepresentationalCoordinates()); int endZone = getZone(EndingPoint.Coordinates.GetRepresentationalCoordinates()); List <int> availableZones = Enumerable.Range(0, 9).Where(e => e != startZone && e != endZone).ToList(); List <int> switchZones = new List <int>(); for (int i = 0; i < numHoles; i++) { int num = UnityEngine.Random.Range(0, availableZones.Count); switchZones.Add(availableZones[num]); availableZones.RemoveAt(num); } int border = System.Math.Min(height, width) / 10; HexCoordinates getRandomHexInZone(int zone) { int xZone = zone % 3; int yZone = zone / 3; int xGridPos, yGridPos; switch (xZone) { default: case 0: xGridPos = UnityEngine.Random.Range(0, leftZoneBoundaryX - border); break; case 1: xGridPos = UnityEngine.Random.Range(leftZoneBoundaryX + border, rightZoneBoundaryX - border); break; case 2: xGridPos = UnityEngine.Random.Range(rightZoneBoundaryX + border, width); break; } switch (yZone) { default: case 0: yGridPos = UnityEngine.Random.Range(0, bottomZoneBoundaryY - border); break; case 1: yGridPos = UnityEngine.Random.Range(bottomZoneBoundaryY + border, topZoneBoundaryY - border); break; case 2: yGridPos = UnityEngine.Random.Range(topZoneBoundaryY + border, height); break; } return(HexCoordinates.FromRepresentationalCoordinates(xGridPos, yGridPos)); } List <HexCoordinates> holeHexes = new List <HexCoordinates>(); for (int i = 0; i < switchZones.Count; i++) { bool foundPosition = false; int attempts = 0; int zone = switchZones[i]; while (!foundPosition) { attempts = 0; do { HexCoordinates h = getRandomHexInZone(zone); if (cellInfo[h].Reachable && cellInfo[h].NumTouchedWalls > 0) { Hole s = Hole.CreateInstance(); s.Place(this, h); holeHexes.Add(h); foundPosition = true; Debug.Log($"Found in zone {zone} in {attempts} attempts"); } attempts++; } while (!foundPosition && attempts < 10); // this zone can't find a place, search another zone for position if (!foundPosition) { Debug.Log($"Failed to find in zone {zone}"); int num = UnityEngine.Random.Range(0, availableZones.Count); zone = availableZones[num]; availableZones.RemoveAt(num); } } } if (ballDistance == null) { ballDistance = new MinMax(3, 6); } foreach (HexCoordinates h in holeHexes) { int targetDistance = UnityEngine.Random.Range(ballDistance.Min, ballDistance.Max); var positions = GetAllPositionsInDistanceRange(h, ballDistance); var position = positions.Where(p => cellInfo[p.Item2].NumTouchedWalls <= 3).OrderBy(p => Mathf.Abs(p.Item1 - targetDistance)).First(); Ball b = Ball.CreateInstance(); b.Place(this, position.Item2); } }