diff --git a/hwe/compare/turn_command_trace.php b/hwe/compare/turn_command_trace.php index 3186270d..6e37cd69 100644 --- a/hwe/compare/turn_command_trace.php +++ b/hwe/compare/turn_command_trace.php @@ -133,6 +133,24 @@ function comparisonApplyTurnFixtureSetup(mixed $setup): void throw new \InvalidArgumentException("setup.{$collection} must be an array"); } } + $generalCooldowns = comparisonGeneralCooldownSelectors( + $setup['generalCooldowns'] ?? [], + 'setup.generalCooldowns', + ); + if ($generalCooldowns !== []) { + $nextExecuteStorage = KVStorage::getStorage($db, 'next_execute'); + foreach ($setup['generalCooldowns'] as $entry) { + $nextAvailableTurn = $entry['nextAvailableTurn'] ?? null; + if (!is_int($nextAvailableTurn) || $nextAvailableTurn < 0) { + throw new \InvalidArgumentException( + 'setup.generalCooldowns entries require a non-negative nextAvailableTurn', + ); + } + $key = "next_execute_{$entry['generalId']}_{$entry['actionName']}"; + $nextExecuteStorage->setValue($key, $nextAvailableTurn); + } + $nextExecuteStorage->resetCache(); + } if (isset($setup['randomFoundingCandidateCityIds'])) { $candidateCityIds = comparisonIntegerList( $setup['randomFoundingCandidateCityIds'], @@ -476,6 +494,7 @@ function comparisonRunTurnCommand(array $request): array $previousLastTurn = $general->getLastTurn(); $previousLastTurnRaw = $previousLastTurn->toRaw(); $command = buildGeneralCommandClass($action, $general, $environment, $args); + $commandFullConditionMet = $command->hasFullConditionMet(); $resultTurn = $turn->processCommand($rng, $command, false); } else { $nationStor = KVStorage::getStorage($db, $general->getNationID(), 'nation_env'); @@ -483,6 +502,7 @@ function comparisonRunTurnCommand(array $request): array $previousLastTurn = LastTurn::fromRaw($nationStor->getValue($lastNationTurnKey)); $previousLastTurnRaw = $previousLastTurn->toRaw(); $command = buildNationCommandClass($action, $general, $environment, $previousLastTurn, $args); + $commandFullConditionMet = $command->hasFullConditionMet(); $resultTurn = $turn->processNationCommand($rng, $command); $nationStor->setValue($lastNationTurnKey, $resultTurn->toRaw()); } @@ -509,7 +529,7 @@ function comparisonRunTurnCommand(array $request): array $general->getNationID(), ) === $args['nationName'] ); - $completed = ( + $completedByState = ( $action === 'che_접경귀환' && $tracingRng->calls !== [] ) || $acceptScoutCompleted || $foundNationCompleted || (($resultTurnRaw['command'] ?? null) === $command->getName() @@ -521,6 +541,7 @@ function comparisonRunTurnCommand(array $request): array && (int)($previousLastTurnRaw['term'] ?? 0) === $preReqTurn ) )); + $completed = $commandFullConditionMet && $completedByState; return [ 'schemaVersion' => 1, diff --git a/hwe/compare/turn_state_snapshot.php b/hwe/compare/turn_state_snapshot.php index 5e974d46..fcd9fc29 100644 --- a/hwe/compare/turn_state_snapshot.php +++ b/hwe/compare/turn_state_snapshot.php @@ -53,6 +53,36 @@ function comparisonOptionalCode(mixed $value): ?string return $value; } +/** @return list */ +function comparisonGeneralCooldownSelectors(mixed $value, string $label): array +{ + if ($value === null) { + return []; + } + if (!is_array($value)) { + throw new \InvalidArgumentException("{$label} must be an array"); + } + $result = []; + foreach ($value as $entry) { + if (!is_array($entry)) { + throw new \InvalidArgumentException("{$label} entries must be objects"); + } + $generalId = $entry['generalId'] ?? null; + $actionName = $entry['actionName'] ?? null; + if (!is_int($generalId) || $generalId < 1 || !is_string($actionName) || $actionName === '') { + throw new \InvalidArgumentException( + "{$label} entries require a positive generalId and non-empty actionName", + ); + } + $result["{$generalId}:{$actionName}"] = [ + 'generalId' => $generalId, + 'actionName' => $actionName, + ]; + } + ksort($result, SORT_STRING); + return array_values($result); +} + /** @param array $row */ function comparisonPickRow(array $row, array $mapping, array $jsonKeys = []): array { @@ -98,6 +128,10 @@ function comparisonTurnStateSnapshot(array $request): array $nationIds = comparisonIntegerList($observe['nationIds'] ?? [], 'nationIds'); $logAfterId = $observe['logAfterId'] ?? 0; $messageAfterId = $observe['messageAfterId'] ?? 0; + $generalCooldownSelectors = comparisonGeneralCooldownSelectors( + $observe['generalCooldowns'] ?? [], + 'generalCooldowns', + ); if (!is_int($logAfterId) || $logAfterId < 0 || !is_int($messageAfterId) || $messageAfterId < 0) { throw new \InvalidArgumentException('logAfterId and messageAfterId must be non-negative integers'); } @@ -116,6 +150,19 @@ function comparisonTurnStateSnapshot(array $request): array 'init_month', 'develcost', ]); + $nextExecuteStorage = KVStorage::getStorage($db, 'next_execute'); + $nextExecuteStorage->resetCache(); + $generalCooldowns = array_map( + static function (array $selector) use ($nextExecuteStorage): array { + $key = "next_execute_{$selector['generalId']}_{$selector['actionName']}"; + $value = $nextExecuteStorage->getValue($key); + return [ + ...$selector, + 'nextAvailableTurn' => is_int($value) ? $value : null, + ]; + }, + $generalCooldownSelectors, + ); $generals = array_map( static function (array $row): array { @@ -362,6 +409,7 @@ function comparisonTurnStateSnapshot(array $request): array 'initYear' => (int)$worldValues['init_year'], 'initMonth' => (int)$worldValues['init_month'], 'develCost' => (int)$worldValues['develcost'], + 'generalCooldowns' => $generalCooldowns, ], 'generals' => $generals, 'cities' => $cities,