P05-02: 全費目と資金区分:表金・裏金、維持費未払い、仕入れ、人件費、元本、利息、税の帳簿への接続(原 P05-02 の完了の条件)
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
extends RefCounted
|
||||
|
||||
## P05-02 F6: the fixture and day script shared by the restart writer
|
||||
## (tests/economy/test_p05_funds_restart_1_write.gd) and reader
|
||||
## (tests/economy/test_p05_funds_restart_2_read.gd), which run as separate engine
|
||||
## processes. Same ledger fixture and accelerated boundary traversal as
|
||||
## tests/economy/test_p05_funds_runtime.gd.
|
||||
|
||||
const HANDOFF_PATH := "res://artifacts/p05-02/restart-handoff.json"
|
||||
const HANDOFF_SCHEMA := "p05-02-restart-handoff-v1"
|
||||
const WRITER_SOURCE := "res://tests/economy/test_p05_funds_restart_1_write.gd"
|
||||
const WRITER_SAVE := "user://p05_restart_write/minute_%d"
|
||||
const READER_SAVE := "user://p05_restart_read/minute_%d"
|
||||
const READER_TAMPERED_SAVE := "user://p05_restart_read/changed"
|
||||
const SEED := 5025
|
||||
# Mid-day 2, just before the day 2 00:00 close (which needs a hidden-fund top-up)
|
||||
# and just after it.
|
||||
const SAVE_MINUTES: Array[int] = [2400, 2850, 2880]
|
||||
const LAST_MINUTE := 4320
|
||||
|
||||
const FRONT := &"PLAYER_CASH"
|
||||
const BACK := &"BACK_CASH"
|
||||
const LOAN := &"LOAN_LIABILITY:p05.fixture"
|
||||
const FRONT_SALES := &"P05_FIXTURE_FRONT_SALES"
|
||||
const BACK_SALES := &"P05_FIXTURE_BACK_SALES"
|
||||
const PAYER := &"P05_FIXTURE_PAYER"
|
||||
|
||||
var _expect: Callable
|
||||
|
||||
|
||||
func _init(expect: Callable) -> void:
|
||||
_expect = expect
|
||||
|
||||
|
||||
func new_fixture() -> Dictionary:
|
||||
var clock: Variant = load("res://autoload/game_clock.gd").new()
|
||||
var state: Variant = load("res://autoload/game_state.gd").new()
|
||||
var runtime: Variant = load("res://autoload/simulation_runtime.gd").new()
|
||||
clock.reset(1, 6, 0)
|
||||
state.reset()
|
||||
_expect.call(bool(runtime.initialize(clock, state, SEED).get(&"valid", false)), "fixture Runtime initializes")
|
||||
clock.set_paused(true)
|
||||
runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
|
||||
_expect.call(bool(runtime.enable_production_operations().get(&"ok", false)), "fixture enables production Operations")
|
||||
for account: StringName in [FRONT_SALES, BACK_SALES, PAYER]:
|
||||
runtime.economy_service.register_account(account)
|
||||
runtime.economy_service.register_account(LOAN, true)
|
||||
_expect.call(runtime.economy_service.initialize_account(PAYER, 100000, clock.absolute_minute, &"p05.fixture.payer").is_success(), "fixture payer funded")
|
||||
return {&"clock": clock, &"state": state, &"runtime": runtime}
|
||||
|
||||
|
||||
func free_fixture(fixture: Dictionary) -> void:
|
||||
for key: StringName in [&"runtime", &"clock", &"state"]:
|
||||
if fixture.get(key) != null:
|
||||
fixture[key].free()
|
||||
|
||||
|
||||
## Applies the day script's steps after `after_minute` through `through_minute`
|
||||
## and calls `on_minute` after each step (the writer saves there).
|
||||
## Day 1: borrow 1000, hidden sale 700 (tax 70, interest 10). Day 2: a cost leaves
|
||||
## 5 front funds for the 10 interest, so hidden funds top up 5. Day 3: front sale
|
||||
## 50 (tax 5, interest 10). Final: front 35, hidden 695.
|
||||
func play(fixture: Dictionary, after_minute: int, through_minute: int, on_minute: Callable) -> void:
|
||||
var steps: Array = [
|
||||
[420, func() -> void: _move(fixture, LOAN, FRONT, 1000, &"LOAN_PRINCIPAL_BORROWED", "borrow")],
|
||||
[450, func() -> void: _sale(fixture, BACK_SALES, BACK, 700, "d1.back")],
|
||||
[1440, Callable()],
|
||||
[2040, func() -> void: _move(fixture, FRONT, &"OPERATING_EXPENSE", 5915, &"OPERATING_EXPENSE", "d2.cost")],
|
||||
[2400, Callable()],
|
||||
[2850, Callable()],
|
||||
[2880, Callable()],
|
||||
[3510, func() -> void: _sale(fixture, FRONT_SALES, FRONT, 50, "d3.front")],
|
||||
[4320, Callable()],
|
||||
]
|
||||
for step: Array in steps:
|
||||
var minute := int(step[0])
|
||||
if minute <= after_minute or minute > through_minute:
|
||||
continue
|
||||
_advance_to(fixture, minute)
|
||||
if (step[1] as Callable).is_valid():
|
||||
(step[1] as Callable).call()
|
||||
if on_minute.is_valid():
|
||||
on_minute.call(minute)
|
||||
|
||||
|
||||
func economy_bytes(runtime: Variant) -> PackedByteArray:
|
||||
return var_to_bytes(runtime.create_snapshot()[&"systems"][&"runtime"][&"economy"])
|
||||
|
||||
|
||||
## Operations history in the transport-safe form the Runtime's own restore
|
||||
## crosslink compares (StringName ids become String values in saves).
|
||||
func days_value(runtime: Variant) -> Variant:
|
||||
return runtime.call("_operations_snapshot_comparison_value", runtime.operations_coordinator.get_day_summaries())
|
||||
|
||||
|
||||
func days_equal(runtime: Variant, expected: Variant) -> bool:
|
||||
return runtime.call("_strict_variants_equal", expected, days_value(runtime))
|
||||
|
||||
|
||||
func category_count(runtime: Variant, categories: Array) -> int:
|
||||
var count := 0
|
||||
for entry: LedgerEntry in runtime.economy_service.get_entries():
|
||||
if entry.category in categories:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
static func sha256(bytes: PackedByteArray) -> String:
|
||||
var context := HashingContext.new()
|
||||
context.start(HashingContext.HASH_SHA256)
|
||||
if not bytes.is_empty():
|
||||
context.update(bytes)
|
||||
return context.finish().hex_encode()
|
||||
|
||||
|
||||
# Every Operations boundary is on a 30-minute mark; each step ends on one.
|
||||
func _advance_to(fixture: Dictionary, minute: int) -> void:
|
||||
var clock: Variant = fixture[&"clock"]
|
||||
var runtime: Variant = fixture[&"runtime"]
|
||||
while int(clock.absolute_minute) < minute:
|
||||
var step := mini(30 - int(clock.absolute_minute) % 30, minute - int(clock.absolute_minute))
|
||||
var target: int = clock.absolute_minute + step
|
||||
clock.absolute_minute = target
|
||||
clock.call("_sync_calendar_fields")
|
||||
clock.snapshot_version += step
|
||||
runtime.call("_advance_weather", target)
|
||||
runtime.call("_advance_facility_upkeep", target)
|
||||
runtime.call("_advance_operations_time", target)
|
||||
_align_clocks(runtime, target)
|
||||
|
||||
|
||||
# The fixture owns no actors, cars or rides, so only the empty aggregates' clocks
|
||||
# follow the minute.
|
||||
func _align_clocks(runtime: Variant, minute: int) -> void:
|
||||
for service: Variant in [runtime.inventory_service, runtime.facility_service, runtime.kitchen_production,
|
||||
runtime.guest_service, runtime.staff_service, runtime.authority_service, runtime.defense_service,
|
||||
runtime.construction_service, runtime.excavation_service]:
|
||||
service.current_minute = minute
|
||||
var transport_snapshot: Dictionary = runtime.elevators.create_snapshot()
|
||||
if not (transport_snapshot[&"shafts"] as Array).is_empty() or not (transport_snapshot[&"trips"] as Array).is_empty():
|
||||
_expect.call(false, "accelerated fixture cannot skip live elevator movement")
|
||||
return
|
||||
transport_snapshot[&"current_minute"] = minute
|
||||
if not runtime.elevators.restore_snapshot(transport_snapshot):
|
||||
_expect.call(false, "empty transport clock aligns through validated snapshot restore")
|
||||
|
||||
|
||||
func _sale(fixture: Dictionary, revenue_account: StringName, fund: StringName, amount: int, key: String) -> void:
|
||||
var runtime: Variant = fixture[&"runtime"]
|
||||
var minute: int = fixture[&"clock"].absolute_minute
|
||||
var entries: Array = [
|
||||
LedgerEntry.new().configure(&"", minute, revenue_account, PAYER, amount, &"SERVICE_SALE", &"p05.fixture"),
|
||||
LedgerEntry.new().configure(&"", minute, fund, revenue_account, amount, &"SERVICE_CASH_SWEEP", &"p05.fixture"),
|
||||
]
|
||||
_expect.call(runtime.economy_service.post(entries, StringName("p05.sale." + key)).is_success(), "fixture sale " + key)
|
||||
|
||||
|
||||
func _move(fixture: Dictionary, from: StringName, to: StringName, amount: int, category: StringName, key: String) -> void:
|
||||
var runtime: Variant = fixture[&"runtime"]
|
||||
_expect.call(runtime.economy_service.transfer(from, to, amount, fixture[&"clock"].absolute_minute, category,
|
||||
&"p05.fixture", StringName("p05.move." + key)).is_success(), "fixture move " + key)
|
||||
@@ -0,0 +1 @@
|
||||
uid://m3lik2gqaho2
|
||||
Reference in New Issue
Block a user