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")