许可优化
许可优化
产品
产品
解决方案
解决方案
服务支持
服务支持
关于
关于
软件库
当前位置:服务支持 >  软件文章 >  荒野乱斗玩家杯数分布模拟及分析matlab

荒野乱斗玩家杯数分布模拟及分析matlab

阅读数 10
点赞 0
article_banner


前段时间一直在尝试在荒野乱斗中用柯尔特3v3野牌上红,无奈实力有限,上分十分艰难,在痛苦之中,想搞清楚野牌1000分究竟什么实力,遂用MATLAB建立一个动画演示小程序。代码末尾给出,注释很详细,机制实现的细节也在注释中。

首先给不玩荒野乱斗的读者简单介绍一下情况,荒野乱斗是在线手机竞技游戏,玩家的每个英雄有一个独立的杯数,相当于天梯中的分数。它有多种模式,但这里我的模型是1v1,因为无论几v几都不影响结果。它与传统的天梯排位的分数加减规则不同,它不是按照匹配双方的差距决定奖惩分数,而是根据分数段位固定奖惩分数,具体见下表

可以看到分数越高越难上分。此外还有一个连胜奖励,连胜次数1-5次,则额外奖励1-5分,超过5次也只奖励5分。

在我建立的模型中,我设置了一千名玩家,他们的实力正态分布,且游戏意愿相同,都是一刻不停地排位。匹配机制是随机抽取两人,如果两人分数差在阈值以内,则匹配成功,否则重新抽取。胜负判断是根据双方实力差,实力差距过大则必胜必败,差距不大则由概率控制。

经过较长时间的演化,分数分布如下

可见1000分算是前10%了,不错不错。但实际上感觉肯定没有10%这么大,因为这个模型是假定所有人一直匹配,实际上不同分段的玩家匹配意愿不一样,而且3v3的话组队更有优势。

到这里有一个疑问,既然1000分附近赢一盘+6,+5,输一盘-10,-11,不考虑连胜奖励的话,要维持在1000分,胜率必须在0.6。但是既然我跟同分数段的玩家匹配胜率在0.6,那不就意味着我比他们强吗?凭啥我只能维持在这个分数?给大家一分钟时间思考。提示:与毕导最近的视频的思想有异曲同工之妙。毕导相关视频​

答案就是,900-1000分与1000-1100分的玩家并不一样多,前者更多,所以你更有机会匹配到前者,然而前者比你更弱,所以假设分数匹配差别阈值为100分,你打900-1000分必胜,1000-1100分必败的话,两个区段玩家数量比是3:2的话,你确实能维持在60%胜率,但这并不是与你同水平的玩家匹配的胜率,所以你确实属于这个分数。

进一步想,荒野乱斗每个分数段加减分数都是一定的,所以维持在这个分数所需的胜率是一定的,而有更具刚才的推论,这个胜率决定了分布上该点做两右侧100分段内的人数比,也就是该点的导数,那么稳定分布的形状就是确定的?确实如此,可以看到图中900-1000分的玩家与1000-1100分的玩家数量接近3:2,1000分附近玩家的胜率确实是0.6.(误差来源于没考虑连胜奖励以及概率带来的扰动)

那么这个分布可以稳定吗?其实只能相对稳定,因为只要你胜率大于0.8,你就可以一直上分,所以那些顶尖玩家的分数是没有上限的,而小于0.8胜率的玩家确实可以在1200分以下形成一个稳定分布,这个分布形状确定。足够长时间后,只有一位超级大佬能维持在0.8以上胜率。

本文章纯属一乐,模拟结果不能反映实际情况,主要是为大家提供一个新的数学理解以及一个可以看乐子的代码。

感谢mzw提供的数学灵感以及代码体验反馈。本文没有受到任何基金的支持。

附录:MATLAB代码

