151 lines
8.2 KiB
GDScript
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)
|