120 lines
5.6 KiB
GDScript
120 lines
5.6 KiB
GDScript
extends GutTest
|
||
|
||
# =============================================================================
|
||
# Test: Gauntlet Movement Buffer System (v2) [Gauntlet #083]
|
||
# Hidden, decaying safe-corridor penalties layered onto candidate scoring.
|
||
# Runs headless; elapsed_time = 0 so 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()
|
||
|
||
# =============================================================================
|
||
# Registration
|
||
# =============================================================================
|
||
|
||
func test_register_buffer_sets_phase_base_penalty():
|
||
manager._register_buffer(Vector2i(5, 5), 40.0)
|
||
assert_true(manager.movement_buffers.has(Vector2i(5, 5)), "Buffer registered")
|
||
assert_almost_eq(manager.movement_buffers[Vector2i(5, 5)]["penalty"], 40.0, 0.001, "Full penalty stored")
|
||
|
||
func test_register_buffer_keeps_strongest():
|
||
manager._register_buffer(Vector2i(5, 5), 20.0)
|
||
manager._register_buffer(Vector2i(5, 5), 40.0)
|
||
assert_almost_eq(manager.movement_buffers[Vector2i(5, 5)]["penalty"], 40.0, 0.001, "Keeps the stronger penalty")
|
||
manager._register_buffer(Vector2i(5, 5), 10.0)
|
||
assert_almost_eq(manager.movement_buffers[Vector2i(5, 5)]["penalty"], 40.0, 0.001, "Weaker refresh does not lower it")
|
||
|
||
# =============================================================================
|
||
# Penalty lookup (inside / adjacent / none / final-window)
|
||
# =============================================================================
|
||
|
||
func test_buffer_penalty_inside_is_full_negative():
|
||
manager._register_buffer(Vector2i(6, 6), 40.0)
|
||
assert_almost_eq(manager._buffer_penalty_at(Vector2i(6, 6)), -40.0, 0.001, "Inside buffer = full negative")
|
||
|
||
func test_buffer_penalty_adjacent_is_half():
|
||
manager._register_buffer(Vector2i(6, 6), 40.0)
|
||
assert_almost_eq(manager._buffer_penalty_at(Vector2i(7, 6)), -20.0, 0.001, "Adjacent buffer = half penalty")
|
||
|
||
func test_buffer_penalty_far_is_zero():
|
||
manager._register_buffer(Vector2i(6, 6), 40.0)
|
||
assert_eq(manager._buffer_penalty_at(Vector2i(15, 15)), 0.0, "Far from buffer = 0")
|
||
|
||
func test_buffer_penalty_lifts_in_final_window():
|
||
manager._register_buffer(Vector2i(6, 6), 40.0)
|
||
manager.elapsed_time = manager.gauntlet_round_duration() - 5.0 # within final 30s
|
||
assert_eq(manager._buffer_penalty_at(Vector2i(6, 6)), 0.0, "Final window lifts buffers")
|
||
|
||
func test_buffer_penalty_empty_is_zero():
|
||
assert_eq(manager._buffer_penalty_at(Vector2i(6, 6)), 0.0, "No buffers = 0")
|
||
|
||
# =============================================================================
|
||
# Time decay (−25% every 5s)
|
||
# =============================================================================
|
||
|
||
func test_decay_reduces_penalty_after_interval():
|
||
manager._register_buffer(Vector2i(5, 5), 40.0)
|
||
manager._decay_movement_buffers(manager.BUFFER_DECAY_INTERVAL) # one full step
|
||
assert_almost_eq(manager.movement_buffers[Vector2i(5, 5)]["penalty"], 30.0, 0.001, "−25% after one interval")
|
||
|
||
func test_decay_waits_for_full_interval():
|
||
manager._register_buffer(Vector2i(5, 5), 40.0)
|
||
manager._decay_movement_buffers(manager.BUFFER_DECAY_INTERVAL * 0.5) # not yet
|
||
assert_almost_eq(manager.movement_buffers[Vector2i(5, 5)]["penalty"], 40.0, 0.001, "No decay before interval elapses")
|
||
|
||
func test_decay_prunes_faded_buffers():
|
||
manager._register_buffer(Vector2i(5, 5), manager.BUFFER_MIN_PENALTY + 0.5)
|
||
manager._decay_movement_buffers(manager.BUFFER_DECAY_INTERVAL)
|
||
assert_false(manager.movement_buffers.has(Vector2i(5, 5)), "Faded buffer pruned below BUFFER_MIN_PENALTY")
|
||
|
||
# =============================================================================
|
||
# Phase-change decay (−50%)
|
||
# =============================================================================
|
||
|
||
func test_phase_change_halves_buffers():
|
||
manager._register_buffer(Vector2i(5, 5), 40.0)
|
||
manager._start_phase(manager.Phase.ROUTE_PRESSURE)
|
||
assert_almost_eq(manager.movement_buffers[Vector2i(5, 5)]["penalty"], 20.0, 0.001, "Phase change halves penalty")
|
||
|
||
# =============================================================================
|
||
# Scoring integration
|
||
# =============================================================================
|
||
|
||
func test_score_movement_buffer_uses_detected_corridor():
|
||
# With no players, the proximity floor is inert; a registered buffer still bites.
|
||
manager._register_buffer(Vector2i(5, 5), 40.0)
|
||
assert_almost_eq(manager._score_movement_buffer(Vector2i(5, 5)), -40.0, 0.001, "Score reflects buffer penalty")
|
||
|
||
func test_score_movement_buffer_zero_without_buffers_or_players():
|
||
assert_eq(manager._score_movement_buffer(Vector2i(5, 5)), 0.0, "No buffers, no players = 0")
|
||
|
||
# =============================================================================
|
||
# Scale helper
|
||
# =============================================================================
|
||
|
||
func test_scale_all_buffers_prunes_and_scales():
|
||
manager._register_buffer(Vector2i(1, 1), 40.0)
|
||
manager._register_buffer(Vector2i(2, 2), manager.BUFFER_MIN_PENALTY + 0.1)
|
||
manager._scale_all_buffers(0.5)
|
||
assert_almost_eq(manager.movement_buffers[Vector2i(1, 1)]["penalty"], 20.0, 0.001, "Scaled by 0.5")
|
||
assert_false(manager.movement_buffers.has(Vector2i(2, 2)), "Below-min entry pruned")
|