merge: add legacy turn command trace runner
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace sammo;
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
http_response_code(404);
|
||||
exit;
|
||||
}
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
$_SERVER['REMOTE_ADDR'] ??= '127.0.0.1';
|
||||
error_reporting(E_ALL & ~E_DEPRECATED);
|
||||
require_once 'lib.php';
|
||||
require_once 'func.php';
|
||||
require_once __DIR__ . '/turn_state_snapshot.php';
|
||||
|
||||
final class TurnComparisonTracingRNG implements RNG
|
||||
{
|
||||
private int $sequence = 0;
|
||||
|
||||
/** @var list<array<string, mixed>> */
|
||||
public array $calls = [];
|
||||
|
||||
public function __construct(private readonly RNG $inner)
|
||||
{
|
||||
}
|
||||
|
||||
public static function getMaxInt(): int
|
||||
{
|
||||
return LiteHashDRBG::getMaxInt();
|
||||
}
|
||||
|
||||
public function nextBytes(int $bytes): string
|
||||
{
|
||||
$value = $this->inner->nextBytes($bytes);
|
||||
$this->record('nextBytes', ['bytes' => $bytes], bin2hex($value));
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function nextBits(int $bits): string
|
||||
{
|
||||
$value = $this->inner->nextBits($bits);
|
||||
$this->record('nextBits', ['bits' => $bits], bin2hex($value));
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function nextInt(?int $max = null): int
|
||||
{
|
||||
$value = $this->inner->nextInt($max);
|
||||
$this->record('nextInt', ['maxInclusive' => $max], $value);
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function nextFloat1(): float
|
||||
{
|
||||
$value = $this->inner->nextFloat1();
|
||||
$this->record('nextFloat1', [], $value);
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function record(string $operation, array $arguments, mixed $result): void
|
||||
{
|
||||
$this->calls[] = [
|
||||
'seq' => $this->sequence++,
|
||||
'operation' => $operation,
|
||||
'arguments' => $arguments,
|
||||
'result' => $result,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
function comparisonRunTurnCommand(array $request): array
|
||||
{
|
||||
if (getenv('TURN_DIFFERENTIAL_ENABLED') !== '1') {
|
||||
throw new \RuntimeException('TURN_DIFFERENTIAL_ENABLED=1 is required');
|
||||
}
|
||||
$kind = $request['kind'] ?? null;
|
||||
if ($kind !== 'general' && $kind !== 'nation') {
|
||||
throw new \InvalidArgumentException('kind must be general or nation');
|
||||
}
|
||||
$actorGeneralId = $request['actorGeneralId'] ?? null;
|
||||
$action = $request['action'] ?? null;
|
||||
$args = $request['args'] ?? null;
|
||||
if (!is_int($actorGeneralId) || $actorGeneralId < 1) {
|
||||
throw new \InvalidArgumentException('actorGeneralId must be a positive integer');
|
||||
}
|
||||
if (!is_string($action) || $action === '') {
|
||||
throw new \InvalidArgumentException('action must be a non-empty string');
|
||||
}
|
||||
|
||||
$snapshotRequest = ['observe' => $request['observe'] ?? []];
|
||||
$before = comparisonTurnStateSnapshot($snapshotRequest);
|
||||
$db = DB::db();
|
||||
$gameStorage = KVStorage::getStorage($db, 'game_env');
|
||||
$gameStorage->resetCache();
|
||||
$environment = $gameStorage->getAll();
|
||||
$general = General::createObjFromDB($actorGeneralId);
|
||||
$turn = new TurnExecutionHelper($general);
|
||||
$seedDomain = $kind === 'general' ? 'generalCommand' : 'nationCommand';
|
||||
$seed = Util::simpleSerialize(
|
||||
UniqueConst::$hiddenSeed,
|
||||
$seedDomain,
|
||||
(int)$environment['year'],
|
||||
(int)$environment['month'],
|
||||
$actorGeneralId,
|
||||
$action,
|
||||
);
|
||||
$tracingRng = new TurnComparisonTracingRNG(new LiteHashDRBG($seed));
|
||||
$rng = new RandUtil($tracingRng);
|
||||
|
||||
if ($kind === 'general') {
|
||||
$command = buildGeneralCommandClass($action, $general, $environment, $args);
|
||||
$resultTurn = $turn->processCommand($rng, $command, false);
|
||||
} else {
|
||||
$command = buildNationCommandClass($action, $general, $environment, $general->getLastTurn(), $args);
|
||||
$resultTurn = $turn->processNationCommand($rng, $command);
|
||||
}
|
||||
|
||||
$general->getLogger()->flush();
|
||||
$turn->applyDB();
|
||||
unset($turn);
|
||||
$after = comparisonTurnStateSnapshot($snapshotRequest);
|
||||
|
||||
return [
|
||||
'schemaVersion' => 1,
|
||||
'engine' => 'ref',
|
||||
'execution' => [
|
||||
'kind' => $kind,
|
||||
'actorGeneralId' => $actorGeneralId,
|
||||
'action' => $action,
|
||||
'args' => $args,
|
||||
'seedDomain' => $seedDomain,
|
||||
'outcome' => [
|
||||
'lastTurn' => $resultTurn->toRaw(),
|
||||
'commandName' => $command->getName(),
|
||||
],
|
||||
],
|
||||
'before' => $before,
|
||||
'after' => $after,
|
||||
'rng' => $tracingRng->calls,
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$input = stream_get_contents(STDIN);
|
||||
$request = json_decode($input === '' ? '{}' : $input, true, flags: JSON_THROW_ON_ERROR);
|
||||
if (!is_array($request)) {
|
||||
throw new \InvalidArgumentException('request must be an object');
|
||||
}
|
||||
echo json_encode(
|
||||
comparisonRunTurnCommand($request),
|
||||
JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION,
|
||||
), PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
fwrite(STDERR, $throwable::class . ': ' . $throwable->getMessage() . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
@@ -314,17 +314,24 @@ function comparisonTurnStateSnapshot(array $request): array
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$input = stream_get_contents(STDIN);
|
||||
$request = json_decode($input === '' ? '{}' : $input, true, flags: JSON_THROW_ON_ERROR);
|
||||
if (!is_array($request)) {
|
||||
throw new \InvalidArgumentException('request must be an object');
|
||||
function comparisonTurnStateSnapshotMain(): void
|
||||
{
|
||||
try {
|
||||
$input = stream_get_contents(STDIN);
|
||||
$request = json_decode($input === '' ? '{}' : $input, true, flags: JSON_THROW_ON_ERROR);
|
||||
if (!is_array($request)) {
|
||||
throw new \InvalidArgumentException('request must be an object');
|
||||
}
|
||||
echo json_encode(
|
||||
comparisonTurnStateSnapshot($request),
|
||||
JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION,
|
||||
), PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
fwrite(STDERR, $throwable::class . ': ' . $throwable->getMessage() . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
echo json_encode(
|
||||
comparisonTurnStateSnapshot($request),
|
||||
JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION,
|
||||
), PHP_EOL;
|
||||
} catch (\Throwable $throwable) {
|
||||
fwrite(STDERR, $throwable::class . ': ' . $throwable->getMessage() . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (realpath((string)($_SERVER['SCRIPT_FILENAME'] ?? '')) === __FILE__) {
|
||||
comparisonTurnStateSnapshotMain();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user