PHP抽獎演算法

橙子与柠檬發表於2024-09-21

一、初始化獎品
id 獎品的id

pid 獎品的自定義id

type 獎品型別,1、虛擬獎品 2、實物獎品 3、禮包碼 待擴充

name 獎品名稱

total 獎品總數

chance 獲獎機率/抽獎基數10000

daynum 每日數量限制

pay 充值限制

$prize = [
['id' => 1, 'pid' => 11, 'type' => 1, 'name' => '典藏英雄', 'total' => 20, 'chance' => 1000, 'daynum' => 10, 'pay' => 2000 ],
['id' => 2, 'pid' => 12, 'type' => 1, 'name' => '史詩皮膚', 'total' => 40, 'chance' => 1000, 'daynum' => 10, 'pay' => 4000 ],
['id' => 3, 'pid' => 13, 'type' => 1, 'name' => '鑽石獎勵', 'total' => 80, 'chance' => 1000, 'daynum' => 10, 'pay' => 4000 ],
['id' => 4, 'pid' => 14, 'type' => 1, 'name' => '榮耀水晶', 'total' => 20, 'chance' => 1000, 'daynum' => 10, 'pay' => 8000 ]
];

二、謝謝參與
$thanks_prize = [
'id' => 0,
'pid' => 0,
'type' => 1,
'name' => '謝謝參與'
];

三、過濾抽獎、如充值條件
$pay_total = 7000;
foreach ($prize as $key => $value) {
if($value['pay'] > $pay_total) unset($prize[$key]);
}

四、重組機率
$now_chance = array_sum(array_column($prize, 'chance'));
$remain_chance = 10000 - $now_chance;
$prize[] = ['id' => 0, 'pid' => 0, 'type' => 1, 'name' => '謝謝參與', 'total' => 0, 'chance' => $remain_chance, 'daynum' => 0, 'pay' => 0];

$award = [];
$num = 0;
foreach ($prize as $_v) {
$num += $_v['chance'];
$award[] = ['id' => $_v['id'], 'pid' => $_v['pid'], 'type' => $_v['type'], 'name' => $_v['name'], 'total' => $_v['total'], 'chance' => $num, 'daynum' => $_v['daynum'], 'pay' => $_v['pay']];
}
初步過濾後,重構新的抽獎資訊,加入謝謝參與
第二步重組機率

五、進行抽獎
$rand = mt_rand(1, 10000);
$result = [];
foreach ($award as $_k => $_v) {
if ($_k == 0) {
if ($rand > 0 && $rand <= $_v['chance']) {
$result = $_v;
break;
}
} else {
if ($rand > $award[$_k - 1]['chance'] && $rand <= $_v['chance']) {
$result = $_v;
break;
}
}
}
開始抽獎,並返回抽中的結果

六、過濾回撥
//此處應該查詢資料庫,檢視該獎品已經抽中的數量
$yet_num = 50;
if($result['pid'] != 0 && $yet_num > $result['total']) {
$result = $thanks_prize;
}

//此處應該查詢資料庫,檢視該獎品今日已經抽中的數量
$yet_today_num = 50;
if($result['pid'] != 0 && $yet_today_num > $result['daynum']) {
$result = $thanks_prize;
}
二次過濾,獎品總數的限制以及獎品的每日限制等

七、最終抽獎結果
//刪除敏感欄位
unset($result['total'],$result['chance'],$result['daynum'],$result['pay']);

//返回最終抽獎結果
echo json_encode([
'prize' => $award,
'rand' => $rand,
'result' => $result
]);

八、抽獎封裝成類
<?php
/**
* Created by PhpStorm.
* User: autofelix
* Date: 2020/10/30
* Time: 13:14
* Desc: 抽獎演算法
*/

class Lottery
{
/**
* 機率基數
* @var int
*/
private $total_chance = 10000;

/**
* 謝謝參與獎勵
* @var array
*/
private $thanks_prize = [
'id' => 0,
'pid' => 0,
'type' => 1,
'name' => '謝謝參與'
];

/**
* 獎池
* @var array
*/
private $prize = [
['id' => 1, 'pid' => 11, 'type' => 1, 'name' => '典藏英雄', 'total' => 20, 'chance' => 1000, 'daynum' => 10, 'pay' => 2000 ],
['id' => 2, 'pid' => 12, 'type' => 1, 'name' => '史詩皮膚', 'total' => 40, 'chance' => 1000, 'daynum' => 10, 'pay' => 4000 ],
['id' => 3, 'pid' => 13, 'type' => 1, 'name' => '鑽石獎勵', 'total' => 80, 'chance' => 1000, 'daynum' => 10, 'pay' => 4000 ],
['id' => 4, 'pid' => 14, 'type' => 1, 'name' => '榮耀水晶', 'total' => 20, 'chance' => 1000, 'daynum' => 10, 'pay' => 8000 ]
];

/**
* Lottery constructor.
*/
public function __construct()
{
}

/**
* @return int
*/
private function get_user_pay()
{
//這裡應該呼叫介面,返回使用者正確的充值資訊
return 3000;
}

/**
* 重構獎池、重組機率
* @return array
*/
private function init_lottery_pond()
{
$award = [];

//充值限制
$user_pay = $this->get_user_pay();
foreach ($this->prize as $key => $value) {
if($value['pay'] <= $user_pay) unset($this->prize[$key]);
}

//加入謝謝惠顧
$now_chance = array_sum(array_column($this->prize, 'chance'));
$remain_chance = $this->total_chance - $now_chance;
$this->prize[] = ['id' => 0, 'pid' => 0, 'type' => 1, 'name' => '謝謝參與', 'total' => 0, 'chance' => $remain_chance, 'daynum' => 0, 'pay' => 0];

//重組機率
$num = 0;
foreach ($this->prize as $_v) {
$num += $_v['chance'];
$award[] = ['id' => $_v['id'], 'pid' => $_v['pid'], 'type' => $_v['type'], 'name' => $_v['name'], 'total' => $_v['total'], 'chance' => $num, 'daynum' => $_v['daynum'], 'pay' => $_v['pay']];
}

return $award;
}

/**
* 獲取抽獎結果
* @return array
*/
public function get_prize()
{
$award = $this->init_lottery_pond();
$rand = mt_rand(1, $this->total_chance);
$result = [];
foreach ($award as $_k => $_v) {
if ($_k == 0) {
if ($rand > 0 && $rand <= $_v['chance']) {
$result = $_v;
break;
}
} else {
if ($rand > $award[$_k - 1]['chance'] && $rand <= $_v['chance']) {
$result = $_v;
break;
}
}
}

$result = $this->filter($result);
return $result;
}

/**
* 抽獎過濾回撥函式
* @param $result
* @return array
*/
public function filter($result)
{
//獎品總數限制,此處應該查資料庫
$yet_num = 50;
if($result['pid'] != 0 && $yet_num > $result['total']) {
$result = $this->thanks_prize;
}

//獎品每日數量限制,此處應該查資料庫
$yet_today_num = 50;
if($result['pid'] != 0 && $yet_today_num > $result['daynum']) {
$result = $this->thanks_prize;
}

//不暴露敏感資訊
unset($result['total'], $result['chance'], $result['daynum'], $result['pay'] );
return $result;
}

private function __clone()
{
}
}

echo json_encode((new Lottery())->get_prize());

相關文章