还在抱怨王者荣耀水晶难抽?PHP一文带你搞懂游戏中的抽奖算法
一、初始化奖品
- id 奖品的id
- pid 奖品的自定义id
- type 奖品类型,1、虚拟奖品 2、实物奖品 3、礼包码 待扩充
- name 奖品名称
- total 奖品总数
- chance 获奖概率/抽奖基数10000
- daynum 每日数量限制
- pay 充值限制
1 <?php2 $prize = [3 ['id' => 1, 'pid' => 11, 'type' => 1, 'name' => '典藏英雄', 'total' => 20, 'chance' => 1000, 'daynum' => 10, 'pay' => 2000 ],4 ['id' => 2, 'pid' => 12, 'type' => 1, 'name' => '史诗皮肤', 'total' => 40, 'chance' => 1000, 'daynum' => 10, 'pay' => 4000 ],5 ['id' => 3, 'pid' => 13, 'type' => 1, 'name' => '钻石奖励', 'total' => 80, 'chance' => 1000, 'daynum' => 10, 'pay' => 4000 ],6 ['id' => 4, 'pid' => 14, 'type' => 1, 'name' => '荣耀水晶', 'total' => 20, 'chance' => 1000, 'daynum' => 10, 'pay' => 8000 ]7 ];
- 奖品详情应该从数据库中读出来
- 奖品详情应该加入缓存,避免数据库的压力
二、谢谢参与
1 <?php2 $thanks_prize = [3 'id' => 0,4 'pid' => 0,5 'type' => 1,6 'name' => '谢谢参与'7 ];
- 为填充剩余概率的奖品
三、过滤抽奖、如充值条件
1 <?php2 $pay_total = 7000;3 foreach ($prize as $key => $value) {4 if($value['pay'] > $pay_total) unset($prize[$key]);5 }
- 初步过滤一些必要因素,比如充值,角色创建时间等
四、重组概率
1 <?php 2 $now_chance = array_sum(array_column($prize, 'chance')); 3 $remain_chance = 10000 - $now_chance; 4 $prize[] = ['id' => 0, 'pid' => 0, 'type' => 1, 'name' => '谢谢参与', 'total' => 0, 'chance' => $remain_chance, 'daynum' => 0, 'pay' => 0]; 5 6 $award = []; 7 $num = 0; 8 foreach ($prize as $_v) { 9 $num = $_v['chance'];10 $award[] = ['id' => $_v['id'], 'pid' => $_v['pid'], 'type' => $_v['type'], 'name' => $_v['name'], 'total' => $_v['total'], 'chance' => $num, 'daynum' => $_v['daynum'], 'pay' => $_v['pay']];11 }
- 初步过滤后,重构新的抽奖信息,加入谢谢参与
- 第二步重组概率
五、进行抽奖
1 <?php 2 $rand = mt_rand(1, 10000); 3 $result = []; 4 foreach ($award as $_k => $_v) { 5 if ($_k == 0) { 6 if ($rand > 0 && $rand <= $_v['chance']) { 7 $result = $_v; 8 break; 9 }10 } else {11 if ($rand > $award[$_k - 1]['chance'] && $rand <= $_v['chance']) {12 $result = $_v;13 break;14 }15 }16 }
开始抽奖,并返回抽中的结果
六、过滤回调
1 <?php 2 //此处应该查询数据库,查看该奖品已经抽中的数量 3 $yet_num = 50; 4 if($result['pid'] != 0 && $yet_num > $result['total']) { 5 $result = $thanks_prize; 6 } 7 8 //此处应该查询数据库,查看该奖品今日已经抽中的数量 9 $yet_today_num = 50;10 if($result['pid'] != 0 && $yet_today_num > $result['daynum']) {11 $result = $thanks_prize;12 }
- 二次过滤,奖品总数的限制以及奖品的每日限制等
七、最终抽奖结果
1 <?php 2 //删除敏感字段 3 unset($result['total'],$result['chance'],$result['daynum'],$result['pay']); 4 5 //返回最终抽奖结果 6 echo json_encode([ 7 'prize' => $award, 8 'rand' => $rand, 9 'result' => $result10 ]);
八、抽奖封装成类
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: autofelix 5 * Date: 2020/10/30 6 * Time: 13:14 7 * Desc: 抽奖算法 8 */ 9 10 class Lottery 11 { 12 /** 13 * 概率基数 14 * @var int 15 */ 16 private $total_chance = 10000; 17 18 /** 19 * 谢谢参与奖励 20 * @var array 21 */ 22 private $thanks_prize = [ 23 'id' => 0, 24 'pid' => 0, 25 'type' => 1, 26 'name' => '谢谢参与' 27 ]; 28 29 /** 30 * 奖池 31 * @var array 32 */ 33 private $prize = [ 34 ['id' => 1, 'pid' => 11, 'type' => 1, 'name' => '典藏英雄', 'total' => 20, 'chance' => 1000, 'daynum' => 10, 'pay' => 2000 ], 35 ['id' => 2, 'pid' => 12, 'type' => 1, 'name' => '史诗皮肤', 'total' => 40, 'chance' => 1000, 'daynum' => 10, 'pay' => 4000 ], 36 ['id' => 3, 'pid' => 13, 'type' => 1, 'name' => '钻石奖励', 'total' => 80, 'chance' => 1000, 'daynum' => 10, 'pay' => 4000 ], 37 ['id' => 4, 'pid' => 14, 'type' => 1, 'name' => '荣耀水晶', 'total' => 20, 'chance' => 1000, 'daynum' => 10, 'pay' => 8000 ] 38 ]; 39 40 /** 41 * Lottery constructor. 42 */ 43 public function __construct() 44 { 45 } 46 47 /** 48 * @return int 49 */ 50 private function get_user_pay() 51 { 52 //这里应该调用接口,返回用户正确的充值信息 53 return 3000; 54 } 55 56 /** 57 * 重构奖池、重组概率 58 * @return array 59 */ 60 private function init_lottery_pond() 61 { 62 $award = []; 63 64 //充值限制 65 $user_pay = $this->get_user_pay(); 66 foreach ($this->prize as $key => $value) { 67 if($value['pay'] <= $user_pay) unset($this->prize[$key]); 68 } 69 70 //加入谢谢惠顾 71 $now_chance = array_sum(array_column($this->prize, 'chance')); 72 $remain_chance = $this->total_chance - $now_chance; 73 $this->prize[] = ['id' => 0, 'pid' => 0, 'type' => 1, 'name' => '谢谢参与', 'total' => 0, 'chance' => $remain_chance, 'daynum' => 0, 'pay' => 0]; 74 75 //重组概率 76 $num = 0; 77 foreach ($this->prize as $_v) { 78 $num = $_v['chance']; 79 $award[] = ['id' => $_v['id'], 'pid' => $_v['pid'], 'type' => $_v['type'], 'name' => $_v['name'], 'total' => $_v['total'], 'chance' => $num, 'daynum' => $_v['daynum'], 'pay' => $_v['pay']]; 80 } 81 82 return $award; 83 } 84 85 /** 86 * 获取抽奖结果 87 * @return array 88 */ 89 public function get_prize() 90 { 91 $award = $this->init_lottery_pond(); 92 $rand = mt_rand(1, $this->total_chance); 93 $result = []; 94 foreach ($award as $_k => $_v) { 95 if ($_k == 0) { 96 if ($rand > 0 && $rand <= $_v['chance']) { 97 $result = $_v; 98 break; 99 }100 } else {101 if ($rand > $award[$_k - 1]['chance'] && $rand <= $_v['chance']) {102 $result = $_v;103 break;104 }105 }106 }107 108 $result = $this->filter($result);109 return $result;110 }111 112 /**113 * 抽奖过滤回调函数114 * @param $result115 * @return array116 */117 public function filter($result)118 {119 //奖品总数限制,此处应该查数据库120 $yet_num = 50;121 if($result['pid'] != 0 && $yet_num > $result['total']) {122 $result = $this->thanks_prize;123 }124 125 //奖品每日数量限制,此处应该查数据库126 $yet_today_num = 50;127 if($result['pid'] != 0 && $yet_today_num > $result['daynum']) {128 $result = $this->thanks_prize;129 }130 131 //不暴露敏感信息132 unset($result['total'], $result['chance'], $result['daynum'], $result['pay'] );133 return $result;134 }135 136 private function __clone()137 {138 }139 }140 141 echo json_encode((new Lottery())->get_prize());
赞 (0)