3D / voxel unit tests
This commit is contained in:
parent
e12e80e511
commit
71cd2b9b41
22 changed files with 4705 additions and 0 deletions
208
tests/unit/pathfinding_3d_test.py
Normal file
208
tests/unit/pathfinding_3d_test.py
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
# 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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue