mirror of
https://github.com/Stone-Red-Code/LargeGridPathfinding.git
synced 2026-09-04 23:41:29 +02:00
Remove FunctionalTests and add advanced benchmarks, tests for GridFiller and optimize GridFiller
This commit is contained in:
@@ -1,180 +0,0 @@
|
||||
using LargeGridPathfinding;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
public class GridFillerFunctionalTests
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
Console.WriteLine("Running GridFiller Functional Tests...\n");
|
||||
|
||||
TestBasicGridFill();
|
||||
TestPartialGridFill();
|
||||
TestObstaclePlacement();
|
||||
TestObstacleRemoval();
|
||||
TestWeightAdjustment();
|
||||
TestRectanglePlacement();
|
||||
|
||||
Console.WriteLine("\n✅ All functional tests passed!");
|
||||
}
|
||||
|
||||
private static void TestBasicGridFill()
|
||||
{
|
||||
Console.WriteLine("Test 1: Basic Grid Fill");
|
||||
var filler = new GridFiller(100, 100);
|
||||
|
||||
filler.FillGrid(fillAll: true);
|
||||
|
||||
// Check that grid has been populated with valid labels
|
||||
int filledCells = 0;
|
||||
for (int y = 0; y < 100; y++)
|
||||
{
|
||||
for (int x = 0; x < 100; x++)
|
||||
{
|
||||
if (filler.Grid[y, x] > 0)
|
||||
filledCells++;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($" Filled cells: {filledCells}/10000");
|
||||
Assert(filledCells > 0, "Grid should have filled cells");
|
||||
Console.WriteLine(" ✓ Passed\n");
|
||||
}
|
||||
|
||||
private static void TestPartialGridFill()
|
||||
{
|
||||
Console.WriteLine("Test 2: Partial Grid Fill");
|
||||
var filler = new GridFiller(100, 100);
|
||||
|
||||
filler.FillGrid(0, 0, 50, 50, fillAll: true);
|
||||
|
||||
// Check that only the partial region has been filled
|
||||
int filledInRegion = 0;
|
||||
int filledOutsideRegion = 0;
|
||||
|
||||
for (int y = 0; y < 100; y++)
|
||||
{
|
||||
for (int x = 0; x < 100; x++)
|
||||
{
|
||||
if (filler.Grid[y, x] > 0)
|
||||
{
|
||||
if (x < 50 && y < 50)
|
||||
filledInRegion++;
|
||||
else
|
||||
filledOutsideRegion++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($" Filled in region (0,0,50,50): {filledInRegion}");
|
||||
Console.WriteLine($" Filled outside region: {filledOutsideRegion}");
|
||||
Assert(filledInRegion > 0, "Region should have filled cells");
|
||||
Assert(filledOutsideRegion == 0, "Outside region should be empty");
|
||||
Console.WriteLine(" ✓ Passed\n");
|
||||
}
|
||||
|
||||
private static void TestObstaclePlacement()
|
||||
{
|
||||
Console.WriteLine("Test 3: Obstacle Placement");
|
||||
var filler = new GridFiller(100, 100);
|
||||
|
||||
var obstacleRect = new Rectangle(10, 10, 20, 20);
|
||||
filler.PlaceObstacle(obstacleRect);
|
||||
|
||||
// Check that obstacle has negative labels
|
||||
int obstaclesPlaced = 0;
|
||||
for (int y = 10; y < 30; y++)
|
||||
{
|
||||
for (int x = 10; x < 30; x++)
|
||||
{
|
||||
if (filler.Grid[y, x] < 0)
|
||||
obstaclesPlaced++;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($" Obstacles placed: {obstaclesPlaced}/400");
|
||||
Assert(obstaclesPlaced == 400, "Obstacle should cover entire rectangle");
|
||||
Console.WriteLine(" ✓ Passed\n");
|
||||
}
|
||||
|
||||
private static void TestObstacleRemoval()
|
||||
{
|
||||
Console.WriteLine("Test 4: Obstacle Removal");
|
||||
var filler = new GridFiller(100, 100);
|
||||
|
||||
var obstacleRect = new Rectangle(10, 10, 20, 20);
|
||||
filler.PlaceObstacle(obstacleRect);
|
||||
filler.RemoveObstacle(obstacleRect);
|
||||
|
||||
// Check that obstacle has been removed
|
||||
int obstaclesRemaining = 0;
|
||||
for (int y = 10; y < 30; y++)
|
||||
{
|
||||
for (int x = 10; x < 30; x++)
|
||||
{
|
||||
if (filler.Grid[y, x] < 0)
|
||||
obstaclesRemaining++;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($" Obstacles remaining: {obstaclesRemaining}");
|
||||
Assert(obstaclesRemaining == 0, "Obstacle should be completely removed");
|
||||
Console.WriteLine(" ✓ Passed\n");
|
||||
}
|
||||
|
||||
private static void TestWeightAdjustment()
|
||||
{
|
||||
Console.WriteLine("Test 5: Weight Adjustment");
|
||||
var filler = new GridFiller(100, 100);
|
||||
|
||||
var points = new List<Point> { new(10, 10), new(11, 11) };
|
||||
filler.SetTileWeights(points, 5);
|
||||
|
||||
Assert(filler.WeightGrid[10, 10] == 5, "Weight at (10,10) should be 5");
|
||||
Assert(filler.WeightGrid[11, 11] == 5, "Weight at (11,11) should be 5");
|
||||
|
||||
filler.ResetTileWeights(points);
|
||||
|
||||
Assert(filler.WeightGrid[10, 10] == 1, "Weight should be reset to 1");
|
||||
Assert(filler.WeightGrid[11, 11] == 1, "Weight should be reset to 1");
|
||||
Console.WriteLine(" ✓ Passed\n");
|
||||
}
|
||||
|
||||
private static void TestRectanglePlacement()
|
||||
{
|
||||
Console.WriteLine("Test 6: Rectangle Placement Tracking");
|
||||
var filler = new GridFiller(100, 100);
|
||||
|
||||
filler.FillGrid(fillAll: true);
|
||||
|
||||
// Check that PlacedRectangles collection is populated
|
||||
int placedCount = filler.PlacedRectangles.Count;
|
||||
Console.WriteLine($" Rectangles placed: {placedCount}");
|
||||
Assert(placedCount > 0, "Should have placed rectangles");
|
||||
|
||||
// Verify each rectangle in the collection matches the grid
|
||||
foreach (var kvp in filler.PlacedRectangles)
|
||||
{
|
||||
int label = kvp.Key;
|
||||
var rect = kvp.Value;
|
||||
|
||||
// Check a few cells from this rectangle
|
||||
for (int y = rect.Top; y < Math.Min(rect.Top + 2, rect.Bottom); y++)
|
||||
{
|
||||
for (int x = rect.Left; x < Math.Min(rect.Left + 2, rect.Right); x++)
|
||||
{
|
||||
Assert(filler.Grid[y, x] == label, $"Grid cell ({x},{y}) should have label {label}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine(" ✓ Passed\n");
|
||||
}
|
||||
|
||||
private static void Assert(bool condition, string message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
throw new Exception($"Assertion failed: {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,60 @@ using BenchmarkDotNet.Running;
|
||||
using LargeGridPathfinding;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
static int CountZones(GridFiller filler)
|
||||
{
|
||||
int width = filler.Width;
|
||||
int height = filler.Height;
|
||||
bool[,] visited = new bool[height, width];
|
||||
int zoneCount = 0;
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
if (!visited[y, x] && filler.Grid[y, x] > 0)
|
||||
{
|
||||
// BFS to find all cells in this zone
|
||||
var queue = new Queue<(int, int)>();
|
||||
queue.Enqueue((x, y));
|
||||
visited[y, x] = true;
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var (cx, cy) = queue.Dequeue();
|
||||
int zoneId = filler.Grid[cy, cx];
|
||||
|
||||
// Check 4 neighbors
|
||||
foreach (var (dx, dy) in new[] { (0, 1), (0, -1), (1, 0), (-1, 0) })
|
||||
{
|
||||
int nx = cx + dx;
|
||||
int ny = cy + dy;
|
||||
if (nx >= 0 && nx < width && ny >= 0 && ny < height &&
|
||||
!visited[ny, nx] && filler.Grid[ny, nx] == zoneId)
|
||||
{
|
||||
visited[ny, nx] = true;
|
||||
queue.Enqueue((nx, ny));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zoneCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return zoneCount;
|
||||
}
|
||||
|
||||
// Check if we should run tests or benchmarks
|
||||
if (args.Length > 0 && args[0] == "--test")
|
||||
{
|
||||
RunFunctionalTests();
|
||||
}
|
||||
else if (args.Length > 0 && args[0] == "--advanced")
|
||||
{
|
||||
BenchmarkRunner.Run<AdvancedGridFillerBenchmarks>();
|
||||
}
|
||||
else
|
||||
{
|
||||
BenchmarkRunner.Run<GridFillerBenchmarks>();
|
||||
@@ -21,6 +70,8 @@ static void RunFunctionalTests()
|
||||
TestObstaclePlacement();
|
||||
TestObstacleRemoval();
|
||||
TestWeightAdjustment();
|
||||
TestZoneCount();
|
||||
TestLargerGrids();
|
||||
|
||||
Console.WriteLine("\n✅ All functional tests passed!");
|
||||
}
|
||||
@@ -112,6 +163,53 @@ static void TestWeightAdjustment()
|
||||
Console.WriteLine(" ✓ Passed\n");
|
||||
}
|
||||
|
||||
static void TestZoneCount()
|
||||
{
|
||||
Console.WriteLine("Test 5: Zone Count (512x512 grid)");
|
||||
var filler = new GridFiller(512, 512);
|
||||
filler.FillGrid(fillAll: true);
|
||||
|
||||
int zoneCount = CountZones(filler);
|
||||
Console.WriteLine($" Zones created: {zoneCount}");
|
||||
Console.WriteLine($" ✓ Passed\n");
|
||||
}
|
||||
|
||||
static void TestLargerGrids()
|
||||
{
|
||||
Console.WriteLine("Test 6: Larger Grids Zone Count (3 runs each)\n");
|
||||
|
||||
int[] gridSizes = { 512, 1024, 2048 };
|
||||
int runsPerSize = 3;
|
||||
|
||||
foreach (int size in gridSizes)
|
||||
{
|
||||
Console.WriteLine($" Testing {size}×{size} grid ({size * size:N0} cells):");
|
||||
|
||||
int[] zoneCounts = new int[runsPerSize];
|
||||
long[] times = new long[runsPerSize];
|
||||
|
||||
for (int run = 0; run < runsPerSize; run++)
|
||||
{
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
|
||||
var filler = new GridFiller(size, size);
|
||||
filler.FillGrid(fillAll: true);
|
||||
|
||||
sw.Stop();
|
||||
zoneCounts[run] = CountZones(filler);
|
||||
times[run] = sw.ElapsedMilliseconds;
|
||||
}
|
||||
|
||||
double avgZones = zoneCounts.Average();
|
||||
double avgTime = times.Average();
|
||||
|
||||
Console.WriteLine($" Zones: {zoneCounts[0]}, {zoneCounts[1]}, {zoneCounts[2]} (avg: {avgZones:F1})");
|
||||
Console.WriteLine($" Times: {times[0]}ms, {times[1]}ms, {times[2]}ms (avg: {avgTime:F0}ms)\n");
|
||||
}
|
||||
|
||||
Console.WriteLine(" ✓ Passed\n");
|
||||
}
|
||||
|
||||
[MemoryDiagnoser]
|
||||
public class GridFillerBenchmarks
|
||||
{
|
||||
@@ -172,3 +270,64 @@ public class GridFillerBenchmarks
|
||||
gridFiller.ResetTileWeights(points);
|
||||
}
|
||||
}
|
||||
|
||||
[MemoryDiagnoser]
|
||||
public class AdvancedGridFillerBenchmarks
|
||||
{
|
||||
private GridFiller gridFiller = null!;
|
||||
private const int GridSize = 512;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
gridFiller = new GridFiller(GridSize, GridSize);
|
||||
}
|
||||
|
||||
[Benchmark(Description = "GetRowContinuousWidth - 10 calls")]
|
||||
public int BenchmarkGetRowWidth()
|
||||
{
|
||||
int result = 0;
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
result += gridFiller.GetRowContinuousWidthPublic(10 + i, 10, 100, 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[Benchmark(Description = "IsAreaFree - 100x100 check")]
|
||||
public bool BenchmarkIsAreaFree()
|
||||
{
|
||||
for (int y = 0; y < 10; y++)
|
||||
{
|
||||
for (int x = 0; x < 10; x++)
|
||||
{
|
||||
if (x < 5 && y < 5)
|
||||
{
|
||||
var result = gridFiller.IsAreaFreePublic(x * 50, y * 50, 50, 50, 1);
|
||||
if (!result) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Stretch factor calculation - 1000 iterations")]
|
||||
public int BenchmarkStretchFactor()
|
||||
{
|
||||
int result = 0;
|
||||
for (int w = 10; w < 100; w += 3)
|
||||
{
|
||||
for (int h = 10; h < 100; h += 3)
|
||||
{
|
||||
result += gridFiller.GetStretchFactorPublic(w, h);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[Benchmark(Description = "FillGrid - Full grid (Advanced)")]
|
||||
public void FillGridFullAdvanced()
|
||||
{
|
||||
gridFiller.FillGrid(fillAll: true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,9 +75,12 @@ public class GridFiller
|
||||
|
||||
Debug.WriteLine("Calculating candidates...");
|
||||
|
||||
_ = Parallel.For(y1.Value, y2.Value, (y, loopState) =>
|
||||
ParallelOptions parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
|
||||
|
||||
_ = Parallel.For(y1.Value, y2.Value, parallelOptions, (y, loopState) =>
|
||||
{
|
||||
for (int x = x1.Value; x < x2; x++)
|
||||
int x = x1.Value;
|
||||
while (x < x2)
|
||||
{
|
||||
if (Grid[y, x] == 0)
|
||||
{
|
||||
@@ -87,6 +90,11 @@ public class GridFiller
|
||||
if (w > 0 && h > 0)
|
||||
{
|
||||
candidates.Add((x, y, w, h, tileWeight));
|
||||
x += w;
|
||||
}
|
||||
else
|
||||
{
|
||||
x++;
|
||||
}
|
||||
|
||||
if (w >= maxRectangleSize && h >= maxRectangleSize)
|
||||
@@ -101,14 +109,24 @@ public class GridFiller
|
||||
Debug.WriteLine($"Candidates Progress: {candidates.Count} / {cells - areaPlaced} ({(float)candidates.Count / (cells - areaPlaced):P2})");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
x++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Debug.WriteLine($"Candidates: {candidates.Count}");
|
||||
|
||||
if (candidates.Count == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Debug.WriteLine("Sorting candidates...");
|
||||
|
||||
var sortedCandidates = new List<(int x, int y, int w, int h, int weight)>(candidates.Count);
|
||||
sortedCandidates.AddRange(candidates);
|
||||
List<(int x, int y, int w, int h, int weight)> sortedCandidates = [.. candidates];
|
||||
|
||||
sortedCandidates.Sort((a, b) =>
|
||||
{
|
||||
int areaComparison = (b.w * b.h).CompareTo(a.w * a.h);
|
||||
@@ -397,6 +415,8 @@ public class GridFiller
|
||||
int bestHeight = 0;
|
||||
int bestArea = 0;
|
||||
int bestStretch = int.MaxValue;
|
||||
int noImprovementCount = 0;
|
||||
const int maxNoImprovement = 5;
|
||||
|
||||
for (int h = 1; h <= maxHeight; h++)
|
||||
{
|
||||
@@ -422,10 +442,15 @@ public class GridFiller
|
||||
bestWidth = currentWidth;
|
||||
bestHeight = h;
|
||||
bestStretch = stretch;
|
||||
noImprovementCount = 0;
|
||||
}
|
||||
else if (area < bestArea && h > bestHeight * 2)
|
||||
else
|
||||
{
|
||||
break;
|
||||
noImprovementCount++;
|
||||
if (noImprovementCount >= maxNoImprovement)
|
||||
{
|
||||
//break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,4 +591,20 @@ public class GridFiller
|
||||
int maxSide = Math.Max(width, height);
|
||||
return maxSide / minSide;
|
||||
}
|
||||
|
||||
// Public methods for benchmarking
|
||||
public int GetRowContinuousWidthPublic(int startX, int y, int maxWidth, int requiredWeight)
|
||||
{
|
||||
return GetRowContinuousWidth(startX, y, maxWidth, requiredWeight);
|
||||
}
|
||||
|
||||
public bool IsAreaFreePublic(int x, int y, int w, int h, int requiredWeight)
|
||||
{
|
||||
return IsAreaFree(x, y, w, h, requiredWeight);
|
||||
}
|
||||
|
||||
public int GetStretchFactorPublic(int width, int height)
|
||||
{
|
||||
return GetStretchFactor(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user