From a0b6a48a518a4ac92a4205fc11a24d0bcffa0f1c Mon Sep 17 00:00:00 2001 From: hided62 Date: Mon, 24 Aug 2026 03:35:39 +0000 Subject: [PATCH] =?UTF-8?q?test:=20=EC=A0=84=ED=88=AC=20=EC=B0=A8=EB=93=B1?= =?UTF-8?q?=20=EA=B3=84=EC=B8=A1=20=EB=B2=94=EC=9C=84=EB=A5=BC=20=EC=97=84?= =?UTF-8?q?=EA=B2=A9=ED=95=98=EA=B2=8C=20=EB=B3=B4=EA=B0=95=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixture 시각을 DB 변경 없이 고정하고 활성 스킬과 fixture identity를 노출해 Core의 전체 상태·RNG·로그 비교가 첫 차이에서 실패하도록 한다. --- hwe/compare/battle_trace.php | 36 +++++++++++++++++++++++++++++++++--- hwe/process_war.php | 6 ++++-- hwe/sammo/WarUnit.php | 18 ++++++++++++++++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/hwe/compare/battle_trace.php b/hwe/compare/battle_trace.php index adf322c6..ee9fb35b 100644 --- a/hwe/compare/battle_trace.php +++ b/hwe/compare/battle_trace.php @@ -151,12 +151,37 @@ function comparisonBuildGeneral( ); } -function comparisonRunBattle(array $fixture): array +/** + * Some legacy battle items read game time through KVStorage instead of the + * General/WarUnit time arguments. Seed only the process-local cache so each + * comparison fixture observes its own clock without mutating the shared Ref DB. + */ +function comparisonOverrideGameTime(int $year, int $month, int $startYear): void +{ + $gameStorage = KVStorage::getStorage(DB::db(), 'game_env'); + $cacheProperty = new \ReflectionProperty(KVStorage::class, 'cacheData'); + $cacheProperty->setAccessible(true); + $cache = $cacheProperty->getValue($gameStorage); + if (!($cache instanceof Map)) { + $cache = new Map(); + } + $cache['year'] = $year; + $cache['month'] = $month; + $cache['startyear'] = $startYear; + $cacheProperty->setValue($gameStorage, $cache); +} + +function comparisonRunBattle(array $fixture, ?string $fixtureJson = null): array { $seed = (string)($fixture['seed'] ?? 'battle-differential'); + $identityJson = trim($fixtureJson ?? json_encode( + $fixture, + JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION, + )); $year = (int)$fixture['year']; $month = (int)$fixture['month']; $startYear = (int)($fixture['startYear'] ?? 180); + comparisonOverrideGameTime($year, $month, $startYear); $rawAttacker = $fixture['attackerGeneral']; $rawAttackerCity = $fixture['attackerCity']; $rawAttackerNation = $fixture['attackerNation']; @@ -257,6 +282,11 @@ function comparisonRunBattle(array $fixture): array return [ 'engine' => 'ref', 'seed' => $seed, + 'fixtureIdentity' => [ + 'schemaVersion' => 1, + 'seed' => $seed, + 'sha256' => hash('sha256', $identityJson), + ], 'conquered' => $conquered, 'attacker' => buildWarTraceUnitSnapshot($attacker), 'city' => buildWarTraceUnitSnapshot($city), @@ -286,7 +316,7 @@ if ($fixturePath === '--jsonl') { } $fixture = json_decode($line, true, flags: JSON_THROW_ON_ERROR); echo json_encode( - comparisonRunBattle($fixture), + comparisonRunBattle($fixture, $line), JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION, ), PHP_EOL; } @@ -305,7 +335,7 @@ try { $fixtureJson = $fixturePath === '-' ? stream_get_contents(STDIN) : file_get_contents($fixturePath); $fixture = json_decode((string)$fixtureJson, true, flags: JSON_THROW_ON_ERROR); echo json_encode( - comparisonRunBattle($fixture), + comparisonRunBattle($fixture, (string)$fixtureJson), JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION, ), PHP_EOL; } catch (\Throwable $throwable) { diff --git a/hwe/process_war.php b/hwe/process_war.php index b411afa6..daee97e8 100644 --- a/hwe/process_war.php +++ b/hwe/process_war.php @@ -661,12 +661,14 @@ function buildWarTraceUnitSnapshot(WarUnit $unit): array 'realPhase' => $unit->getRealPhase(), 'maxPhase' => $unit->getMaxPhase(), 'hp' => $unit->getHP(), - 'rawWarPower' => $unit->getRawWarPower(), + // Ref leaves warPower uninitialized until beginPhase(). Canonical + // traces use numeric zero for that not-yet-computed state, as Core does. + 'rawWarPower' => $unit->getRawWarPower() ?? 0, 'warPower' => $unit->getWarPower(), 'warPowerMultiplier' => $unit->getWarPowerMultiply(), 'killed' => $unit->getKilled(), 'dead' => $unit->getDead(), - 'activatedSkills' => $unit->getActivatedSkillLog(), + 'activatedSkills' => $unit->getActivatedSkillTraceSnapshot(), ]; if ($unit instanceof WarUnitGeneral) { diff --git a/hwe/sammo/WarUnit.php b/hwe/sammo/WarUnit.php index 7758b968..5141da11 100644 --- a/hwe/sammo/WarUnit.php +++ b/hwe/sammo/WarUnit.php @@ -120,6 +120,24 @@ class WarUnit return $this->logActivatedSkill; } + /** + * Comparison traces observe a phase before clearActivatedSkill() runs. + * Keep the product-facing cumulative accessor above unchanged, while + * exposing the canonical logged + currently-active view used by Core's + * WarBattleTraceUnitSnapshot. + */ + function getActivatedSkillTraceSnapshot(): array + { + $snapshot = $this->logActivatedSkill; + foreach ($this->activatedSkill as $skillName => $state) { + if (!$state) { + continue; + } + $snapshot[$skillName] = ($snapshot[$skillName] ?? 0) + 1; + } + return $snapshot; + } + function getRawNation(): array { return $this->rawNation;