extends SceneTree ## P14-03 route choice with real waiting: two guest shafts, real guests at real ## cells, A* switching shafts under a whole-shaft rush, shared cabins within ## capacity, recovery after cancellation, exact save of wait costs, route loss ## on demolition, and congestion priced at a warp exit. var _checks := 0 var _failures := 0 var _serial := 0 var _clock: Variant var _state: Variant var _runtime: Variant const START := Vector3i(1, 0, 0) const GOAL := Vector3i(2, 1, 0) func _initialize() -> void: call_deferred("_run") func _run() -> void: print("Route wait choice (P14-03)") _clock = load("res://autoload/game_clock.gd").new() _state = load("res://autoload/game_state.gd").new() _runtime = load("res://autoload/simulation_runtime.gd").new() _clock.reset(1, 6, 0) _state.reset() _runtime.initialize(_clock, _state, 1403) _clock.set_paused(true) _layout() _test_shaft_rush() _test_shared_cabin() _test_route_loss() _test_warp_exit_congestion() _runtime.free(); _clock.free(); _state.free() print("%s: %d checks" % ["PASS" if _failures == 0 else "FAIL", _checks]) quit(_failures) func _cmd(kind: StringName, payload: Dictionary) -> CommandResult: _serial += 1 return _runtime.submit_command(kind, payload, &"player", &"", 0, _clock.absolute_minute, StringName("p1403.%d" % _serial)) func _build(id: StringName, origin: Vector3i) -> void: var result := _cmd(&"build_facility", {&"definition_id": id, &"origin": origin}) _expect(result.is_success(), "build %s at %s %s" % [id, origin, result.message]) func _next_evaluation() -> void: _clock.advance_minutes(10 - int(_clock.absolute_minute) % 10 + 1) func _layout() -> void: _expect(_cmd(&"change_resources", {&"deltas": {&"gold": 30000, &"materials": 300}}).is_success(), "fund the fixture") # Shaft A at x0 (floors 0-3), shaft B at x4 (floors 0-1), joined on floor 1. _build(&"guest_elevator", Vector3i(0, 0, 0)) _build(&"guest_elevator", Vector3i(4, 0, 0)) _build(&"mana_warp", Vector3i(-4, 0, 0)) _clock.advance_minutes(360) _build(&"guest_elevator", Vector3i(0, 2, 0)) _build(&"mana_warp", Vector3i(-4, 2, 0)) for x: int in [2, 3]: _build(&"guest_corridor", Vector3i(x, 1, 0)) _clock.advance_minutes(360) _next_evaluation() _expect(_runtime.elevators.get_report()[&"shafts"].size() == 2, "two independent shafts") func _spawn(guest_id: StringName, cell: Vector3i) -> bool: var result := _cmd(&"spawn_guest", {&"guest_id": guest_id, &"guest_type_id": &"traveler", &"options": {&"current_cell": cell}}) _expect(result.is_success(), "spawn real guest %s at %s %s" % [guest_id, cell, result.message]) return result.is_success() func _route() -> Array: return _runtime.path_service.find_path(START, GOAL) func _uses_shaft_a(path: Array) -> bool: return path.size() > 1 and path[1] == Vector3i(0, 0, 0) func _test_shaft_rush() -> void: var idle := _route() var idle_cost: int = _runtime.path_service.last_path_cost _expect(_uses_shaft_a(idle), "idle tower routes through the nearer shaft A") # Twelve real guests on shaft A's ground stop all wait for floor 3, a # different floor pair from the queried 0->1 trip. var riders: Array[StringName] = [] for i: int in 12: var guest_id := StringName("rush_%02d" % i) if _spawn(guest_id, Vector3i(0, 0, 0)): var trip: Dictionary = _runtime.elevators.request_trip(StringName("guest:%s" % guest_id), Vector3i(0, 0, 0), Vector3i(0, 3, 0), _clock.absolute_minute) _expect(bool(trip.get(&"ok", false)), "real guest %s queues for floor 3" % guest_id) riders.append(StringName("guest:%s" % guest_id)) var rush := _route() var rush_cost: int = _runtime.path_service.last_path_cost _expect(not _uses_shaft_a(rush) and rush.has(Vector3i(4, 0, 0)), "whole-shaft rush diverts the 0->1 trip to idle shaft B") _expect(_runtime.path_service.last_path_cost > idle_cost, "diverted route is dearer than the idle one") var a_connector: StringName = _runtime.path_service.get_step_connector(Vector3i(0, 0, 0), Vector3i(0, 1, 0), []) _expect(int(_runtime.path_service.get_connector(a_connector)[&"dynamic_cost"]) == 12 * PathService.ORTHOGONAL_MOVE_COST, "shaft A 0->1 prices all 12 riders waiting elsewhere in the shaft") var saved: Dictionary = _runtime.create_snapshot() var restored_ok: bool = _runtime.restore_snapshot(saved.duplicate(true)) var again: Dictionary = _runtime.create_snapshot() if restored_ok and again != saved: for system: Variant in saved[&"systems"][&"runtime"]: if saved[&"systems"][&"runtime"][system] != again[&"systems"][&"runtime"].get(system): print("DIFF runtime.", system) for key: Variant in saved: if key != &"systems" and saved[key] != again.get(key): print("DIFF ", key) _expect(restored_ok and again == saved, "rush wait costs and queues survive save restore exactly (restored=%s)" % restored_ok) _expect(not _uses_shaft_a(_route()), "restored rush still diverts") var tampered: Dictionary = saved.duplicate(true) for row: Dictionary in tampered[&"systems"][&"runtime"][&"path"][&"connectors"]: if row[&"id"] == a_connector: row[&"dynamic_cost"] = 0 var live: Dictionary = _runtime.create_snapshot() _expect(not _runtime.restore_snapshot(tampered) and _runtime.create_snapshot() == live, "a save whose shaft wait disagrees with its queue is rejected atomically") var a_route_during_rush: int = _runtime.path_service.calculate_path_cost(idle) for actor_id: StringName in riders: _runtime.elevators.cancel_actor(actor_id) _expect(int(_runtime.path_service.get_connector(a_connector)[&"dynamic_cost"]) == 0, "cancelled rush clears the shaft wait") var a_route_after: int = _runtime.path_service.calculate_path_cost(idle) _expect(a_route_after == a_route_during_rush - 12 * PathService.ORTHOGONAL_MOVE_COST, "cleared shaft wait removes exactly the queued riders' cost from shaft A's route") _expect(rush_cost > 0, "rush route cost recorded") func _test_shared_cabin() -> void: var before: Dictionary = _runtime.create_snapshot() var riders: Array[StringName] = [] for i: int in 6: var guest_id := StringName("share_%02d" % i) if _spawn(guest_id, Vector3i(4, 0, 0)): var actor_id := StringName("guest:%s" % guest_id) _expect(bool(_runtime.elevators.request_trip(actor_id, Vector3i(4, 0, 0), Vector3i(4, 1, 0), _clock.absolute_minute).get(&"ok", false)), "rider %s queues on shaft B" % guest_id) riders.append(actor_id) var max_boarded := 0 var arrived := {} for minute: int in 60: _clock.advance_minutes(1) var boarded := 0 for trip: Dictionary in _runtime.elevators.get_report()[&"trips"]: if trip[&"shaft_id"] != _shaft_b() or trip[&"state"] != PathService.ELEVATOR_REQUEST_BOARDED: continue boarded += 1 max_boarded = maxi(max_boarded, boarded) for actor_id: StringName in riders: if _runtime.elevators.get_actor_trip(actor_id).is_empty(): arrived[actor_id] = true _expect(max_boarded > 1 and max_boarded <= 4, "riders share the cabin up to its capacity of 4 (max %d)" % max_boarded) _expect(arrived.size() == riders.size(), "all six riders are carried (none lost)") _expect(_runtime.restore_snapshot(before), "rewind shared-cabin drill") func _shaft_b() -> StringName: for shaft: Dictionary in _runtime.elevators.get_report()[&"shafts"]: if (shaft[&"stops"] as Array).front().x == 4: return shaft[&"shaft_id"] return &"" func _test_route_loss() -> void: var before: Dictionary = _runtime.create_snapshot() var shaft_b_id: StringName = &"" for facility: Dictionary in _runtime.construction_service.get_facilities(): if facility[&"definition_id"] == &"guest_elevator" and facility[&"origin"] == Vector3i(4, 0, 0): shaft_b_id = facility[&"instance_id"] _expect(_cmd(&"demolish_facility", {&"instance_id": shaft_b_id}).is_success(), "demolish idle shaft B") _expect(_uses_shaft_a(_route()), "route loss falls back to the remaining shaft") _expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "post-loss state restores") _expect(_runtime.restore_snapshot(before), "rewind route-loss drill") func _test_warp_exit_congestion() -> void: var ps: PathService = _runtime.path_service var lower := Vector3i(-4, 0, 0) var upper := Vector3i(-4, 2, 0) var quiet: Array = ps.find_path(lower, upper) var quiet_cost: int = ps.last_path_cost _expect(quiet == [lower, upper], "idle warp is the cheapest way up") var crowd := 0 for i: int in 6: if _spawn(StringName("crowd_%02d" % i), upper): crowd += 1 ps.find_path(lower, upper) _expect(crowd > 0 and ps.last_path_cost > quiet_cost, "a crowded warp exit raises the warp route's A* cost") func _expect(condition: bool, label: String) -> void: _checks += 1 if condition: print(" ✓ %s" % label) else: _failures += 1 push_error(" ✗ %s" % label)