上一期的文章我们实现了《明日方舟》干员寻访模拟器单次抽奖的功能,了解了random.choice这个函数的特点,通过原始奖池机制把等可能转化为不等可能,上一期我们还编写了一个便捷的不等可能事件抽取函数,也比较了原始机制和该函数机制的优缺点,如果你忘记了上一期的内容,或者第一次看此栏目文章,可以选择移步上一期的文章:
Python实现复杂规则游戏抽奖模拟器第一期:间接学习random模块函数
给大家重温一下抽奖规则:
1、基准概率
六星干员出率:2%,五星干员出率:8%,四星干员出率:50%,三星干员出率:40%,不会出现一、二星干员。

2、默认卡池
六星干员42名,包括暂时绝版的干员6名;五星干员83名(33名不能寻访获得);四星干员47名(7名不能寻访获得);三星干员17名(1名无法寻访)。共计150名干员可被寻访到。
每次抽奖在150位非绝版干员中随机获取一名,那么各干员在其所属星级内等可能出现
3、官方每更新一个卡池,该卡池用户前十次抽奖内必有一次是五星或六星干员,具体第几次为随机。
4、官方的标准卡池(而非默认卡池)含有两名特定六星干员,三名特定五星干员,特定干员在所属星级内的出率总和占该星级出率的50%,也就是说,如果你抽中了六星干员,有50%概率抽到两名特定六星中的一个,具体是随机的,并且是等可能的,但也有50%概率抽到别的非特定干员。
5、如果用户在任何一个卡池连续50次都没有抽到六星干员,下一次六星干员的总出率将提高两个百分点之后每一次都会提升两个百分点,直到抽到六星,将恢复基准概率。这个次数不会因卡池变换而清零。
6、每次抽奖消耗600合成玉,合成玉与另一种货币:至纯源石的换算规律是:1源石=180合成玉

今天我们要继续实现模拟器的功能:“50次提概率”和“十次出一次”的两种机制。

首先我们回顾一下,上一期我们的抽奖机制是这样的:

import random
#100个事件,2个是六星,8个是五星,50个是4星,40个是三星
stars=[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,6,6]
#为节省篇幅,把一个星级所有干员信息放在了一行,需要查看的话往右边拉
six_stars=['黑','能天使','莫斯提马','艾雅法拉','伊芙利特','刻俄柏','斯卡蒂','煌','陈','银灰','赫拉格','阿','推进之王','安洁莉娜','麦哲伦','星熊','塞雷娅','夜莺','闪灵','风笛']
five_stars=['守林人','陨星','灰喉','白金','送葬人','普罗旺斯','蓝毒','夜魔','惊蛰','天火','布洛卡','拉普兰德','星极','诗怀雅','幽灵鲨','芙兰卡','狮蝎','食铁兽','崖心','槐琥','红','凛冬','德克萨斯','苇草','初雪','格劳克斯','真理','空','梅尔','雷蛇','临光','可颂','吽','白面鸮','赫默','华法琳','慑砂']
four_stars=['杰西卡','梅','流星','安比尔','白雪','红云','夜烟','远山','角峰','调香师','末药','苏苏洛','蛇屠箱','古米','霜叶','缠丸','猎蜂','慕斯','杜宾','阿消','暗索','砾','地灵','深海色','清道夫','桃金娘','红豆','宴','格雷伊']
three_stars=['炎熔','史都华德','克洛丝','空爆','月见夜','泡普卡','玫兰莎','香草','翎羽','芬','卡缇','米格鲁','斑点','芙蓉','安赛尔','梓兰']
a=random.choice(stars)
if a==3:result=random.choice(three_stars)
elif a==4:result=random.choice(four_stars)
elif a==5:result=random.choice(five_stars)
else:result=random.choice(six_stars)
print(result)

1、“50次加概率”的实现

如果想要实现50次没抽到六星干员就提升六星干员概率的话,我们应该给每次抽奖计数,如果这次没抽到六星,计数加1,如果抽到了六星,那么计数将清零。

