还在抱怨王者荣耀水晶难抽?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());

来源:https://www.icode9.com/content-1-783051.html

(0)

相关推荐