test(scenes): ✅ Add test script and scene for courier era 10 instant synchronization validation
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
8e23aeca66
commit
a538d38832
2 changed files with 187 additions and 0 deletions
181
src/game/engine/scenes/tests/courier_era10_instant_sync_proof.gd
Normal file
181
src/game/engine/scenes/tests/courier_era10_instant_sync_proof.gd
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
extends Node
|
||||
## p3-01 bullet 11: era_10 / instant sync proof.
|
||||
##
|
||||
## Tests two sub-cases:
|
||||
## 1. Era_2 courier over 9 hexes does NOT deliver on step 1 (no Adamantine Echo).
|
||||
## Confirms the baseline slow-courier behavior.
|
||||
## 2. A courier whose position already equals the receiver capital delivers on
|
||||
## the very next step (simulates the end-state of instant delivery; the
|
||||
## step function checks position == dest and emits SharedMapDelivered).
|
||||
##
|
||||
## NOTE: Full Adamantine Echo path (both players hold the wonder → instant
|
||||
## delivery from any distance regardless of position) is covered by Rust unit
|
||||
## test `adamantine_echo_delivers_instantly` in mc-turn/src/courier_resolver.rs,
|
||||
## since injecting wonders into GdGameState is not yet exposed to GDScript.
|
||||
|
||||
var _pass_count: int = 0
|
||||
var _fail_count: int = 0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not ClassDB.class_exists("GdTradeLedger"):
|
||||
print("[SKIP] GdTradeLedger not registered — GDExtension not loaded")
|
||||
get_tree().quit(0)
|
||||
return
|
||||
_run_proof()
|
||||
_print_results()
|
||||
get_tree().quit(0 if _fail_count == 0 else 1)
|
||||
|
||||
|
||||
func _run_proof() -> void:
|
||||
_test_era2_baseline_not_instant()
|
||||
_test_courier_at_destination_delivers_immediately()
|
||||
|
||||
|
||||
func _test_era2_baseline_not_instant() -> void:
|
||||
# Era_2 over 9 hexes (1 hex/turn) must NOT deliver on step 1.
|
||||
var state: RefCounted = ClassDB.instantiate("GdGameState") as RefCounted
|
||||
if not _assert("baseline: GdGameState instantiates", state != null):
|
||||
return
|
||||
state.call("create_grid", 15, 10)
|
||||
state.call("add_player_militarist", 0, 0)
|
||||
state.call("add_player_militarist", 9, 0)
|
||||
|
||||
var map_view_inst: RefCounted = ClassDB.instantiate("GdCourierMapView") as RefCounted
|
||||
if not _assert("baseline: GdCourierMapView instantiates", map_view_inst != null):
|
||||
return
|
||||
var gd_map_view: RefCounted = map_view_inst.call("from_game_state", state) as RefCounted
|
||||
if not _assert("baseline: from_game_state non-null", gd_map_view != null):
|
||||
return
|
||||
|
||||
var gd_trade: RefCounted = ClassDB.instantiate("GdTrade") as RefCounted
|
||||
if not _assert("baseline: GdTrade instantiates", gd_trade != null):
|
||||
return
|
||||
|
||||
var ledger_inst: RefCounted = ClassDB.instantiate("GdTradeLedger") as RefCounted
|
||||
var agreement_json: String = JSON.stringify({
|
||||
"agreements": [
|
||||
{
|
||||
"SharedMap": {
|
||||
"agreement_id": 0,
|
||||
"partners": [0, 1],
|
||||
"turn_started": 1,
|
||||
"duration": 8,
|
||||
"share_turns_remaining": 0,
|
||||
"payment_gold": 200,
|
||||
"payment_luxury": null,
|
||||
"courier_route": {
|
||||
"sender": 0,
|
||||
"receiver": 1,
|
||||
"courier_era_tier": 2,
|
||||
"dispatched_turn": 1,
|
||||
"position": [0, 0],
|
||||
"eta_turn": null,
|
||||
"delivered": false,
|
||||
"intercepted": false,
|
||||
"planned_path": [],
|
||||
"path_step": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"next_agreement_id": 1
|
||||
})
|
||||
var ledger: RefCounted = ledger_inst.call("from_json", agreement_json) as RefCounted
|
||||
if not _assert("baseline: ledger loads", ledger != null):
|
||||
return
|
||||
|
||||
var events: Array = gd_trade.call("step_shared_map_agreements", ledger, gd_map_view, 1) as Array
|
||||
var delivered_step1: bool = false
|
||||
for ev: Dictionary in events:
|
||||
if ev.get("type") == "shared_map_delivered":
|
||||
delivered_step1 = true
|
||||
break
|
||||
_assert("baseline: era_2 does NOT deliver on step 1 over 9-hex gap", not delivered_step1)
|
||||
|
||||
|
||||
func _test_courier_at_destination_delivers_immediately() -> void:
|
||||
# Courier already at position == receiver capital → delivers on next step.
|
||||
var state: RefCounted = ClassDB.instantiate("GdGameState") as RefCounted
|
||||
if not _assert("instant: GdGameState instantiates", state != null):
|
||||
return
|
||||
state.call("create_grid", 15, 10)
|
||||
state.call("add_player_militarist", 0, 0)
|
||||
state.call("add_player_militarist", 9, 0)
|
||||
|
||||
var map_view_inst: RefCounted = ClassDB.instantiate("GdCourierMapView") as RefCounted
|
||||
var gd_map_view: RefCounted = map_view_inst.call("from_game_state", state) as RefCounted
|
||||
if not _assert("instant: GdCourierMapView created", gd_map_view != null):
|
||||
return
|
||||
|
||||
var gd_trade: RefCounted = ClassDB.instantiate("GdTrade") as RefCounted
|
||||
if not _assert("instant: GdTrade instantiates", gd_trade != null):
|
||||
return
|
||||
|
||||
# Courier position (9,0) == receiver capital; not yet delivered.
|
||||
var ledger_inst: RefCounted = ClassDB.instantiate("GdTradeLedger") as RefCounted
|
||||
var agreement_json: String = JSON.stringify({
|
||||
"agreements": [
|
||||
{
|
||||
"SharedMap": {
|
||||
"agreement_id": 0,
|
||||
"partners": [0, 1],
|
||||
"turn_started": 1,
|
||||
"duration": 8,
|
||||
"share_turns_remaining": 0,
|
||||
"payment_gold": 200,
|
||||
"payment_luxury": null,
|
||||
"courier_route": {
|
||||
"sender": 0,
|
||||
"receiver": 1,
|
||||
"courier_era_tier": 2,
|
||||
"dispatched_turn": 1,
|
||||
"position": [9, 0],
|
||||
"eta_turn": null,
|
||||
"delivered": false,
|
||||
"intercepted": false,
|
||||
"planned_path": [],
|
||||
"path_step": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"next_agreement_id": 1
|
||||
})
|
||||
var ledger: RefCounted = ledger_inst.call("from_json", agreement_json) as RefCounted
|
||||
if not _assert("instant: ledger loads", ledger != null):
|
||||
return
|
||||
|
||||
var events: Array = gd_trade.call("step_shared_map_agreements", ledger, gd_map_view, 1) as Array
|
||||
var delivered_found: bool = false
|
||||
var turns_remaining_from_event: int = -1
|
||||
for ev: Dictionary in events:
|
||||
if ev.get("type") == "shared_map_delivered" and ev.get("agreement_id") as int == 0:
|
||||
delivered_found = true
|
||||
turns_remaining_from_event = ev.get("turns_remaining") as int
|
||||
break
|
||||
|
||||
_assert("instant: SharedMapDelivered when courier starts at destination", delivered_found)
|
||||
_assert("instant: turns_remaining in event equals duration (8)", turns_remaining_from_event == 8)
|
||||
|
||||
var sm_agreements: Array = ledger.call("iter_shared_map") as Array
|
||||
_assert("instant: ledger retains agreement in share-window phase", sm_agreements.size() == 1)
|
||||
if sm_agreements.size() > 0:
|
||||
var sm_out: RefCounted = sm_agreements[0] as RefCounted
|
||||
_assert("instant: agreement is_delivered after step", sm_out.call("is_delivered") as bool)
|
||||
var share_remaining: int = sm_out.call("get_share_turns_remaining") as int
|
||||
_assert("instant: share window = 8 turns", share_remaining == 8)
|
||||
|
||||
|
||||
func _assert(label: String, condition: bool) -> bool:
|
||||
if condition:
|
||||
_pass_count += 1
|
||||
print("[PASS] %s" % label)
|
||||
else:
|
||||
_fail_count += 1
|
||||
push_error("[FAIL] %s" % label)
|
||||
return condition
|
||||
|
||||
|
||||
func _print_results() -> void:
|
||||
print("courier_era10_instant_sync_proof: %d passed, %d failed" % [_pass_count, _fail_count])
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://courier_era10_instant_sync"]
|
||||
|
||||
[ext_resource type="Script" path="res://engine/scenes/tests/courier_era10_instant_sync_proof.gd" id="1"]
|
||||
|
||||
[node name="CourierEra10InstantSyncProof" type="Node"]
|
||||
script = ExtResource("1")
|
||||
Loading…
Add table
Reference in a new issue