中关村在线

游戏

幻塔抽卡机制深度解析

初探幻塔抽卡规则与概率机制

===引子===

最近在研究各类抽卡游戏的机制设计,收集不同系统的优点,以便优化和完善自己的分析工具包,已考察多个游戏的抽卡规则。

来看看幻塔的抽卡机制如何。

官方公布武器概率信息

金核与赤核抽取SSR武器的基础概率为0.75%,综合概率达2%,包含80抽必得保底机制。

金核与赤核抽卡中,SR武器基础出率为1%,计入十连保底后综合概率达12%。

按SSR概率设定,前79次抽卡每次为0.75%,第80次必出,计算得综合概率约为1.990625%,接近官方公布的2%。若将前79次概率微调至0.76%,综合概率则升至2.0005%,更贴近2%。因此推测实际设定可能为0.76%,但出于宣传考虑,取整为0.75%以便与5的倍数对齐,真实机制或略有调整。

对SR不太理解,便观察了一些主播抽卡,人工整理了部分数据。

8 7 7 7 7 8 8 10 10 10 9 9 8 9 9 8 9 10 9 8 9 9 9 9 8 9 9 10 8 9 9 1 1 8 8 9 4 3 8 3 10 8 9 9 9 8 9 8 9 9 8 9 9 8 8 9 10 9 8 8 9 9 9 8 8 9 9 10 9 10 8 8 1 9 9 8 9 9 9 9 10 6 2 9 9 8 8 8 8 8 9 8 8 8 3 5 10 8 9 8 10 9 10 8 9 2 6 8 10 9 8 8 9 9 8 10 10 9 8 8 10 8 9 10 8 9 8 10 9 10 1 8 10 10 10 10 5 3 8 9 10 8 9 10 10 9 8 9 9 9 10 10 7 1 10 10

从第8次抽取起,SR的获取概率开始逐步上升。由于数据有限且关注度不高,大致估算每抽概率增加0.33%,使整体概率达到11.7%,中橙色线所示。

===采用模型===

SSR概率模型与官方公布一致,前79抽每抽0.75%,第80抽必定获得。

SR概率调整:1至7次均为1%,第8次34%,第9次67%。

由于幻塔的抽卡机制具有保底重置特性(抽出SSR后重置),且存在铸金兑换规则,原有基于道具获取即重置保底假设的计算工具已不适用,必须设计专门的动态规划算法,才能准确处理这一复杂情况。

在群友帮助下得知,贴吧已有用户(immortalcow)编写了相关DP代码,并建立了代码仓库。

检查无误,所有情况均已涵盖,稍作修改加入SR概率递增后,直接用于绘图,省去重写步骤。

暂时没写意志抽取部分,现在懒得弄,以后有空再修改完善。

抽卡记录不足,只能分析到这种程度了。

===其他小点===

当SSR数量达到7个后,继续抽取相同SSR仍返还2个铸金。假设SSR综合概率为2%,SR综合概率为12%,且SR已全部集齐。此时的期望指在抽卡次数趋于无穷时,获得每个道具所需的平均抽卡次数。

在限定条件下讨论(现实中每次抽卡次数有限,结果通常比理论更差)。

获取SSR的途径中,42.51%来自80抽保底,32.3%通过120铸金兑换,另有25.19%出自非保底区域。

限定SSR的获取来源中,80抽保底占32.13%,120铸金兑换占48.84%,非保底区域占19.03%。

在限定概率下,每个SSR的期望抽取次数为34次,限定SSR则为51.406次。该数值明显低于实际所需抽卡数,具体情况可参考对应表格。目前发现可能存在细微误差,后续将进行核查修正。

这个限制条件对普通玩家抽卡参考价值不大,建议参考上方图表。若未在120次内抽中保底,将会非常不划算。

===代码===

code

[code=py]

import numpy

import pandas

pandas.set_option("display.max_rows",1000)

import matplotlib

from matplotlib import pyplot as plt

SSR_PROBABILITY_BASIC=0.0075

SR_PROBABILITY_BASIC=0.01

SSR_PROBABILITY_CRITICAL=0.5

SSR_INSURANCE_LIMIT=80

SR_INSURANCE_LIMIT=10

SSR_PRICE_GOLD_CAST=120

SSR_MAX_COUNT=7

def AddOrUpdate(state,key,value):

if key in state:

state[key]+=value

else:

state[key]=value

def ProcessState(ssr_count,ssr_insurance,sr_insurance,gold_cast):

if gold_cast>=SSR_PRICE_GOLD_CAST:

ssr_count+=gold_cast//SSR_PRICE_GOLD_CAST

gold_cast%=SSR_PRICE_GOLD_CAST

return (SSR_MAX_COUNT,0,0,0)

return ssr_count,ssr_insurance,sr_insurance,gold_cast

def Transfer(state):

result={}

for (ssr_count,ssr_insurance,sr_insurance,gold_cast),probability in state.items():

if ssr_count>=SSR_MAX_COUNT:

AddOrUpdate(result,(SSR_MAX_COUNT,0,0,0),probability)

continue

ssr_insurance+=1

sr_insurance+=1

if ssr_insurance>=SSR_INSURANCE_LIMIT:

