public int DistToSqrd(Point3d other) { int dx = X - other.X; int dy = Y - other.Y; int dz = Z - other.Z; return dx*dx + dy*dy + dz*dz; }
static List<Range> ProjectBombs(int x, int y, Point3d[] bombsByZ, int radSqr) { var result = new List<Range>(); foreach (var b in bombsByZ) { int dx = b.X - x; int dy = b.Y - y; int xYDistSqr = dx*dx + dy*dy; if (radSqr >= xYDistSqr) { int maxDz = (int)Math.Sqrt(radSqr - xYDistSqr); var newRange = new Range(b.Z - maxDz, b.Z + maxDz + 1); // The intersection of a bomb is symetric around its Z // Also, each bomb has Z >= all previous ones // Thus, if the Begin of the range is less than a previous Begin, the new range totally superceeds the previous range, // and the previous range can be removed for (int iExisting = result.Count - 1; iExisting >= 0; iExisting--) { if (newRange.Begin <= result[iExisting].Begin) { result.RemoveAt(iExisting); } else { // result is build up sorted by Begin // Thus, if Begin at iExisting is too small, there is no point continuing back through the list break; } } result.Add(newRange); } } return result; }
private static int FindMinRadius(Point3d p, List<Point3d> bombs) { var result = bombs.Select(b => b.DistToSqrd(p)).Min(); return result; }
static void Main() { var bombs = new List<Point3d>(); var rand = new Random(); while (bombs.Count < 200) { int x = rand.Next(CubeSize); int y = rand.Next(CubeSize); int z = rand.Next(CubeSize); var next = new Point3d(x, y, z); if (!bombs.Contains(next)) { bombs.Add(next); } } // Add in Corner bombs, as otherwise the corners tend to be the safest spots, which isn't very interesting bombs.Add(new Point3d(0, 0, 0)); bombs.Add(new Point3d(0, 0, 1000)); bombs.Add(new Point3d(0, 1000, 0)); bombs.Add(new Point3d(0, 1000, 1000)); bombs.Add(new Point3d(1000, 0, 0)); bombs.Add(new Point3d(1000, 0, 1000)); bombs.Add(new Point3d(1000, 1000, 0)); bombs.Add(new Point3d(1000, 1000, 1000)); // Add the center of each face bombs.Add(new Point3d(0, 500, 500)); bombs.Add(new Point3d(1000, 500, 500)); bombs.Add(new Point3d(500, 0, 500)); bombs.Add(new Point3d(500, 1000, 500)); bombs.Add(new Point3d(500, 500, 0)); bombs.Add(new Point3d(500, 500, 1000)); Console.WriteLine("{0} bombs - eg {1}", bombs.Count, bombs.First()); Console.WriteLine(); FindSafest(bombs); }
static void FindSafest(List<Point3d> bombs) { var bombsByZ = bombs.OrderBy(b => b.Z).ToArray(); int bestRad = 0; var bestPlace = new Point3d(); // Basic algorithm - each x = x1, y = y1 defines a line of CubeSize points for each Z // Project each bomb with current best radius onto this line to find // section where the sphere around each bomb intersects // Then, only need to check Z values not in these intersections for (int x = 0; x < CubeSize; x++) { for (int y = 0; y < CubeSize; y++) { var exclusions = ProjectBombs(x, y, bombsByZ, bestRad); int oldRad = bestRad; foreach (int z in ValuesLeft(exclusions)) { var p = new Point3d(x, y, z); int candidate = FindMinRadius(p, bombs); if (candidate > bestRad) { bestRad = candidate; bestPlace = p; } } if (bestRad > oldRad) { Console.WriteLine("{0,20} : Cursor : {1} : Found new radius of {2}", TimeStamp(), bestPlace, bestRad); } } } Console.WriteLine("Total Time = {0}", s_timer.Elapsed); Console.WriteLine("Safest radius {0} @ {1}", bestRad, bestPlace); }