% 这是模拟荒野乱斗玩家分数分布情况的代码。可能由CHATGPT辅助完成,不保证正确性。
% 此代码前面有简短的引言注释,主体部分是一个循环,循环完成的任务有进行匹配、分数计算、 % 数据统计、可视化等等。为了可读性,许多代码进行了function封装放于末尾。 % 作者:zky,完成时间:2024年8月18日。 %--------------------------------------------------------------------------- % 荒野乱斗是一个排位游戏,每个英雄有独立的杯数(分数),有多种模式。 % 此代码仅考虑每个玩家只有一个分数,且游戏意愿相同,都是一刻不停地排位,只进行1v1对抗。 % 玩家之间的实力是正态分布的,且不会变化,用变量players_strength描述。 % 玩家的分数由变量players_scores描述。 % 玩家获胜与失败会对分数进行加减,且分数越高,赢了加得越少,输了减得越多。 % 玩家根据分数进行匹配,分数差别过大不会匹配到一起。但如果此分数段玩家较少,匹配时间会增大。 % 胜负方式依据双方玩家的实力players_strength进行判断。 % 分数结算规则: % 800分以下获胜加8分,800-900加7分,依次递减,直到1200分以上只加3分。 % 50分以下输不扣分,50-99扣1分,100-1100每隔100分多扣一分 % 直到1100分以上扣12分。 % 除此之外还有连胜机制:当前胜利若连胜n次,n在1-5内,则额外加n分,n>5只加5分。 %--------------------------------------------------------------------------- close all; % 关闭所有MATLAB图像窗口 n = 1000; % 玩家数量(影响运算速度) % 生成标准正态分布的玩家实力数组 mu = 0; % 实力均值 sigma = 1; % 实力标准差 players_strength = normrnd(mu, sigma, [1, n]); players_strength = sort(players_strength, 'descend'); % 从大到小排序 % 初始化玩家的各种数据 players_scores = zeros(1, n); % 玩家分数初始化为0 matches_played = zeros(1, n); % 每个玩家参与的对局数初始化 wins = zeros(1, n); % 每个玩家的胜利次数 winning_streaks = zeros(1, n); % 每个玩家的连胜次数 invalid_matches = zeros(1, n); % 初始化每个玩家的连续无效匹配次数 % 动画效果:不停刷新图像窗口,直到窗口关闭或达到循环上限。 num_turns = 6000000000; % 循环次数上限 update_interval = 10000; % 每多少次循环更新一次动画 figureHandle = figure; % 初始化图形窗口 % 主体循环 for i = 1:num_turns idx = randperm(n, 2); % 从所有玩家中抽取2人 % 成功匹配的分数差距阈值,初始为100分,匹配失败次数越多,条件越宽松。 % 假设玩家分数均匀分布在1000分内,抽中间玩家,再抽附近100分玩家概率为0.2。 % 记录每人的连续无效匹配次数(抽中但分数差不在阈值内的次数),当次数达50, % 就将连续无效匹配次数除以50取整。被抽中两人的整数和乘以100,作为阈值放宽的大小。 % 阈值可自由修改,但严重影响胜率,阈值越大,分布越往右移。 nn = sum(floor(invalid_matches(idx) / 50)); % 计算分数差距放宽程度 score_diff_limit = 100 + 100 * nn; % 分数差距阈值 score_diff = max(players_scores(idx)) - min(players_scores(idx)); % 计算分数差距 if score_diff <= score_diff_limit % 匹配成功 % 计算相对实力差距 relative_strength_diff = players_strength(idx(1)) - players_strength(idx(2)); invalid_matches(idx) = 0; % 重置无效匹配次数 matches_played(idx) = matches_played(idx) + 1; % 更新对局数 % 调用更新分数和状态的函数 [players_scores, wins, winning_streaks] = update_scores(... players_scores, wins, winning_streaks, idx, relative_strength_diff); else % 未成功匹配,更新无效匹配次数 invalid_matches(idx) = invalid_matches(idx) + 1; end % 可视化部分 if mod(i, update_interval) == 0 if ~isvalid(figureHandle) break; % 如果窗口被关闭,则退出循环 end % 调用可视化函数 visualize(players_scores, wins, matches_played, winning_streaks); end end %--------------------------------------------------------------------------- % 自定义函数部分 % 胜负判定函数 function [players_scores, wins, winning_streaks] = update_scores(... players_scores, wins, winning_streaks, idx, relative_strength_diff) % 双方实力差距为1则必胜,-1必败,差距在其间则胜率线性递增。 % 将实力差距等比缩放到(0,1)内,方便与随机数rand比较。 win_probability = relative_strength_diff / 2 + 0.5; if rand < win_probability % 1胜出 % 调用分数处理函数process_win [players_scores, wins, winning_streaks] = process_win(... players_scores, wins, winning_streaks, idx(1), idx(2)); else % 2胜出 [players_scores, wins, winning_streaks] = process_win(... players_scores, wins, winning_streaks, idx(2), idx(1)); end end % 分数处理函数 function [players_scores, wins, winning_streaks] = process_win(... players_scores, wins, winning_streaks, winner_idx, loser_idx) % 连胜奖励,调用连胜计算函数winning_streak_bonus bonus = winning_streak_bonus(winning_streaks(winner_idx)); % 胜者分数变换 players_scores(winner_idx) = players_scores(winner_idx) + ... results_win(players_scores(winner_idx)) + bonus; % 败者分数变换 players_scores(loser_idx) = players_scores(loser_idx) - ... results_lose(players_scores(loser_idx)); wins(winner_idx) = wins(winner_idx) + 1; % 胜者胜利数+1 winning_streaks(winner_idx) = winning_streaks(winner_idx) + 1; % 连胜数+1 winning_streaks(loser_idx) = 0; % 败者连胜数清零 end % 连胜计算函数 function bonus = winning_streak_bonus(streaks) bonus = min(streaks, 5); % 连胜1-5次则额外加1-5分,大于5次只加5分 end % 获胜分数加表 function change = results_win(scores) change = zeros(size(scores)); change(scores < 800) = 8; change(scores >= 800 & scores < 900) = 7; change(scores >= 900 & scores < 1000) = 6; change(scores >= 1000 & scores < 1100) = 5; change(scores >= 1100 & scores < 1200) = 4; change(scores >= 1200) = 3; end % 失败分数减表 % 与上面一分一段分别赋值等价,只不过这里用floor函数写得更紧凑 function change = results_lose(scores) change = zeros(size(scores)); change(scores >= 50 & scores < 99) = 1; change(scores < 1100) = floor(scores(scores < 1100) / 100)+1; change(scores >= 1100) = 12; end % 可视化函数 function visualize(players_scores, wins, matches_played, winning_streaks) clf; % 清除原来图像,否则文字会重影 subplot(2, 1, 1); histogram(players_scores, 'BinWidth', 100); % 最后一个数字控制直方图长条宽度 title('玩家分数分布图'); xlabel('分数'); ylabel('玩家数量'); over_1000 = mean(players_scores > 1000); % 1000分以上玩家占比 over_1250 = mean(players_scores > 1250); % 1250分以上玩家占比 % 1000分附近玩家胜率 win_rate_1000 = mean(wins((players_scores >= 950 & players_scores <= 1050)) ./ ... matches_played((players_scores >= 950 & players_scores <= 1050))); % 1250分附近玩家胜率 win_rate_1250 = mean(wins((players_scores >= 1200 & players_scores <= 1300)) ./ ... matches_played((players_scores >= 1200 & players_scores <= 1300))); % 如果分母为0的话,输出0 win_rate_1000(isnan(win_rate_1000)) = 0; win_rate_1250(isnan(win_rate_1250)) = 0; % 寻找胜率前三玩家索引 win_rates = wins ./ matches_played; win_rates(isnan(win_rates)) = 0; [~, idx_top3] = maxk(win_rates, 3); top3_win_rates = win_rates(idx_top3); subplot(2, 1, 2); axis off; text(0, 0.5, sprintf(['玩家平均对局数:%.2f\n1000分以上玩家占比: %.2f%%\n' ... '1250分以上玩家占比: %.2f%%\n1000分附近玩家胜率: %.2f\n1250分附近玩家胜率: %.2f\n' ... '胜率前三玩家的编号 [%d, %d, %d]\n胜率分别为 [%.3f, %.3f, %.3f]\n分数分别为 [%d, %d, %d]\n' ... '已匹配局数分别为 [%d, %d, %d]\n连胜次数分别为 [%d, %d, %d]'], mean(matches_played), ... over_1000 * 100, over_1250 * 100, win_rate_1000, win_rate_1250, idx_top3(1), ... idx_top3(2), idx_top3(3), top3_win_rates(1), top3_win_rates(2), top3_win_rates(3), ... players_scores(idx_top3(1)), players_scores(idx_top3(2)), players_scores(idx_top3(3)), ... matches_played(idx_top3(1)), matches_played(idx_top3(2)), matches_played(idx_top3(3)), ... winning_streaks(idx_top3(1)), winning_streaks(idx_top3(2)), winning_streaks(idx_top3(3))), ... 'FontSize', 12, 'Color', 'black'); drawnow; %pause(0.01); % 在每帧动画中插入暂停时间 end



免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删

相关文章
技术文档
QR Code
微信扫一扫,欢迎咨询~
customer

online

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 board-phone 155-2731-8020
close1
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

姓名不为空

姓名不为空
手机不正确

手机不正确

手机不正确
公司不为空

公司不为空

公司不为空