#为节省篇幅,省略了卡池信息
import random
no_six_times=0#先决定结果的稀有度,如果不清楚为什么这么做,请参照第一期
a=random.choice(stars)if a==3:result=random.choice(three_stars)no_six_times+=1
elif a==4:result=random.choice(four_stars)no_six_times+=1
elif a==5:result=random.choice(five_stars)no_six_times+=1
else:result=random.choice(six_stars)
print(result)

我们来推算一下,按照抽奖规则,当你已经有50次没抽到时,下一次六星的概率将是4%,51次对应下一次6%,52次对应下一次8%…以此类推,直到98次未抽到时,下一次六星的概率为100%(建议使用初中一次函数的知识计算,比较方便)。也就是说,第99次必定抽到六星干员。这个逻辑一定要清楚,一个是98次之后的下一次抽奖六星的概率,另外一个是第99次必中,不是100的原因是因为六星本身就有2%的概率。

我们推算完以后,比较困难的是通过数据的变化体现概率提升,我打算这么做:50次以上时,每次没有抽到六星,就把stars里面的两个非6数字改成6,这样,提升了六星的概率,等量降低了低星的出现概率。

这个时候,stars中各项的顺序便尤为重要,如果把stars打乱,那么如果我们用stars[0]=6 stars[1]=6的话,可能因为这一项原来就是6,然后就覆盖掉了,这样也许不能达到提升概率的效果。所以我们要以星级升序排列,决不能让5星排在前面,这样会抽不到5星的,对客户是很不公平的。

我们可以这样提升概率:

while no_six_times>=50:stars[(No_six_times-50)*2]=6stars[(No_six_times-50)*2+1]=6no_six_times-=1

在这里给大家一些关于精确索引内容的经验:
1、要常常使用列表,而不是元组,集合,因为对列表的排序是原地进行的,而且它是有序序列,这样我们可以直接取第零项获取升序或降序的最值。
2、当你始终需要改变列表的最后一项时,常用-1索引。
3、通常在两个两个一改变的时候,最好想我使用上面的思路,这样只要小小的计算就行了。
4、在处理文件需要大型列表时,通常读取各行,形成由各行组成的列表,然后再在每一行识别段落、句子之类的,把每一行转换为列表,嵌套进原列表,也就是一个大列表包含各行,每一行又是一个列表,这样识别目标内容非常容易,比如file_lines[10][5]代表的就是第十行第五个字符或者单词。

假设你50次没抽到六星了,下一次六星的概率应该是4%,提升完概率真实的stars应该是这样:

stars=[6,6,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,6,6]
#前两个原本是3,提升概率后成了6,总的算下来是4%,符合预期

可是我们下一次抽奖的时候就出现了问题:提升完概率以后,no_six_times按照计算就变成49了(实际的次数应该是50),那如果用户按照提升后的概率还是没抽到六星,no_six_times变成了50(实际应次数为51),按照算法,提升后的stars还是上面的样子(实际应该是6%),之后次数又变回了49,这完全不符合我们的意愿,第51次没抽到之后,应该是第三项第四项变为6,我们应该将no_six_times分离为两个变量,一个用来提升概率(就是现在的no_six_stars),还有一个是用来反映真实次数的变量(我们把它命名为real_no_six)。

no_six_times=real_no_six
while No_six_times>=50:stars[(No_six_times-50)*2]=6stars[(No_six_times-50)*2+1]=6No_six_times=No_six_times-1

相应地,我们在抽奖的时候要把no_six_times+=1改成real_no_six+=1,因为前者仅仅用于控制卡池,给它加1没有实际意义,也和我们的预期不符。方便起见,我们把前面决定结果稀有度的变量a改为“choices”,注意:我们抽到六星干员后还要重置六星的概率,这个比较简单,只要还原初始那个列表就行了。如下:

