Files
tekton/tests/test_gauntlet_scoring.gd
T

186 lines
8.0 KiB
GDScript

extends GutTest
# =============================================================================
# Test: Gauntlet Candidate Scoring System (v2) [Gauntlet #073]
# Covers each score component, camping accumulation, and full-formula
# composition. Runs headless (no multiplayer peer), so elapsed_time = 0 and
# the final-30s window is inactive unless a test sets elapsed_time directly.
# =============================================================================
const GauntletManager = preload("res://scripts/managers/gauntlet_manager.gd")
var manager
var main_mock: Node
var gridmap_mock: Node
func before_each():
main_mock = Node.new()
add_child(main_mock)
gridmap_mock = Node.new()
gridmap_mock.name = "EnhancedGridMap"
main_mock.add_child(gridmap_mock)
manager = GauntletManager.new()
main_mock.add_child(manager)
manager.initialize(main_mock, gridmap_mock)
manager.current_phase = 0
func after_each():
if main_mock:
main_mock.queue_free()
# =============================================================================
# LayerPriority
# =============================================================================
func test_layer_priority_matches_phase_weights():
manager.current_phase = 0
# Outer ring cell (corner-ish) gets the phase-0 outer weight (+60).
assert_eq(manager._score_layer_priority(Vector2i(2, 2)), 60.0, "Phase 0 outer = +60")
# Inner cell near center gets phase-0 inner weight (-40).
assert_eq(manager._score_layer_priority(Vector2i(9, 8)), -40.0, "Phase 0 inner = -40")
func test_layer_priority_phase3_inner():
manager.current_phase = 2
assert_eq(manager._score_layer_priority(Vector2i(9, 8)), 60.0, "Phase 2 inner = +60")
# =============================================================================
# StickyNeighbor
# =============================================================================
func test_sticky_neighbor_score_scales():
assert_eq(manager._score_sticky_neighbor(Vector2i(5, 5)), 0.0, "No neighbors = 0")
manager.sticky_cells[Vector2i(5, 6)] = true
assert_eq(manager._score_sticky_neighbor(Vector2i(5, 5)), 8.0, "One neighbor = +8")
func test_sticky_neighbor_score_capped():
# Surround (5,5) on all 8 sides → 8 * 8 = 64, capped at 64.
for dx in range(-1, 2):
for dz in range(-1, 2):
if dx == 0 and dz == 0:
continue
manager.sticky_cells[Vector2i(5 + dx, 5 + dz)] = true
assert_eq(manager._score_sticky_neighbor(Vector2i(5, 5)), 64.0, "Capped at +64")
# =============================================================================
# InwardPressure
# =============================================================================
func test_inward_pressure_higher_near_center():
manager.current_phase = 2
var near = manager._score_inward_pressure(Vector2i(8, 8)) # close to center
var far = manager._score_inward_pressure(Vector2i(1, 1)) # far corner
assert_true(near > far, "Inward pressure stronger near center")
func test_inward_pressure_phase_scaling():
# Same cell, later phase => higher inward pressure ceiling.
var pos := Vector2i(8, 8)
manager.current_phase = 0
var p0 = manager._score_inward_pressure(pos)
manager.current_phase = 2
var p2 = manager._score_inward_pressure(pos)
assert_true(p2 > p0, "Later phase pushes inward harder")
# =============================================================================
# PlayerPressure
# =============================================================================
func test_player_pressure_ring():
# 3 cells from a player → +20.
var players = [Vector2i(5, 5)]
assert_eq(manager._score_player_pressure(Vector2i(8, 5), players), 20.0, "2-4 cells away = +20")
func test_player_pressure_under_player_penalized():
var players = [Vector2i(5, 5)]
# elapsed_time 0, round 180 → not final window → directly under = -50.
assert_eq(manager._score_player_pressure(Vector2i(5, 5), players), -50.0, "Under player (early) = -50")
func test_player_pressure_under_player_final_window():
manager.elapsed_time = manager.gauntlet_round_duration() - 5.0 # within final 30s
var players = [Vector2i(5, 5)]
assert_eq(manager._score_player_pressure(Vector2i(5, 5), players), 10.0, "Under player (final) = +10")
func test_player_pressure_no_players():
assert_eq(manager._score_player_pressure(Vector2i(5, 5), []), 0.0, "No players = 0")
# =============================================================================
# ClusterGrowth
# =============================================================================
func test_cluster_growth_none():
assert_eq(manager._score_cluster_growth(Vector2i(5, 5)), 0.0, "No sticky neighbors = 0")
func test_cluster_growth_expand():
manager.sticky_cells[Vector2i(5, 6)] = true
assert_eq(manager._score_cluster_growth(Vector2i(5, 5)), 15.0, "Expanding cluster = +15")
func test_cluster_growth_connect():
manager.sticky_cells[Vector2i(4, 5)] = true
manager.sticky_cells[Vector2i(6, 5)] = true
manager.sticky_cells[Vector2i(5, 6)] = true
assert_eq(manager._score_cluster_growth(Vector2i(5, 5)), 25.0, "Connecting clusters = +25")
# =============================================================================
# CampingPressure
# =============================================================================
func test_camp_region_grouping():
assert_eq(manager._region_of(Vector2i(0, 0)), Vector2i(0, 0), "Cells 0-3 → region 0")
assert_eq(manager._region_of(Vector2i(5, 7)), Vector2i(1, 1), "Cells 4-7 → region 1")
func test_camping_pressure_thresholds():
var region: Vector2i = manager._region_of(Vector2i(8, 8))
manager._camp_tracking[1] = {"region": region, "time": 6.0}
assert_eq(manager._score_camping_pressure(Vector2i(8, 8)), 20.0, ">5s = +20")
manager._camp_tracking[1]["time"] = 9.0
assert_eq(manager._score_camping_pressure(Vector2i(8, 8)), 40.0, ">8s = +40")
manager._camp_tracking[1]["time"] = 11.0
assert_eq(manager._score_camping_pressure(Vector2i(8, 8)), 60.0, ">10s = +60")
func test_camping_pressure_none():
assert_eq(manager._score_camping_pressure(Vector2i(8, 8)), 0.0, "No camping = 0")
# =============================================================================
# Repetition
# =============================================================================
func test_repetition_penalty():
manager._last_tick_cells = [Vector2i(5, 5)]
assert_eq(manager._score_repetition(Vector2i(5, 6)), -30.0, "Adjacent to last tick = -30")
assert_eq(manager._score_repetition(Vector2i(15, 15)), 0.0, "Far from last tick = 0")
# =============================================================================
# PathSafety (soft penalty)
# =============================================================================
func test_path_safety_no_players_no_penalty():
assert_eq(manager._score_path_safety(Vector2i(5, 5)), 0.0, "No players = no penalty")
# =============================================================================
# Camp tracking accumulation
# =============================================================================
func test_camp_time_for_region_picks_max():
var region := Vector2i(1, 1)
manager._camp_tracking[1] = {"region": region, "time": 3.0}
manager._camp_tracking[2] = {"region": region, "time": 7.0}
assert_almost_eq(manager._camp_time_for_region(region), 7.0, 0.001, "Longest camp time wins")
# =============================================================================
# Full formula composition
# =============================================================================
func test_full_score_runs_and_is_finite():
var s = manager._calculate_candidate_score(Vector2i(5, 5), [])
assert_true(is_finite(s), "Full score is a finite number")
func test_full_score_rewards_sticky_cluster():
# A cell hugging an existing cluster should generally beat an isolated one.
# Average several samples to wash out RandomNoise (±20).
manager.sticky_cells[Vector2i(5, 6)] = true
manager.sticky_cells[Vector2i(6, 5)] = true
var clustered := 0.0
var isolated := 0.0
for _i in range(40):
clustered += manager._calculate_candidate_score(Vector2i(5, 5), [])
isolated += manager._calculate_candidate_score(Vector2i(15, 15), [])
assert_true(clustered > isolated, "Clustered cell scores higher on average")