diff --git a/hwe/compare/turn_command_trace.php b/hwe/compare/turn_command_trace.php index f71dd6c6..0f994a73 100644 --- a/hwe/compare/turn_command_trace.php +++ b/hwe/compare/turn_command_trace.php @@ -71,6 +71,159 @@ final class TurnComparisonTracingRNG implements RNG } } +/** @return array */ +function comparisonMappedPatch(array $row, array $mapping): array +{ + $patch = []; + foreach ($mapping as $canonical => $legacy) { + if (array_key_exists($canonical, $row)) { + $patch[$legacy] = $row[$canonical]; + } + } + return $patch; +} + +function comparisonApplyTurnFixtureSetup(mixed $setup): void +{ + if ($setup === null) { + return; + } + if (!is_array($setup)) { + throw new \InvalidArgumentException('setup must be an object'); + } + $db = DB::db(); + $world = $setup['world'] ?? null; + if ($world !== null) { + if (!is_array($world)) { + throw new \InvalidArgumentException('setup.world must be an object'); + } + $game = KVStorage::getStorage($db, 'game_env'); + foreach (['year', 'month', 'startyear'] as $key) { + $fixtureKey = $key === 'startyear' ? 'startYear' : $key; + if (!array_key_exists($fixtureKey, $world)) { + continue; + } + $value = $world[$fixtureKey]; + if (!is_int($value) || $value < 1 || ($key === 'month' && $value > 12)) { + throw new \InvalidArgumentException("setup.world.{$fixtureKey} is invalid"); + } + $game->setValue($key, $value); + } + $game->resetCache(); + } + + foreach (['nations', 'cities', 'generals', 'diplomacy'] as $collection) { + if (isset($setup[$collection]) && !is_array($setup[$collection])) { + throw new \InvalidArgumentException("setup.{$collection} must be an array"); + } + } + + foreach ($setup['nations'] ?? [] as $row) { + if (!is_array($row) || !is_int($row['id'] ?? null) || $row['id'] < 1) { + throw new \InvalidArgumentException('setup.nations requires positive integer ids'); + } + $data = comparisonMappedPatch($row, [ + 'id' => 'nation', + 'name' => 'name', + 'color' => 'color', + 'capitalCityId' => 'capital', + 'gold' => 'gold', + 'rice' => 'rice', + 'tech' => 'tech', + 'level' => 'level', + 'typeCode' => 'type', + 'war' => 'war', + 'generalCount' => 'gennum', + 'power' => 'power', + ]); + if (isset($row['meta'])) { + $data['aux'] = Json::encode($row['meta']); + } + $data += [ + 'name' => "fixture-nation-{$row['id']}", + 'color' => '#777777', + 'capital' => 0, + 'gold' => 0, + 'rice' => 0, + 'level' => 1, + 'type' => 'che_중립', + 'aux' => '{}', + ]; + $db->insertUpdate('nation', $data, $data); + } + + foreach ($setup['cities'] ?? [] as $row) { + if (!is_array($row) || !is_int($row['id'] ?? null) || $row['id'] < 1) { + throw new \InvalidArgumentException('setup.cities requires positive integer ids'); + } + $patch = comparisonMappedPatch($row, [ + 'nationId' => 'nation', + 'population' => 'pop', + 'agriculture' => 'agri', + 'commerce' => 'comm', + 'security' => 'secu', + 'supplyState' => 'supply', + 'frontState' => 'front', + 'defence' => 'def', + 'wall' => 'wall', + 'state' => 'state', + 'term' => 'term', + 'trust' => 'trust', + 'trade' => 'trade', + ]); + if ($patch !== []) { + $db->update('city', $patch, 'city = %i', $row['id']); + } + } + + foreach ($setup['generals'] ?? [] as $row) { + if (!is_array($row) || !is_int($row['id'] ?? null) || $row['id'] < 1) { + throw new \InvalidArgumentException('setup.generals requires positive integer ids'); + } + $patch = comparisonMappedPatch($row, [ + 'nationId' => 'nation', + 'cityId' => 'city', + 'troopId' => 'troop', + 'leadership' => 'leadership', + 'strength' => 'strength', + 'intelligence' => 'intel', + 'officerLevel' => 'officer_level', + 'gold' => 'gold', + 'rice' => 'rice', + 'crew' => 'crew', + 'crewTypeId' => 'crewtype', + 'train' => 'train', + 'atmos' => 'atmos', + 'killTurn' => 'killturn', + 'npcState' => 'npc', + ]); + if ($patch !== []) { + $db->update('general', $patch, 'no = %i', $row['id']); + } + } + + foreach ($setup['diplomacy'] ?? [] as $row) { + if ( + !is_array($row) || + !is_int($row['fromNationId'] ?? null) || + !is_int($row['toNationId'] ?? null) || + $row['fromNationId'] < 1 || + $row['toNationId'] < 1 + ) { + throw new \InvalidArgumentException('setup.diplomacy requires positive integer nation ids'); + } + $data = comparisonMappedPatch($row, [ + 'fromNationId' => 'me', + 'toNationId' => 'you', + 'state' => 'state', + 'term' => 'term', + 'dead' => 'dead', + ]); + $data += ['state' => 3, 'term' => 0, 'dead' => 0]; + $db->insertUpdate('diplomacy', $data, $data); + } +} + function comparisonRunTurnCommand(array $request): array { if (getenv('TURN_DIFFERENTIAL_ENABLED') !== '1') { @@ -90,6 +243,7 @@ function comparisonRunTurnCommand(array $request): array throw new \InvalidArgumentException('action must be a non-empty string'); } + comparisonApplyTurnFixtureSetup($request['setup'] ?? null); $snapshotRequest = ['observe' => $request['observe'] ?? []]; $before = comparisonTurnStateSnapshot($snapshotRequest); $db = DB::db();