Files
aetherbound-guild/runtime/tests/p9_8/five_profile_contract_test.gd
T

151 lines
8.2 KiB
GDScript

extends SceneTree
const Fixture = preload("res://tests/p9_8/runtime_fixture.gd")
const RoundService = preload("res://p9_8/round_service.gd")
const BattleService = preload("res://p9_8/battle_service.gd")
const BossService = preload("res://p9_8/boss_service.gd")
const BossBattleService = preload("res://p9_8/boss_battle_service.gd")
const SaveService = preload("res://p9_8/save_service.gd")
const PROFILES := [
{"id": "normal", "artifact": "AR-001", "promotion": 0, "risks": [0, 1, 0, 1, 0], "plan": "brace", "gap": 12},
{"id": "active", "artifact": "AR-002", "promotion": 1, "risks": [1, 0, 1, 0, 1], "plan": "rotate", "gap": 7},
{"id": "efficient", "artifact": "AR-001", "promotion": 1, "risks": [0, 0, 0, 0, 0], "plan": "rotate", "gap": 5},
{"id": "adversarial", "artifact": "AR-002", "promotion": 0, "risks": [0, 1, 0, 1, 0], "plan": "brace", "gap": 18, "defeat": true},
{"id": "returning", "artifact": "AR-001", "promotion": 0, "risks": [1, 0, 1, 0, 1], "plan": "rotate", "gap": 15, "interrupt": true},
]
var failures: Array[String] = []
var timestamps := 0
var longest_gap := 0
var defeat_recoveries := 0
var interruption_reloads := 0
var save_root := ""
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
save_root = OS.get_environment("ABG_P9_8_PROFILE_SAVE_ROOT")
if save_root.is_empty():
save_root = "/private/tmp/abg-p98-profile-save-default"
DirAccess.make_dir_recursive_absolute(save_root)
for profile in PROFILES:
run_profile(profile)
if failures.is_empty():
print("ABG_P9_8_FIVE_PROFILE_CONTRACT_OK profiles=normal,active,efficient,adversarial,returning timestamps=%d longest_gap=%d artifact=AR-001,AR-002 promotion=both_branches routes=risk0,1 defeat_recoveries=%d interruption_reloads=%d boss=rotate,brace close=5/5 offline_rewards=0 starvation=0 invalid_mutations=0" % [timestamps, longest_gap, defeat_recoveries, interruption_reloads])
quit(0)
else:
for failure in failures:
push_error(failure)
quit(1)
func run_profile(profile: Dictionary) -> void:
var service = RoundService.new(Fixture.market8())
var clock := 0
clock = decision(profile, clock, "artifact")
expect(service.choose_artifact("%s-artifact" % profile.id, String(profile.artifact)).ok, "%s Artifact commits" % profile.id)
if not service.state.promotion_draft.is_empty():
clock = decision(profile, clock, "promotion")
var recruit_id := String(service.state.promotion_draft.recruit_id)
var promotion_id := String(service.state.promotion_draft.offer_ids[int(profile.promotion)])
expect(service.confirm_promotion("%s-promotion" % profile.id, recruit_id, promotion_id).ok, "%s promotion commits" % profile.id)
if bool(profile.get("defeat", false)):
clock = run_forced_defeat(service, profile, clock)
for offset in range(5):
var market_index := 8 + offset
if service.state.company.size() < 10:
clock = decision(profile, clock, "hire-%d" % market_index)
var recruit_id := String(service.state.available_recruit_offer_ids[0])
service.focus_recruit_offer(recruit_id)
expect(service.hire("%s-hire-%d" % [profile.id, market_index], recruit_id).ok, "%s Market %d hire" % [profile.id, market_index])
clock = decision(profile, clock, "line-%d" % market_index)
expect(service.commit_deployment("%s-line-%d" % [profile.id, market_index], service.auto_order_candidate()).ok, "%s Market %d line" % [profile.id, market_index])
var route_id := "P98-R%d-Q%d" % [market_index, int(profile.risks[offset])]
clock = decision(profile, clock, "route-%d" % market_index)
service.focus_route(route_id)
expect(service.commit_route("%s-route-%d" % [profile.id, market_index], route_id).ok, "%s Market %d route" % [profile.id, market_index])
if bool(profile.get("interrupt", false)) and market_index == 10:
var saves = SaveService.new()
saves.save_path = save_root.path_join("%s-interrupt.json" % String(profile.id))
saves.clear()
expect(saves.save_snapshot(service.state.snapshot(), 8000).ok, "returning interruption saves")
var loaded: Dictionary = saves.load_snapshot(8012)
expect(loaded.ok and loaded.battle_interrupted and int(loaded.offline_coin) == 0, "returning interruption restores the pre-battle Market with zero offline gain")
service = RoundService.new()
expect(service.state.restore_snapshot(loaded.snapshot), "returning interruption snapshot restores")
service.focus_route(route_id)
expect(service.commit_route("%s-route-%d-retry" % [profile.id, market_index], route_id).ok, "returning recommits the same deterministic route")
interruption_reloads += 1
var battle = BattleService.new()
expect(battle.start_from_market(service.state).ok, "%s Round %d starts" % [profile.id, market_index])
var guard := 0
while not battle.state.terminal and guard < 2000:
battle.advance_ticks(1)
guard += 1
expect(guard < 2000 and battle.state.outcome == "victory", "%s Round %d wins" % [profile.id, market_index])
expect(service.settle_battle("%s-settle-%d" % [profile.id, market_index], battle.state.snapshot()).ok, "%s Round %d settles" % [profile.id, market_index])
if service.state.recovery_required:
clock = decision(profile, clock, "recover-%d" % market_index)
expect(service.recover_injured_company("%s-recover-%d" % [profile.id, market_index]).ok, "%s Round %d recovery" % [profile.id, market_index])
if not service.state.promotion_draft.is_empty():
var recruit_id := String(service.state.promotion_draft.recruit_id)
var promotion_id := String(service.state.promotion_draft.offer_ids[int(profile.promotion)])
expect(service.confirm_promotion("%s-late-promotion" % profile.id, recruit_id, promotion_id).ok, "%s late promotion" % profile.id)
var boss = BossService.new()
expect(boss.state.restore_snapshot(service.state.snapshot()), "%s Boss Muster restores" % profile.id)
var full_line: Array[String] = []
for recruit_id in boss.state.company.slice(0, mini(boss.state.line_capacity, boss.state.company.size())):
full_line.append(String(recruit_id))
clock = decision(profile, clock, "boss-line")
expect(boss.commit_deployment("%s-boss-line" % profile.id, full_line).ok, "%s Boss line commits" % profile.id)
clock = decision(profile, clock, "boss-plan")
expect(boss.commit_boss_plan("%s-boss-plan" % profile.id, String(profile.plan)).ok, "%s Boss plan commits" % profile.id)
var boss_battle = BossBattleService.new()
expect(boss_battle.start_from_contract(boss.state).ok, "%s Boss starts" % profile.id)
var boss_guard := 0
while not boss_battle.state.terminal and boss_guard < 1800:
boss_battle.advance_ticks(1)
boss_guard += 1
expect(boss_guard < 1800 and boss_battle.state.outcome == "victory", "%s Boss plan wins" % profile.id)
expect(boss.settle_boss("%s-boss-settle" % profile.id, boss_battle.state.snapshot()).ok, "%s Boss settles" % profile.id)
clock = decision(profile, clock, "contract-close")
expect(boss.close_contract("%s-close" % profile.id).ok, "%s Contract closes" % profile.id)
expect(boss.state.run_closed and boss.state.persistent_renown == 3 and boss.state.company_records.size() == 1, "%s persistent result is exact" % profile.id)
func run_forced_defeat(service, profile: Dictionary, clock: int) -> int:
var fragile_id := "Farstring" if "Farstring" in service.state.company else String(service.state.company[-1])
var line: Array[String] = [fragile_id]
clock = decision(profile, clock, "fragile-line")
service.commit_deployment("adversarial-fragile-line", line)
service.focus_route("P98-R8-Q2")
service.commit_route("adversarial-defeat-route", "P98-R8-Q2")
var battle = BattleService.new()
battle.start_from_market(service.state)
var guard := 0
while not battle.state.terminal and guard < 2000:
battle.advance_ticks(1)
guard += 1
expect(battle.state.outcome == "defeat", "adversarial fragile Company loses the elite route")
service.settle_battle("adversarial-defeat-settle", battle.state.snapshot())
clock = decision(profile, clock, "defeat-recovery")
expect(service.recover_injured_company("adversarial-defeat-recover").ok, "adversarial recovers once")
defeat_recoveries += 1
return clock
func decision(profile: Dictionary, clock: int, _label: String) -> int:
var gap := int(profile.gap)
longest_gap = maxi(longest_gap, gap)
timestamps += 1
return clock + gap
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append("P9.8 profiles: %s" % message)