McRogueFace/tests/unit/pathfinding_3d_test.py
2026-02-06 16:15:07 -05:00

208 lines
6.1 KiB
Python

# pathfinding_3d_test.py - Unit tests for 3D pathfinding
# Tests A* pathfinding on VoxelPoint navigation grid
import mcrfpy
import sys
def test_simple_path():
"""Test pathfinding on an open grid"""
print("Testing simple pathfinding...")
viewport = mcrfpy.Viewport3D(pos=(0, 0), size=(320, 240))
viewport.grid_size = (10, 10)
# Find path from corner to corner
path = viewport.find_path((0, 0), (9, 9))
# Should find a path
assert len(path) > 0, "Expected a path, got empty list"
# Path should end at destination (start is not included)
assert path[-1] == (9, 9), f"Path should end at (9, 9), got {path[-1]}"
# Path length should be reasonable (diagonal allows shorter paths)
# Manhattan distance is 18, but with diagonals it can be ~9-14 steps
assert len(path) >= 9 and len(path) <= 18, f"Path length {len(path)} is unexpected"
print(f" PASS: Simple pathfinding ({len(path)} steps)")
def test_path_with_obstacles():
"""Test pathfinding around obstacles"""
print("Testing pathfinding with obstacles...")
viewport = mcrfpy.Viewport3D(pos=(0, 0), size=(320, 240))
viewport.grid_size = (10, 10)
# Create a wall blocking direct path
# Wall from (4, 0) to (4, 8)
for z in range(9):
viewport.at(4, z).walkable = False
# Find path from left side to right side
path = viewport.find_path((2, 5), (7, 5))
# Should find a path (going around the wall via z=9)
assert len(path) > 0, "Expected a path around the wall"
# Verify path doesn't go through wall
for x, z in path:
if x == 4 and z < 9:
assert False, f"Path goes through wall at ({x}, {z})"
print(f" PASS: Pathfinding with obstacles ({len(path)} steps)")
def test_no_path():
"""Test pathfinding when no path exists"""
print("Testing no path scenario...")
viewport = mcrfpy.Viewport3D(pos=(0, 0), size=(320, 240))
viewport.grid_size = (10, 10)
# Create a complete wall blocking all paths
# Wall from (5, 0) to (5, 9) - blocks entire grid
for z in range(10):
viewport.at(5, z).walkable = False
# Try to find path from left to right
path = viewport.find_path((2, 5), (7, 5))
# Should return empty list (no path)
assert len(path) == 0, f"Expected empty path, got {len(path)} steps"
print(" PASS: No path returns empty list")
def test_start_equals_end():
"""Test pathfinding when start equals end"""
print("Testing start equals end...")
viewport = mcrfpy.Viewport3D(pos=(0, 0), size=(320, 240))
viewport.grid_size = (10, 10)
# Find path to same location
path = viewport.find_path((5, 5), (5, 5))
# Should return empty path (already there)
assert len(path) == 0, f"Expected empty path for start==end, got {len(path)} steps"
print(" PASS: Start equals end")
def test_adjacent_path():
"""Test pathfinding to adjacent cell"""
print("Testing adjacent cell pathfinding...")
viewport = mcrfpy.Viewport3D(pos=(0, 0), size=(320, 240))
viewport.grid_size = (10, 10)
# Find path to adjacent cell
path = viewport.find_path((5, 5), (5, 6))
# Should be a single step
assert len(path) == 1, f"Expected 1 step, got {len(path)}"
assert path[0] == (5, 6), f"Expected (5, 6), got {path[0]}"
print(" PASS: Adjacent cell pathfinding")
def test_heightmap_threshold():
"""Test apply_threshold sets walkability"""
print("Testing HeightMap threshold...")
viewport = mcrfpy.Viewport3D(pos=(0, 0), size=(320, 240))
# Create a heightmap
hm = mcrfpy.HeightMap((10, 10))
# Set heights: left half low (0.2), right half high (0.8)
for z in range(10):
for x in range(5):
hm[x, z] = 0.2
for x in range(5, 10):
hm[x, z] = 0.8
# Initialize grid
viewport.grid_size = (10, 10)
# Apply threshold: mark high areas (>0.6) as unwalkable
viewport.apply_threshold(hm, 0.6, 1.0, walkable=False)
# Check left side is walkable
assert viewport.at(2, 5).walkable == True, "Left side should be walkable"
# Check right side is unwalkable
assert viewport.at(7, 5).walkable == False, "Right side should be unwalkable"
# Pathfinding should fail to cross
path = viewport.find_path((2, 5), (7, 5))
assert len(path) == 0, "Path should not exist through unwalkable terrain"
print(" PASS: HeightMap threshold")
def test_slope_cost():
"""Test slope cost calculation"""
print("Testing slope cost...")
viewport = mcrfpy.Viewport3D(pos=(0, 0), size=(320, 240))
viewport.grid_size = (10, 10)
# Create terrain with a steep slope
# Set heights manually
for z in range(10):
for x in range(10):
viewport.at(x, z).height = 0.0
# Create a cliff at x=5
for z in range(10):
for x in range(5, 10):
viewport.at(x, z).height = 2.0 # 2.0 units high
# Apply slope cost: max slope 0.5, mark steeper as unwalkable
viewport.set_slope_cost(max_slope=0.5, cost_multiplier=2.0)
# Check that cells at the cliff edge are marked unwalkable
# Cell at (4, 5) borders (5, 5) which is 2.0 higher
assert viewport.at(4, 5).walkable == False, "Cliff edge should be unwalkable"
assert viewport.at(5, 5).walkable == False, "Cliff top edge should be unwalkable"
# Cells away from cliff should still be walkable
assert viewport.at(0, 5).walkable == True, "Flat area should be walkable"
assert viewport.at(9, 5).walkable == True, "Flat high area should be walkable"
print(" PASS: Slope cost")
def run_all_tests():
"""Run all unit tests"""
print("=" * 60)
print("3D Pathfinding Unit Tests")
print("=" * 60)
try:
test_simple_path()
test_path_with_obstacles()
test_no_path()
test_start_equals_end()
test_adjacent_path()
test_heightmap_threshold()
test_slope_cost()
print("=" * 60)
print("ALL TESTS PASSED")
print("=" * 60)
sys.exit(0)
except AssertionError as e:
print(f"FAIL: {e}")
sys.exit(1)
except Exception as e:
print(f"ERROR: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
# Run tests
run_all_tests()