Files
tekton/tests/test_gauntlet_movement_buffer.gd

120 lines
5.6 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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")