if choices==6:result=random.choice(six_stars)real_no_six=0stars=[4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,5,5,5,5,5,5,5,5,6,6]print('已获得:%s\n恭喜寻访到六星干员!'%result)
elif choices==5:result=random.choice(five_stars)real_no_six+=1print('已获得:%s,恭喜寻访到五星干员\n距离上次寻访到六星干员已有%s次'%(result,real_no_six))
elif choices==4:result=random.choice(four_stars)real_no_six+=1print('已获得:%s,你寻访到了四星干员\n距离上次寻访到六星干员已有%s次'%(result,real_no_six))
else:result=random.choice(three_stars)No_six_stars_times=No_six_stars_times+1print('已获得:%s,真菜,你寻访到了三星干员\n距离上次寻访到六星干员已有%s次'%(result,real_no_six))

改完之后我们就可以正常运行程序,效果符合预期,也实现了一个变量用于控制概率,另一个变量反应真实数据。

2、“10次出一次”的实现

这里的“出一次”是指出一次五星或者六星,我们称之为“保底”,在这个程序里用“least”这个英文单词表明这个东西与保底有关,保底的这一次是没有三星四星干员的,那么到了那时,原本的stars池应该是这样:

least_stars=[5,5,5,5,5,5,5,5,6,6]

保底的那一次抽奖我们称为“触发保底机制”,10次中必有一次触发保底机制,一旦触发了,保底将结束,除非重新设置保底,否则保底机制不再会被触发。那我们该如何在十次内抽取一次触发保底呢?我们应该在“变化事件集”中,以可变概率抽取目标事件。什么意思呢?就是这样:

n=10#这个不能随便放在这里,应当放入一个函数,按照我们的控制设置保底
#我是为了让大家明白而放这里的a=random.randint(1,n)#randint函数是两头都包含在内的,1和10都包含
#randint在所给两个参数之间随机抽取一个整数if a!=1:#始终以1作为目标事件,也就是触发保底机制do_least=True
else:do_least=Falsen-=1if do_least:choices=random.choice(least_stars)
else:choices=random.choice(stars)#接下来就是之前介绍过的抽奖机制了

我们这里始终以1触发保底机制的原因很明确,假设你设置的不是1,而是5,那你还剩四次的时候,肯定是抽不到5的,那不就不能保证10次中一次了吗?

那当我们即拥有若干次数的保底,又有50次没抽到六星干员呢?(这种情况是可能的,就比如说你已经抽了50次,再去设置保底)如果下一次正好触发了保底机制,那我们不能按照stars里面进行常规概率提升了,我们应该对least_stars进行概率提升,由于least_stars里面的六星出率是20%,如果还是按照2%一加很麻烦且不太符合逻辑,所以在least_stars内我们可以20%地加。
我们需要给原来的代码进行改进,加上保底中六星概率提升的功能(此处将n重命名为least):

no_six_times=real_no_six
if least!=0:a=random.randint(1,least)if a==1:least=0do_least=Trueelse:least-=1do_least=False
while no_six_times>=50:if do_least:#这一块就是需要添加的东西least_stars[(no_six_times-50)*2]=6least_stars[(no_six_times-50)*2+1]=6else:stars[(no_six_times-50)*2]=6stars[(no_six_times-50)*2+1]=6no_six_times=no_six_times-1
if do_least:choices=random.choice(least_stars)
else:choices=random.choice(stars)

我们再把它整合成一个函数,定名single,表示单抽。

def single():global real_no_six,least,do_leastno_six_times=real_no_sixif least!=0:a=random.randint(1,least)if a==1:least=0#触发保底机制后清除剩余的次数do_least=Trueelse:least-=1do_least=Falsewhile no_six_times>=50:if do_least==True:least_stars[(no_six_times-50)*2]=6least_stars[(no_six_times-50)*2+1]=6else:stars[(no_six_times-50)*2]=6stars[(no_six_times-50)*2+1]=6no_six_times=no_six_times-1if do_least==True:choices=random.choice(least_stars)else:choices=random.choice(stars)if choices==6:result=random.choice(six_stars)real_no_six=0stars=[4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,5,5,5,5,5,5,5,5,6,6]do_least=False#无论通过什么方式抽到六星,都要解除保底least=0print(result)#这里简写了,下同elif choices==5:result=random.choice(five_stars)real_no_six+=1do_least=Falseleast=0print(result)elif choices==4:result=random.choice(four_stars)real_no_six+=1print(result)else:result=random.choice(three_stars)real_no_six+=1print(result)

