Unified draw engine with one request contract and one response contract for every draw method.
- PHP 8.4+
- BCMath extension
composer require infocyph/game-drawUse Infocyph\Draw\Draw as the only public draw entrypoint.
[
'method' => '...', // required
'items' => [...], // required
'candidates' => [...], // required for grand + campaign.*
'sourceFile' => '...', // optional alternative for grand
'options' => [...], // optional
][
'method' => '...',
'entries' => [
[
'itemId' => ?string,
'candidateId' => ?string,
'value' => mixed,
'meta' => array
]
],
'raw' => mixed,
'meta' => [
'mode' => 'single|multi',
'requestedCount' => int,
'returnedCount' => int
]
]Single-pick and multi-pick methods both return entries[].
luckygrandprobabilityeliminationweightedEliminationroundRobincumulativebatchedtimeBasedweightedBatchsequentialrangeWeightedcampaign.runcampaign.batchcampaign.simulate
use Infocyph\Draw\Draw;
$draw = new Draw();$result = $draw->execute([
'method' => 'lucky',
'items' => [
['item' => 'gift_a', 'chances' => 10, 'amounts' => [1]],
['item' => 'gift_b', 'chances' => 20, 'amounts' => [2]],
],
'options' => ['count' => 2],
]);$result = $draw->execute([
'method' => 'grand',
'items' => ['gift_a' => 5, 'gift_b' => 2],
'candidates' => ['u1', 'u2', 'u3', 'u4'],
'options' => ['retryCount' => 50],
]);$result = $draw->execute([
'method' => 'probability',
'items' => [
['name' => 'item1', 'weight' => 0.2],
['name' => 'item2', 'weight' => 0.8],
],
'options' => ['count' => 3],
]);$result = $draw->execute([
'method' => 'elimination',
'items' => [
['name' => 'item1'],
['name' => 'item2'],
],
'options' => ['count' => 2],
]);$result = $draw->execute([
'method' => 'weightedElimination',
'items' => [
['name' => 'item1', 'weight' => 10],
['name' => 'item2', 'weight' => 20],
],
'options' => ['count' => 2],
]);$result = $draw->execute([
'method' => 'roundRobin',
'items' => [
['name' => 'item1'],
['name' => 'item2'],
],
'options' => ['count' => 3],
]);$result = $draw->execute([
'method' => 'cumulative',
'items' => [
['name' => 'item1'],
['name' => 'item2'],
],
'options' => ['count' => 3],
]);$result = $draw->execute([
'method' => 'batched',
'items' => [
['name' => 'item1'],
['name' => 'item2'],
['name' => 'item3'],
],
'options' => ['count' => 2, 'withReplacement' => false],
]);$result = $draw->execute([
'method' => 'timeBased',
'items' => [
['name' => 'item1', 'weight' => 10, 'time' => 'daily'],
['name' => 'item2', 'weight' => 20, 'time' => 'weekly'],
],
'options' => ['count' => 2],
]);$result = $draw->execute([
'method' => 'weightedBatch',
'items' => [
['name' => 'item1', 'weight' => 10],
['name' => 'item2', 'weight' => 20],
],
'options' => ['count' => 3],
]);$result = $draw->execute([
'method' => 'sequential',
'items' => [
['name' => 'item1'],
['name' => 'item2'],
],
'options' => ['count' => 3],
]);$result = $draw->execute([
'method' => 'rangeWeighted',
'items' => [
['name' => 'item1', 'min' => 1, 'max' => 50, 'weight' => 10],
['name' => 'item2', 'min' => 5, 'max' => 25, 'weight' => 15],
],
'options' => ['count' => 2],
]);$result = $draw->execute([
'method' => 'campaign.run',
'items' => [
'gold' => ['count' => 1, 'group' => 'premium'],
'silver' => ['count' => 2, 'group' => 'basic'],
],
'candidates' => ['u1', 'u2', 'u3', 'u4'],
'options' => [
'rules' => [
'perUserCap' => 1,
'perItemCap' => ['gold' => 1],
'groupQuota' => ['premium' => 1, 'basic' => 2],
],
'retryLimit' => 100,
'withExplain' => true,
],
]);$result = $draw->execute([
'method' => 'campaign.batch',
'items' => ['bootstrap' => ['count' => 1]],
'candidates' => ['u1', 'u2', 'u3', 'u4'],
'options' => [
'rules' => ['perUserCap' => 1],
'phases' => [
['name' => 'phase_1', 'items' => ['item_a' => ['count' => 2]]],
['name' => 'phase_2', 'items' => ['item_b' => ['count' => 2]]],
],
'retryLimit' => 100,
],
]);$result = $draw->execute([
'method' => 'campaign.simulate',
'items' => ['gold' => ['count' => 1], 'silver' => ['count' => 2]],
'candidates' => ['u1', 'u2', 'u3', 'u4'],
'options' => ['iterations' => 1000, 'retryLimit' => 100],
]);