McRogueFace/tests/benchmarks/tcod_scale_test.py

141 lines
4.7 KiB
Python

#!/usr/bin/env python3
"""
TCOD Scaling Benchmark - Test pathfinding/FOV on large grids
Tests whether TCOD operations scale acceptably on 1000x1000 grids,
to determine if TCOD data needs chunking or can stay as single logical grid.
"""
import mcrfpy
import sys
import time
# Grid sizes to test
SIZES = [(100, 100), (250, 250), (500, 500), (1000, 1000)]
ITERATIONS = 10
def benchmark_grid_size(grid_x, grid_y):
"""Benchmark TCOD operations for a given grid size"""
results = {}
# Create scene and grid
scene_name = f"bench_{grid_x}x{grid_y}"
mcrfpy.createScene(scene_name)
ui = mcrfpy.sceneUI(scene_name)
texture = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
# Time grid creation
t0 = time.perf_counter()
grid = mcrfpy.Grid(
pos=(0, 0),
size=(800, 600),
grid_size=(grid_x, grid_y),
texture=texture
)
ui.append(grid)
results['create_ms'] = (time.perf_counter() - t0) * 1000
# Set up some walkability (maze-like pattern)
t0 = time.perf_counter()
for y in range(grid_y):
for x in range(grid_x):
cell = grid.at(x, y)
# Create a simple maze: every 3rd cell is a wall
cell.walkable = not ((x % 3 == 0) and (y % 3 == 0))
cell.transparent = cell.walkable
results['setup_walkability_ms'] = (time.perf_counter() - t0) * 1000
# Add an entity for FOV perspective
entity = mcrfpy.Entity(
grid_pos=(grid_x // 2, grid_y // 2),
texture=texture,
sprite_index=64,
grid=grid
)
# Benchmark FOV computation
fov_times = []
for i in range(ITERATIONS):
# Move entity to different positions
ex, ey = (i * 7) % (grid_x - 20) + 10, (i * 11) % (grid_y - 20) + 10
t0 = time.perf_counter()
grid.compute_fov(ex, ey, radius=15)
fov_times.append((time.perf_counter() - t0) * 1000)
results['fov_avg_ms'] = sum(fov_times) / len(fov_times)
results['fov_max_ms'] = max(fov_times)
# Benchmark A* pathfinding (corner to corner)
path_times = []
for i in range(ITERATIONS):
# Path from near origin to near opposite corner
x1, y1 = 1, 1
x2, y2 = grid_x - 2, grid_y - 2
t0 = time.perf_counter()
path = grid.compute_astar_path(x1, y1, x2, y2)
path_times.append((time.perf_counter() - t0) * 1000)
results['astar_avg_ms'] = sum(path_times) / len(path_times)
results['astar_max_ms'] = max(path_times)
results['astar_path_len'] = len(path) if path else 0
# Benchmark Dijkstra (full map distance calculation)
dijkstra_times = []
for i in range(ITERATIONS):
cx, cy = grid_x // 2, grid_y // 2
t0 = time.perf_counter()
grid.compute_dijkstra(cx, cy)
dijkstra_times.append((time.perf_counter() - t0) * 1000)
results['dijkstra_avg_ms'] = sum(dijkstra_times) / len(dijkstra_times)
results['dijkstra_max_ms'] = max(dijkstra_times)
return results
def main():
print("=" * 60)
print("TCOD Scaling Benchmark")
print("=" * 60)
print(f"Testing grid sizes: {SIZES}")
print(f"Iterations per test: {ITERATIONS}")
print()
all_results = {}
for grid_x, grid_y in SIZES:
print(f"\n--- Grid {grid_x}x{grid_y} ({grid_x * grid_y:,} cells) ---")
try:
results = benchmark_grid_size(grid_x, grid_y)
all_results[f"{grid_x}x{grid_y}"] = results
print(f" Creation: {results['create_ms']:.2f}ms")
print(f" Walkability: {results['setup_walkability_ms']:.2f}ms")
print(f" FOV (r=15): {results['fov_avg_ms']:.3f}ms avg, {results['fov_max_ms']:.3f}ms max")
print(f" A* path: {results['astar_avg_ms']:.2f}ms avg, {results['astar_max_ms']:.2f}ms max (len={results['astar_path_len']})")
print(f" Dijkstra: {results['dijkstra_avg_ms']:.2f}ms avg, {results['dijkstra_max_ms']:.2f}ms max")
except Exception as e:
print(f" ERROR: {e}")
all_results[f"{grid_x}x{grid_y}"] = {'error': str(e)}
print("\n" + "=" * 60)
print("SUMMARY - Per-frame budget analysis (targeting 16ms for 60fps)")
print("=" * 60)
for size, results in all_results.items():
if 'error' in results:
print(f" {size}: ERROR")
else:
total_logic = results['fov_avg_ms'] + results['astar_avg_ms']
print(f" {size}: FOV+A* = {total_logic:.2f}ms ({total_logic/16*100:.0f}% of frame budget)")
print("\nDone.")
sys.exit(0)
# Run immediately (no timer needed for this test)
mcrfpy.createScene("init")
mcrfpy.setScene("init")
# Use a timer to let the engine initialize
def run_benchmark(runtime):
main()
mcrfpy.setTimer("bench", run_benchmark, 100)