AddOrUpdate(result,ProcessState(ssr_count+1,0,0,gold_cast+1),probability*SSR_PROBABILITY_CRITICAL)

AddOrUpdate(result,ProcessState(ssr_count,0,0,gold_cast+1),probability*(1-SSR_PROBABILITY_CRITICAL))

continue

AddOrUpdate(result,ProcessState(ssr_count+1,ssr_insurance,0,gold_cast+1),probability*SSR_PROBABILITY_BASIC*SSR_PROBABILITY_CRITICAL)

AddOrUpdate(result,ProcessState(ssr_count,ssr_insurance,0,gold_cast+1),probability*SSR_PROBABILITY_BASIC*(1-SSR_PROBABILITY_CRITICAL))

if sr_insurance>=SR_INSURANCE_LIMIT:

AddOrUpdate(result,ProcessState(ssr_count,ssr_insurance,0,gold_cast+2),probability*(1-SSR_PROBABILITY_BASIC))

continue

sr_rate_now = SR_PROBABILITY_BASIC

if sr_insurance == 9:

sr_rate_now = 0.66+0.01

if sr_insurance == 8:

sr_rate_now = 0.33+0.01

AddOrUpdate(result,ProcessState(ssr_count,ssr_insurance,0,gold_cast+2),probability*(1-SSR_PROBABILITY_BASIC)*sr_rate_now)

AddOrUpdate(result,ProcessState(ssr_count,ssr_insurance,sr_insurance,gold_cast+1),probability*(1-SSR_PROBABILITY_BASIC)*(1-sr_rate_now))

return result

def GetDistribute(state):

result=[0]*(SSR_MAX_COUNT)

for (ssr_count,_,__,___),probability in state.items():

for i in range(ssr_count):

result+=probability

return result

def CalculateSteps(max_step,ssr_insurance=0,sr_insurance=0,gold_cast=0):

results=[{(0,ssr_insurance,sr_insurance,gold_cast):1}]

for _ in range(max_step):

results.append(Transfer(results[-1]))

results[-2]=GetDistribute(results[-2])

for i,j in results[-1].items():

if i[0]==0:

print(i,j)

results[-1]=GetDistribute(results[-1])

return pandas.DataFrame(results,columns=range(1,SSR_MAX_COUNT+1))

final_result=CalculateSteps(MAX_STEPS,SSR_INSURANCE,SR_INSURANCE,GOLD_CAST)

from copy import deepcopy

import matplotlib.cm as cm

import numpy as np

import GGanalysis as gg

from GGanalysis.gacha_plot import quantile_function

def get_most_pull(n):

core = 0

pull = 0

items = 0

while True:

pull += 1

core += 1

if pull % SSR_INSURANCE_LIMIT == 0 or pull % SR_INSURANCE_LIMIT == 0:

core += 1

if core >= SSR_PRICE_GOLD_CAST:

core %= SSR_PRICE_GOLD_CAST

items += 1

if items >= n:

return pull

ans_list = [gg.finite_dist_1D([1])]

for i in range(1, SSR_MAX_COUNT+1):

ans = deepcopy(final_result.values)

ans[1:] = ans[:-1]

dist = final_result.values - ans

dist = dist[:get_most_pull(i)+1]

dist = gg.finite_dist_1D(dist)

dist.p_normalization()

print(i, dist.exp, dist.exp/i)

ans_list.append(dist)

def Hotta_num(x):

return str(x-1)+星

Hotta_fig = quantile_function(

ans_list,

幻塔限定SSR武器抽取概率说明

item_name=限定SSR武器,

本图展示玩家SR角色已全部满破,并计入铸金兑换情况,模型仅供参考,不保证完全准确。因存在铸金机制,期望值参考意义有限。如需估算抽取次数,建议结合图示概率查看。当前条件下,获取单个道具最多需110抽,集齐七个道具最多需764抽。计算由@immortalcow完成,绘图出自@一棵平衡树之手。

text_tail=,

max_pull=675,

mark_func=Hotta_num,

line_colors=0.5*(cm.Blues(np.linspace(0.1, 1, SSR_MAX_COUNT+1))+cm.Greys(np.linspace(0.4, 1, SSR_MAX_COUNT+1))),

y_base_gap=25,

y2x_base=3,

mark_exp=False,

mark_max_pull=False,

is_finite=True)

Hotta_fig.show_figure(dpi=300, savefig=True)

[/code]

展开全文
人赞过该文
内容纠错

相关电商优惠

任天堂Switch 2

任天堂Switch 2

4029
49人评分
100%好评
小霸王Q900

小霸王Q900

708
54人评分
99%好评
任天堂Switch Lite

任天堂Switch Lite

1063
52人评分
97%好评
任天堂Switch OLED版

任天堂Switch OLED版

2588
1636人评分
99%好评

评论

更多评论
还没有人评论~ 快来抢沙发吧~

读过此文的还读过

点击加载更多

内容相关产品

说点什么吧~ 0

发评论,赚金豆

收藏 0 分享
首页查报价问答论坛下载手机笔记本游戏硬件数码影音家用电器办公打印 更多

更多频道

频道导航
辅助工具