设置和使用保底比前面的概率提升要简单的多,但是当有一个特殊情况时,会出现一个致命漏洞,我在某一次抽奖后遇到了报错:

>>> single()
Traceback(most recent call last): File "<pyshell#10>", line 19, in <module>:single() File H:\明日方舟干员寻访模拟器\主程序.py, line 14, in single:least_stars[(no_six_times-50)*2]=6
IndexError: list index out of range.
>>> real_no_six
56
>>> do_least
True

我们触发了保底机制,同时确实有50次没抽到六星了,结果却无法抽奖,我们来推算一下,56次没抽到六星,下次对应的将是stars[12]和stars[13]改为6,然后因为触发了保底,所以应该是least_stars[12]=6,然而,least_stars只有十项,所以会触发这个错误。经计算,54次以后的下一次如果触发保底,应该是100%出六星。所以我们只要做个判断,当real_no_six小于(等号可加可不加)54时就能避免错误且保证其他功能不受影响(以下是修正后的片段):

while no_six_times>=50:if do_least==True:if real_no_six<54:#加上这行least_stars[(no_six_times-50)*2]=6least_stars[(no_six_times-50)*2+1]=6else:stars[(no_six_times-50)*2]=6stars[(no_six_times-50)*2+1]=6no_six_times=no_six_times-1

那么single函数的基本任务就完成了,我们可以着手其他的功能了。下一期我们将继续实现标准卡池特定干员概率提升的相关功能,欢迎关注我的专栏:游戏抽奖模拟器专栏。我们下期再见。

快速跳转下一期:
Python实现《明日方舟》干员寻访模拟器第三期:原始但实用的卡池机制,不过要小心!

