diff --git a/.gitignore b/.gitignore index 59ed94e..c0dee59 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ vendor composer.lock git-story_media test.php +*~ \ No newline at end of file diff --git a/README.md~ b/README.md~ deleted file mode 100644 index 75b43df..0000000 --- a/README.md~ +++ /dev/null @@ -1,308 +0,0 @@ -# Game Draw - -[![Codacy Badge](https://app.codacy.com/project/badge/Grade/56e7f49275dc4042b67d53b4209b193d)](https://www.codacy.com/gh/abmmhasan/Game-Draw/dashboard?utm_source=github.com&utm_medium=referral&utm_content=abmmhasan/Game-Draw&utm_campaign=Badge_Grade) -![Libraries.io dependency status for GitHub repo](https://img.shields.io/librariesio/github/abmmhasan/game-draw) -![Packagist Downloads](https://img.shields.io/packagist/dt/abmmhasan/game-draw) -[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) -![Packagist Version](https://img.shields.io/packagist/v/abmmhasan/game-draw) -![Packagist PHP Version Support](https://img.shields.io/packagist/php-v/abmmhasan/game-draw) -![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/abmmhasan/game-draw) - - -The **Game Draw** library provides flexible and varied methods for selecting winners based on different types of draws, **Lucky Draw**, **Grand Draw**, and a customizable **Flexible Draw** to meet a range of requirements. - -> Please don't use this to generate things/prizes with People's hard-earned money. It is intended to make things fun with bonus gifts only. - - -## Prerequisites - -- **Language:** PHP 8+ -- **PHP Extension:** BCMath (may need to install manually) - -## Installation - -``` -composer require infocyph/game-draw -``` -## Overview - -### 1. LuckyDraw - -The `LuckyDraw` class allows for winner selection based on item chances and weighted amounts. - -#### Input Data - -```php -$products = [ - [ - 'item' => 'product_000_NoLuck', // Item code or Identifier - 'chances' => '100000', // Item Chances - 'amounts'=> [ 1 ] // Item Amounts - ], - [ - 'item' => 'product_001', - 'chances' => '1000', - 'amounts' => '1.5,10.00001,1' // Weighted CSV formatted range (min,max,bias) - ], - [ - 'item' => 'product_002', - 'chances' => '500.001', // Fraction Allowed - 'amounts' => [ - 1 => 100, // Amount chances - 5 => 50, // Format: Amount => Chances - 10 => 10.002, // Fraction allowed - ] - ], - [ - 'item' => 'product_003', - 'chances' => '100', - 'amounts' => [ - 1 => 100, - 5 => 50, - 10 => 10, - 20 => 5, - ] - ], - [ - 'item' => 'product_004', - 'chances' => '1', - 'amounts' => [ 10, 15, 30, 50 ] // Amounts without probability - ], -] -``` - -- **item**: Provide your item's unique identifier - -- **chances**: Weight of item (Float/Int). - - It will be compared along all the items in array. - - The higher the chances the greater the chances of getting the item. - - In case of active inventory you can pass available item stock here - -- **amounts**: String or Array of Item amount (Float/Int). It can be any like: - - (array) Single Positive value, i.e. [ 1 ] or Multiple Positive value (randomly picked), i.e. [ 1, 2, 3, 5] - - (array) Weighted amount, i.e. - ```php - [ - 5 => 100, - 15 => 50, - 50 => 10, - 80 => 5.001 - ] - ``` - - (String) Weighted CSV formatted range (min,max,bias) ```'1,10.00001,0.001'``` - - Only 3 members allowed in CSV format **min,max,bias** - - Max should be greater than or equal to min, bias should be greater than 0 - - The higher the bias, the more the chance to pick the lowest amount - -#### Usage - -```php -$luckyDraw = new AbmmHasan\Draw\LuckyDraw($products); -$result = $luckyDraw->pick(); -``` - -Example Output: - -```php -[ - 'item' => 'product_000_NoLuck', - 'amount' => 1 -] -``` - -> Inventory Solutions: Available stock should be passed (after subtracting used amount from stock amount) in chances properly. - -### 2. GrandDraw - -The `GrandDraw` class is designed for large draws where items and user entries are managed in bulk. - -#### Input Data - -```php -$prizes = -[ - 'product_001'=>50, // Item Code/Identifier => Amount of the item - 'product_002'=>5, - 'product_003'=>3, - 'product_004'=>2, - 'product_005'=>1 -]; -``` - -- **item**: Provide your item's unique identifier - -- **amounts**: Amount of gift. It must be a positive integer value. - -User entries are loaded using a CSV file: - -```csv -"usr47671", -"usr57665", -"usr47671", -..... -``` - -#### Usage - -```php -$grandDraw = new AbmmHasan\Draw\GrandDraw(); -$grandDraw->setItems($prizes) - ->setUserListFilePath('./Sample1000.csv'); -$winners = $grandDraw->getWinners(); -``` - -Example Output: - -```php -[ - 'product_001' => ['usr47671', 'usr57665', 'usr92400'], - 'product_002' => ['usr50344', 'usr60450', 'usr62662'] -] -``` - -### 3. FlexibleDraw - -The `FlexibleDraw` class offers a versatile approach to draws, supporting various types of selection methods, including probability-based, elimination, round-robin, and more. This flexibility allows for customized and dynamic but basic draws. - -#### Supported Draw Types -- **Probability Draw**: Selects items based on their assigned probability weights. -- **Elimination Draw**: Items are drawn and removed from the selection pool, preventing repeated selection. -- **Round Robin Draw**: Items are selected in a round-robin sequence, ensuring each item is chosen before repeating. -- **Cumulative Draw**: Draws items based on cumulative weights, adjusting probabilities over time. -- **Batched Draw**: Draws a specified batch of items, with or without replacement. -- **Time-Based Draw**: Selects items based on time intervals or frequency criteria. -- **Weighted Batch Draw**: Draws a batch of items based on weighted probabilities. -- **Group Draw**: Selects items from within specific groups. -- **Sequential Draw**: Draws items in a predefined sequential order. -- **Range Weighted Draw**: Selects items based on a specified range with a weighted probability. - -#### FlexibleDraw Options - -1. **Probability Draw**: - - Selects items based on weighted probabilities, allowing higher-weight items to be more likely chosen. - - ```php - $items = [ - ['name' => 'item1', 'weight' => 10], - ['name' => 'item2', 'weight' => 20], - ]; - ``` - -2. **Weighted Elimination Draw**: - - Items are selected and removed from the pool, ensuring each item is chosen only once. - - ```php - $items = [ - ['name' => 'item1', 'weight' => 10], - ['name' => 'item2', 'weight' => 20], - ]; - -3. **Elimination Draw**: - - Items are selected and removed from the pool, ensuring each item is chosen only once. - - ```php - $items = [ - ['name' => 'item1'], - ['name' => 'item2'], - ]; - ``` - -4. **Round Robin Draw**: - - Items are selected sequentially in a round-robin pattern. - - ```php - $items = [ - ['name' => 'item1'], - ['name' => 'item2'], - ]; - ``` - -5. **Cumulative Draw**: - - Draws items based on cumulative weights, with probabilities adjusting dynamically. - - ```php - $items = [ - ['name' => 'item1'], - ['name' => 'item2'], - ]; - ``` - -6. **Batched Draw**: - - Draws a batch of items in one call, with options for replacement. If replacement is enabled, same items will/can be drawn multiple times. - - ```php - $items = [ - ['name' => 'item1'], - ['name' => 'item2'], - ]; - ``` - -7. **Time-Based Draw**: - - Draws items based on specified time intervals or conditions. - - ```php - $items = [ - ['name' => 'item1', 'weight' => 10, 'time' => 'daily'], - ['name' => 'item2', 'weight' => 20, 'time' => 'weekly'], - ]; - ``` - -8. **Weighted Batch Draw**: - - Draws a batch of items using weighted probabilities, balancing selection based on item weights. - - ```php - $items = [ - ['name' => 'item1', 'weight' => 10], - ['name' => 'item2', 'weight' => 20], - ]; - ``` - -9. **Sequential Draw**: - - Selects items in a fixed, sequential order, ideal for draws that require strict ordering. - - ```php - $items = [ - ['name' => 'item1'], - ['name' => 'item2'], - ]; - ``` - -10. **Range Weighted Draw**: -- Specifies a range for each item using `min`, `max`, and `weight` values. -- Ensures `min` is less than `max` for valid selection. - -```php -$items = [ - ['name' => 'item1', 'min' => 1, 'max' => 50, 'weight' => 10], - ['name' => 'item2', 'min' => 5, 'max' => 25, 'weight' => 15], -]; -``` - -#### Usage - -```php -$flexibleDraw = new AbmmHasan\Draw\FlexibleDraw($items); -$result = $flexibleDraw->draw(); -``` - -### Example Output - -Based on the selected draw type, the output will provide details on the chosen item, including any range, group, or other specifications: - -```php -[ - 'item' => 'item1', - 'weight' => 10, - 'group' => 'groupA' -] -``` - -### FlexibleDraw Configuration Summary - -The `FlexibleDraw` class can adapt to various selection methods, supporting custom configurations with properties like `weight`, `group`, `min`, `max`, and more. This class is ideal for applications that require nuanced, dynamic draw methods beyond basic random selection but not complex as Lucky Draw or high volume as Grand Draw. - ---- - -## Support - -Having trouble? Create an issue! diff --git a/src/Draw/LuckyDraw.php~ b/src/Draw/LuckyDraw.php~ deleted file mode 100644 index 2ae8a54..0000000 --- a/src/Draw/LuckyDraw.php~ +++ /dev/null @@ -1,177 +0,0 @@ -items)) { - throw new LengthException('Items array must contain at least one item.'); - } - - $requiredKeys = array_flip(['item', 'chances', 'amounts']); - foreach ($this->items as $index => $item) { - $missingKeys = array_diff_key($requiredKeys, $item); - if (!empty($missingKeys)) { - throw new InvalidArgumentException( - "Item at index $index is missing required keys: " . implode(', ', array_keys($missingKeys)), - ); - } - } - } - - /** - * Picks an item & amount based on chances. - * - * @param bool $check Indicates whether to run the check method before picking an item (default: true) - * @return array Returns an array containing the picked item and its amount. - * @throws Exception - */ - public function pick(bool $check = true): array - { - $check && $this->check(); - $items = $this->prepare(array_filter(array_column($this->items, 'chances', 'item'))); - $pickedItem = $this->draw($items); - - // Retrieve amounts array for the picked item - $amounts = $this->items[array_search($pickedItem, array_column($this->items, 'item'))]['amounts']; - is_string($amounts) && $amounts = [$this->weightedAmountRange($amounts)]; - - return [ - 'item' => $pickedItem, - 'amount' => $this->selectAmount($amounts), - ]; - } - - /** - * Selects an amount based on type. - * - * @param array $amounts - * @return float|int - * @throws Exception - */ - private function selectAmount(array $amounts): float|int - { - return match (true) { - count($amounts) === 1 => current($amounts), - $this->isSequential($amounts) => $amounts[array_rand($amounts)], - default => $this->draw($amounts), - }; - } - - /** - * Generates a weighted random number within a specified range. - * - * @param string $amounts A string containing the minimum, maximum, and bias values separated by commas. - * @return float|int A single randomly generated number within the specified range. - * @throws UnexpectedValueException If the amount range is invalid or the bias is less than or equal to 0. - */ - private function weightedAmountRange(string $amounts): float|int - { - $amounts = str_getcsv($amounts); - count($amounts) !== 3 && throw new UnexpectedValueException('Invalid amount range (expected: min,max,bias).'); - [$min, $max, $bias] = array_map('floatval', $amounts); - $max <= $min && throw new UnexpectedValueException('Maximum value should be greater than minimum.'); - $bias <= 0 && throw new UnexpectedValueException('Bias should be greater than 0.'); - return max( - min(round($min + pow(lcg_value(), $bias) * ($max - $min + 1), $this->getFractionLength([$min, $max])), - $max), - $min, - ); - } - - /** - * Draws among an array of items based on given weight. - * - * @param array $items The array of items to be processed. - * @return string The selected item from the array. - * @throws Exception if the random number generation fails. - */ - private function draw(array $items): string - { - if (count($items) === 1) { - return key($items); - } - - $random = random_int(1, array_sum($items)); - foreach ($items as $key => $value) { - $random -= (int)$value; - if ($random <= 0) { - return $key; - } - } - return array_search(max($items), $items); - } - - /** - * Prepares an array of items. - * - * @param array $items The array of items to be prepared. - * @return array The prepared array of items. - * @throws UnexpectedValueException - */ - private function prepare(array $items): array - { - if ($length = $this->getFractionLength($items)) { - $items = $this->multiply($items, $length); - } - return $items; - } - - /** - * Calculate the length of the fraction part in an array of items. - * - * @param array $items The array of items to calculate the fraction length from. - * @return int The length of the fraction part. - * @throws UnexpectedValueException - */ - private function getFractionLength(array $items): int - { - $length = 0; - foreach ($items as $item) { - $item > 0 || throw new UnexpectedValueException('Chances should be positive decimal number!'); - $fraction = strpos($item, '.'); - $fraction && $length = max(strlen($item) - $fraction - 1, $length); - } - return (int)$length; - } - - /** - * Multiplies each item by a decimal length. - * - * @param array $items Items array. - * @param int $length Length to multiply by. - * @return array Adjusted items array. - */ - private function multiply(array $items, int $length): array - { - $multiplier = 10 ** $length; - return array_map(fn($value) => (int)bcmul((string)$value, (string)$multiplier), $items); - } - - /** - * Checks if an array has sequential keys. - * - * @param array $array Array to check. - * @return bool True if sequential. - */ - private function isSequential(array $array): bool - { - return array_keys($array) === range(0, count($array) - 1); - } -}