ajhahn.de
← Theria
GDScript 75 lines
extends GutTest
## The deterministic grid pathfinder. These pin the routing contract the client and the bots lean
## on: a clear line is a straight shot, a blocked line routes around the obstacle on a
## collision-free path, a target inside an obstacle resolves to free ground, and the same query
## always yields the same path (so a bot's route replays identically). Pure data checks — no engine
## coupling, the same discipline as the rest of the sim tests.


func test_clear_line_is_a_direct_single_waypoint() -> void:
	var nav := NavGrid.new()
	var from := Vector2(0.0, 0.0)
	var to := Vector2(200.0, 0.0)  # open ground near the map centre
	var path := nav.find_path(from, to)
	assert_eq(path.size(), 1, "a clear line needs no intermediate waypoints")
	assert_eq(path[0], to, "the single waypoint is the destination itself")


func test_from_equals_to_returns_the_point() -> void:
	var nav := NavGrid.new()
	var p := Vector2(0.0, 0.0)
	var path := nav.find_path(p, p)
	assert_eq(path.size(), 1, "a zero-length move is one waypoint")
	assert_eq(path[0], p)


func test_routes_around_a_blocking_obstacle_on_a_clear_path() -> void:
	var nav := NavGrid.new()
	var center := MapData.nexus_for_team(0)  # a real obstacle, open ground around it
	var from := center + Vector2(700.0, 0.0)
	var to := center - Vector2(700.0, 0.0)
	assert_false(nav.segment_clear(from, to), "the straight line must run through the obstacle")
	var path := nav.find_path(from, to)
	assert_gt(path.size(), 0, "a routable goal yields a path")
	# Every leg of the realised route (from the unit's position through the waypoints) is clear.
	var prev := from
	for w in path:
		assert_true(nav.segment_clear(prev, w), "each leg of the route avoids the obstacles")
		prev = w
	assert_lt(
		path[path.size() - 1].distance_to(to),
		NavGrid.CELL * 2.0,
		"the route ends on the requested destination",
	)


func test_target_inside_an_obstacle_resolves_to_free_ground() -> void:
	var nav := NavGrid.new()
	var center := MapData.nexus_for_team(0)
	var from := center + Vector2(1000.0, 0.0)
	var path := nav.find_path(from, center)  # aim straight at the obstacle's centre
	assert_gt(path.size(), 0, "a blocked target still yields a path to its edge")
	var last := path[path.size() - 1]
	assert_false(
		MapData.point_blocked(last, SimCore.UNIT_RADIUS),
		"the route ends on free ground, not inside the obstacle",
	)


func test_same_query_yields_an_identical_path() -> void:
	var nav := NavGrid.new()
	var center := MapData.nexus_for_team(0)
	var from := center + Vector2(700.0, 0.0)
	var to := center - Vector2(700.0, 0.0)
	var a := nav.find_path(from, to)
	var b := nav.find_path(from, to)
	assert_eq(a, b, "pathfinding is deterministic — the same query is byte-identical")


func test_each_spawn_can_route_toward_the_enemy_base() -> void:
	# The map stays traversable: a team can always route from its base to the enemy's.
	var nav := NavGrid.new()
	for team in MapData.NEXUS_POSITIONS.size():
		var path := nav.find_path(MapData.spawn_for_team(team), MapData.spawn_for_team(1 - team))
		assert_gt(path.size(), 0, "a team can route from its base toward the enemy base")