Python实现《明日方舟》干员寻访模拟器第二期:间接学习变化序列抽取目标事件和序列精确索引内容的经验相关推荐

  1. Python实现《明日方舟》干员寻访模拟器第三期:原始但实用的卡池机制,不过要小心!

    上一期文章我们实现了比较完善的单次抽奖函数,成功地在默认卡池中抽取单个结果,并且解决了一个微小但致命的问题,今天我们着手进行<明日方舟>干员寻访模拟器的实现,如果您忘记了上一期的内容,或者 ...

  2. 基于python的明日方舟自动刷取理智的脚本(一)—— 初始化环境

    写在前面的话 身为一名计算机专业的懒鬼和明日方舟三年老博士(悲,在学习编程之后便一直有想法做一个脚本来解放我的双手.奈何此前一直没有多余的时间,放了假也是泡在网吧里.这学期接触了pyautogui库后 ...

  3. 明日方舟抽卡模拟器wiki_明日方舟wiki抽卡模拟器安卓手机下载-游戏大玩家

    明日方舟wiki抽卡模拟器这是一款由明日方舟衍生出来的全新游戏,这个游戏主要是针对游戏内的抽卡玩法而独自打造的一个模拟器,明日方舟的玩家可能会觉得游戏内的卡片资源非常的少,没有关系,在这款全新的模拟器 ...

  4. python读取文件夹下所有文件的名称_python2.7 学习之读取文件夹下所有文件名称及内容...

    python2.7 学习之读取文件夹下所有文件名称及内容 最近稍稍有点空闲时间, 于是重新温习了一下之前学习过的 python 基础废话不多说, 记录一下自己的所得 首先, 安装什么的不在本人的温习范 ...

  5. 用python求期望_用Python计算明日方舟2021龙门幸运墙期望

    按照去年的惯例,方舟今年春节的时候也整了个红包盲盒. 比起去年简单粗暴的直接送,今年的盲盒实际上增加了两层隐性的保底机制:第一层是每天有两次机会而非一次,两次尝试取收益更高的结果:第二层是如果不幸成为 ...

  6. Python实现复杂规则游戏抽奖模拟器第一期:间接学习random模块函数

    模拟游戏的抽奖对于揣测游戏稀有物资的出率有较大的意义,通过编写模拟抽奖之类的程序,我们也能够对概率之类的东西有更深的了解,今天我们将开始编写对游戏<明日方舟>的干员寻访进行模拟的一个程序. ...

  7. 从明日方舟入手数据统计--盒须图

    (封面源自必应,侵删)(多图预警) 由于此文的目的是数据统计学习而非游戏攻略且时间仓促,文中所用数据皆出自题主自己的明日方舟干员库,可能与实际数据有所出入.如需要权威数据请去明日方舟官方wiki 引子 ...

  8. 明日方舟抽卡模拟器wiki_明日方舟抽卡模拟器wiki

    明日方舟抽卡模拟器wikiapp是一款明日方舟抽卡模拟器,明日方舟抽卡模拟器wikiapp完全还原了游戏中的抽卡场景,带给玩家们一个真实的体验感,有十连抽和单抽大家可以根据自己的需求选择,没有任何的消 ...

  9. 明日方舟如何刷初始号,明日方舟如何刷初始号教程

    相信大部分玩过明日方舟的玩家朋友都知道明日方舟可以刷初始,新手玩家刷到一个好的初始号可以比较轻松的度过新手期. 以前都是建立游客号过新手教程后刷初始的,但是现在明日方舟识别手机设备ID,导致15天内只 ...

最新文章

  1. ios仿淘宝管理收货地址demo
  2. ERROR in ./node_modules/element-ui/lib/theme-chalk/fonts/element-icons.ttf 1:0 Module parse failed:
  3. MySQL 数据库慢查询日志分析脚本
  4. 基于FPGA的目标点的提取与定位系统设计
  5. adb install 和adb uninstall
  6. myeclipse 9.0安装 vss1.6.2不显示问题
  7. java实现map和object互转
  8. DDOS的攻击原理和防护指南
  9. python 报错继续执行_Python报错不要慌,这三个关键词帮你解决问题!
  10. 我的世界java版做船_“不要在云了,船的合成用木铲?”我的世界:Java和基岩版的差异...
  11. 音频插件安装教程,Arturia Prophet V3 Mac安装说明
  12. 最近我一个朋友在职场上陷入了迷茫
  13. 机器学习/深度学习测试题(二)—— 单层线性神经网络求解异或问题
  14. 【bootstrap】-----使用核心
  15. 使用OpenSSL转换X509 PEM与PFX证书
  16. 解析阿里云V3版本的专有云网络架构原理,构架图图解
  17. Materials studio中的简单聚合物的建立及盒子的弛豫
  18. selenium 如何使浏览器不加载图片和CSS???
  19. 地图编辑器到底是做什么的?今天之后你会对此有个新的了解
  20. 2020 全国大学生数学建模竞赛C题思路+代码

热门文章

  1. oracle中 awr,ORACLE AWR(AWR、SNAPSHOT、BASELINE)
  2. php bigpipe实现,如何通过php实现BigPipe分块输出
  3. Peer Effects Report(博弈论+机制设计)
  4. Si3N4-BN氮化硅-氮化硼系复合材料|α-Si3N4(w)/RBSN复合材料|β-SiC(w)/Si3N4复合材料|金属镍-氮化硅陶瓷复合材料 氮化物
  5. Antd 导航菜单使用iconfont
  6. UOS 移植 Flash Player 页游微端
  7. 突然就想起了那个海边
  8. 学习Python不用愁,五个阶段助你快速拿高薪工作
  9. getservletcontext().getrealpath
  10. WX1860AL4---千兆专用类芯片,参数