test(compare): trace NPC possession selection

This commit is contained in:
2026-07-31 04:39:00 +00:00
parent 765dee831c
commit 90d078c100
4 changed files with 514 additions and 30 deletions
+86
View File
@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace sammo;
final class NpcPossessionSelector
{
public static function buildSeed(string|int $hiddenSeed, int $owner, string $now): string
{
return Util::simpleSerialize($hiddenSeed, 'SelectNPCToken', $owner, $now);
}
/** @param array<string, mixed> $general */
public static function weight(array $general): float
{
return pow(
(int)$general['leadership'] + (int)$general['strength'] + (int)$general['intel'],
1.5,
);
}
/**
* @param array<int|string, array<string, mixed>> $oldPick
* @param list<int> $keepIds
* @return array{picked: array<int|string, array<string, mixed>>, cancelled: bool}
*/
public static function applyKeep(array $oldPick, array $keepIds): array
{
$picked = [];
foreach ($keepIds as $keepId) {
if (array_key_exists($keepId, $oldPick) && (int)$oldPick[$keepId]['keepCnt'] > 0) {
$picked[$keepId] = $oldPick[$keepId];
$picked[$keepId]['keepCnt'] = (int)$picked[$keepId]['keepCnt'] - 1;
}
}
return [
'picked' => $picked,
'cancelled' => count($picked) === count($oldPick),
];
}
/**
* @param array<int|string, array<string, mixed>> $candidates
* @param array<int|string, float> $weights
* @param iterable<string> $reservedPayloads
*/
public static function removeReserved(array &$candidates, array &$weights, iterable $reservedPayloads): void
{
foreach ($reservedPayloads as $reservedPayload) {
$reserved = Json::decode($reservedPayload);
foreach (array_keys($reserved) as $reservedNpc) {
if (array_key_exists($reservedNpc, $weights)) {
unset($candidates[$reservedNpc], $weights[$reservedNpc]);
}
}
}
}
/**
* @param array<int|string, array<string, mixed>> $candidates
* @param array<int|string, float> $weights
* @param array<int|string, array<string, mixed>> $picked
* @param null|callable(int|string): void $onDraw
* @return array<int|string, array<string, mixed>>
*/
public static function select(
array $candidates,
array $weights,
array $picked,
RandUtil $rng,
?callable $onDraw = null,
): array {
$pickLimit = min(count($candidates), 5);
while (count($picked) < $pickLimit) {
$generalId = $rng->choiceUsingWeight($weights);
if ($onDraw !== null) {
$onDraw($generalId);
}
if (!array_key_exists($generalId, $picked)) {
$picked[$generalId] = $candidates[$generalId];
}
}
return $picked;
}
}