Press "Enter" to skip to content

如何为2v2游戏创建数据驱动的Elo评级系统

将数学放在桌面上:从算法到乒乓疯狂,追寻终极办公室冠军。

Photo by Pascal Swier on Unsplash

嗨,欢迎!

我叫Lazare,刚刚完成了商业数据分析的第二学士学位。本文是基于我本科论文的工作。

从友谊赛到激烈的竞争,乒乓球在企业文化中找到了自己的定位,为团队提供了一种独特的连接和竞争方式。

本文探讨了一个基于2v2 Elo评分系统的数学原理,该系统可以应用于乒乓球或任何其他2v2游戏。本文还讨论了支持数据处理的体系结构,并介绍了使用Python创建实时排名和数据分析的Web应用程序的过程。

Elo排名

Elo评级系统是一种用于确定零和博弈中玩家相对技能水平的方法。它最初用于国际象棋,但现在也被应用于其他各种体育项目,如棒球、篮球、各种棋类游戏和电子竞技。

这个系统在国际象棋中有一个广为人知的例子,它被用来对世界各地的棋手进行排名。马格努斯·卡尔森,也被称为“国际象棋的莫扎特”,在2023年拥有世界最高的Elo评分,达到2853分,展示了他在这项游戏中的非凡技巧。

Elo评分公式分为两部分:首先,它计算给定一组玩家的预期结果,然后根据比赛结果和预期结果确定评分调整

预期结果计算

考虑以下国际象棋的例子,玩家A和玩家B的评分分别为R𝖠和R𝖡。对于玩家A对阵玩家B的预期得分方程如下:

如何为2v2游戏创建数据驱动的Elo评级系统 四海 第2张

Elo算法使用一个可调整的变量来控制胜利概率如何受到玩家评分的影响。在这个例子中,它设定为400,这是大多数体育项目,包括国际象棋的典型值。

现在让我们看一个更现实的例子,玩家A的评分为1500,玩家B的评分为1200。

同样的方程可以计算玩家A对阵玩家B的预期得分:

如何为2v2游戏创建数据驱动的Elo评级系统 四海 第3张

通过这个计算,我们知道玩家A对阵玩家B有84.9%的胜率。

要找到玩家B对阵玩家A的预计获胜概率,使用同样的公式,但是反转评分的顺序:

如何为2v2游戏创建数据驱动的Elo评级系统 四海 第4张

玩家A获胜的概率和玩家B获胜的概率之和等于1(0.849 + 0.151 = 1)。在这种情况下,玩家A有84.9%的胜率,而玩家B只有15.1%的胜率。

评分计算

胜者和败者之间的评分差异决定了每场比赛后获得或失去的总积分。

  • 如果一个Elo评分较高的玩家获胜,他们将获得较少的积分,而他们的对手在失败中也只会失去一些积分。
  • 相反,如果排名较低的玩家获胜,这个成就被认为更加重要,因此奖励更大,而排名较高的对手则会受到相应的惩罚。

计算玩家A对战玩家B的新评分的公式如下:

如何为2v2游戏创建数据驱动的Elo评级系统 四海 第5张

在这个公式中,(S𝖠 — E𝖠)代表玩家A的实际得分与预期得分之间的差异。附加变量K确定了在一场比赛后玩家的评分可以改变多少。在国际象棋中,这个变量被设定为32。

如果玩家A获胜,则实际得分(本例中为1)将大于预期得分0.849,从而产生正的差异。

这表明玩家A的表现比最初预期的要好。因此,Elo评分系统重新校准了两名玩家的评分:

  • 玩家A的评分会因为胜利而增加
  • 玩家B的评分会因为失败而减少

同样,这个方程也可以计算出玩家A和玩家B的新评分:

如何为2v2游戏创建数据驱动的Elo评级系统 四海 第6张

总之,Elo评分系统为评估和比较玩家的技能提供了一种强大而高效的方法。它在每场比赛后持续更新玩家的评分,考虑到两个对手之间的技能差异。

这种方法奖励冒险精神,因为击败一个比自己评分高的玩家将会导致玩家评分更大幅度的增加,如下表所示:

图I:国际象棋中Elo系统的示例 | 作者的表格

另一方面,如果一个评分较高的玩家冒着输掉的可能性并输给一个评分较低的玩家,他们的评分将会受到显著影响:他们会失去更多积分,而对手则会获得更多积分。

总之,当一个玩家赢得一场比赛时,他们的获胜概率越低,他们可以获得的积分就越高。

目前这个评分公式原本是为国际象棋设计的,对于桌上足球来说并不完全适用。

事实上,桌上足球比国际象棋更复杂,包括以下更多的变量:

  • 它是一个由两个团队组成的四人游戏(2v2)
  • 每个团队成员可以对其队友产生积极或消极的影响
  • 与国际象棋的二元结果不同,桌上足球的胜利或失败程度可以根据团队的得分差异而大幅变化

评分算法探索

重点是将Elo评分系统适应到涉及四名玩家分为两个团队的桌上足球比赛的独特要求上。

获胜概率

为了开始计算新的玩家评分,需要建立一个经过改进的公式来确定涉及四名玩家分为两个团队的比赛的预期结果。

为了说明这一点,考虑一个假设的四人桌上足球比赛场景:玩家1、玩家2、玩家3和玩家4,每个人都有不同的评分代表他们的技能水平。

图II:涉及4名玩家的桌上足球比赛场景 | 作者的表格

为了计算修订的Elo评分系统中团队1对团队2的预期得分,需要确定参与游戏的每个玩家的预期评分。

玩家1的预期评分,表示为E𝖯𝟣,可以通过使用Elo评分公式计算每个对手的评分之和的平均值来计算,如下所示:

如何为2v2游戏创建数据驱动的Elo评级系统 四海 第9张

经过广泛测试,决定对预期分数公式进行调整,将用于除以等级差的变量设置为500,而不是象棋中使用的传统值400。这个增加的值意味着玩家的等级将对他们的预期分数产生较小的影响。

这个调整的一个主要原因是,与象棋不同,桌上足球中存在一定的运气因素。通过使用500,可以更准确地预测比赛结果,并开发出可靠的评级系统。

计算玩家2对玩家3和玩家4的预期分数E𝖯𝟤的方法与计算玩家1的方法相同,可以采用相同的方法。

团队的预期分数E𝖳𝟣可以通过取E𝖯𝟣和E𝖯𝟤的平均值来计算:

如何为2v2游戏创建数据驱动的Elo评级系统 四海 第10张

计算每个玩家的预期分数后,可以用它们来计算比赛的结果。预期分数最高的团队更有可能获胜。通过对每个团队成员的预期分数求平均值,可以解决团队内技能差异的问题!

下表显示了玩家1和玩家2对玩家3和玩家4的预期分数。

  • P1对P3和P4的预期分数分别为0.091和0.201,对应于14.6%的获胜机会
  • P2对P3和P4的预期分数分别为0.201和0.387,总的获胜概率为29.4%
  • 对于P1来说,与像P2这样的更强的玩家合作可以增加他们的总体获胜机会,如22%
图III:根据图II中显示的情景的预期得分|作者的表格

如果P1和P2的团队获胜,P1获得的积分比他们的个人预期得分所示的要少,因为排名较高的P2也对胜利作出了贡献,并降低了他们的总体获胜概率。

另一方面,P2由于有一个排名较低的队友而获得更多积分。在获胜的情况下,P2因为冒险而获得了奖励,而P1获得的积分较少,因为假设P2对胜利的贡献更大,如果他们输了,则反之亦然。

评分参数

现在可以将四人比赛的预期结果纳入考虑多个影响比赛和玩家评级的变量的新公式中。

如前所述,K值可以根据评级系统的需求进行修改。这个新公式考虑了每个玩家所玩的比赛次数,反映了他们的资深程度以及比赛的结果。

例如,在2014年世界杯半决赛中,德国以7-1的比分击败了巴西。这是世界杯历史上最令人震惊和耻辱的结果之一,因为巴西是东道主,并且自1975年以来从未在主场输过一场竞争比赛。

如果我们将评级系统应用于这场比赛,我们会预计德国将获得大量积分,而巴西将失去大量积分,反映出他们的表现和技能水平的差异。

K-值在这种情况下,用K𝟣表示的K-值决定了一场比赛后玩家的评级将发生多大变化。这个修订后的K值考虑了玩家玩的比赛数量,以平衡每场比赛对他们评级的影响。经过多次测试,为每个玩家计算K值开发出了一个公式。

对于玩家1,这表示为:

如何为2v2游戏创建数据驱动的Elo评级系统 四海 第12张

这个K值的公式旨在对新玩家的评分产生更大的影响,同时为经验丰富的玩家提供稳定性和较少的评分波动。具体来说,在玩了300场比赛之后,玩家的评分更能代表他们的技能水平。

图 IV:随时间变化的K值 | 图表由作者提供

图 IV 显示了玩的场次对K值的影响。从50开始,该图表显示随着玩的场次增加,K值逐渐减小,玩了300场比赛后,K值减半为25。这确保了随着经验的增加,每场比赛对玩家评分的影响逐渐减少。

积分因子为了考虑每个团队得分,引入了一个新的变量,称为“积分因子”,它乘以每个玩家的K参数,基于两个团队之间的积分绝对差异。当一支球队以较大的优势获胜时,比赛的影响必须更大,即压倒性的胜利。

计算积分因子使用以下公式:

如何为2v2游戏创建数据驱动的Elo评级系统 四海 第14张

该公式取两个团队得分的绝对差异,加1,并计算结果的以10为底的对数。然后将该值立方,并将2加到结果中,以获得积分因子的最终值。

图 V:积分因子 | 图表由作者提供

最终评分计算

在调整了所有必要的变量之后,我们开发了一个改进的公式来计算参与游戏的每个玩家的新排名。

现在每个玩家的评分都考虑了他们之前的评分、对手的评分、队友的影响、比赛历史和比赛得分。该公式确保每个玩家根据他们的真实表现得到奖励,同时考虑每场比赛的公平性。

根据前面的例子,玩家A的新排名公式如下:

如何为2v2游戏创建数据驱动的Elo评级系统 四海 第16张

这个改进的公式根据玩家的实际表现进行奖励,鼓励冒险,并为新老玩家提供更平衡的评分系统。

现在我们有了Elo算法,我们可以继续进行数据库建模。

数据库设计和建模

所提出的数据库模型采用关系型方法,通过使用主键(PK)和外键(FK)将数据组织成相互连接的表。这种结构化的组织有助于数据管理和分析,因此选择PostgreSQL作为数据库管理系统是合适的。PK和FK有助于维护数据库中的数据一致性,并减少冗余。

图 VI:数据库模型图示 | 图片由作者提供

这个数据库模型中的表之间存在两种关系:一对多和多对多。

Player”表和“Match”表之间的关系是多对多,因为一个玩家可以参与多场比赛,多个玩家可以参与一场比赛。一个名为“PlayerMatch”的连接表桥接了这种关系,包含两个外键:“player_id”(引用参与的玩家)和“match_id”(引用相应的比赛)。

这个结构确保了玩家和比赛之间的准确关联,如下面的代码所示:

CREATE TABLE PlayerMatch (player_match_id serial PRIMARY KEY,player_id INT NOT NULL REFERENCES Player(player_id),match_id INT NOT NULL REFERENCES Match(match_id));

类似的逻辑也适用于“TeamMatch”表,它作为“Match”表和“Team”表之间的交集,允许多个团队参加一场比赛,一场比赛涉及多个团队。

为了简化随着时间推移的排名分析,还设计了“PlayerRating”和“TeamRating”的单独表。这些表分别通过“player_match_id”和“team_match_id”与“PlayerMatch”和“TeamMatch”表连接。

数据完整性

除了使用主键和外键之外,此数据库模型还使用适当的数据类型和CHECK约束来确保数据的完整性:

  • Match”表中的“winning_team_score”和“losing_team_score”列是整数,防止非数字输入
  • CHECK约束强制“winning_team_score”必须是11
  • CHECK约束强制“losing_team_score”必须在0和10之间,遵守游戏规则

如下面的代码块所示,在数据库创建中实现了为每个主键使用序列以便于数据输入。这种自动化简化了后续使用Python循环进行数据输入过程的整体流程。

CREATE SEQUENCE player_id_seq START 1;CREATE SEQUENCE team_id_seq START 1;CREATE SEQUENCE match_id_seq START 1;CREATE SEQUENCE player_match_id_seq START 1;CREATE SEQUENCE player_rating_id_seq START 1;CREATE SEQUENCE team_match_id_seq START 1;CREATE SEQUENCE team_rating_id_seq START 1;

数据处理

主要的挑战是找到一种处理比赛数据的方式,使得可以从正在处理和插入到数据库中的初始数据中检索出ID。

这些特定的ID可以作为外键来管理剩余的数据,从而在处理过程中创建必要的关系。换句话说,第一步是从原始数据中识别和存储特定的数据(ID),然后使用这些ID作为桥梁来链接和处理其余的数据。

数据逐步处理,使用越来越复杂的Python循环。每个新条目都被分配一个从表的序列生成的唯一主键。

  1. 第一步是处理各个玩家并获取他们的ID。
  2. 接下来,使用玩家ID处理团队。对于每对唯一的比赛中的玩家,创建一个在“Team”表中的条目(外键为players)。
  3. 之后,使用获胜和失败的团队ID来处理比赛。处理完比赛后,通过检索相应的比赛、玩家和团队ID来处理“PlayerMatch”和“TeamMatch”表。
  4. 一旦处理完所有必要的数据,将“PlayerMatch”和“TeamMatch”的ID以及“match”的时间戳用于“PlayerRating”和“TeamRating”表,以跟踪评分随时间的变化。

Web应用程序开发

Web应用程序的目标是允许用户输入游戏结果、验证数据并直接与数据库进行交互。这确保了数据始终是最新的且实时提供,使用户始终能够访问排名或查看其指标。

此外,我希望使Web应用程序适用于移动设备,因为谁会想要拖着笔记本电脑玩桌上足球呢?那样既不实用也不有趣。

技术栈

后端在比较 Django 和 Flask 这两个用于构建 Python 网络应用的流行 Web 框架后,我们选择了 Flask,因为它对初学者更友好。Flask Web 框架用于处理用户请求、处理数据并与 PostgreSQL 数据库进行交互。

前端前端由静态 HTML 和 CSS 文件组成,定义了 Web 应用的结构和样式。JavaScript 用于表单验证和处理用户交互,以确保用户提交的数据在发送到后端之前始终保持一致和准确。

数据可视化在数据可视化方面,最大的挑战是要保持数据的实时性。为了克服这个限制,数据可视化层使用 Python 库 Plotly 生成可以在时间轴上可视化玩家评级的交互式图表。该组件从后端接收数据,进行处理,并以用户友好的格式呈现给用户。

数据库PostgreSQL 既用于本地开发环境,也用于 AWS 上的生产环境,通过 Heroku 进行连接。Heroku 提供自动数据库备份功能,确保数据受到保护,并且在需要时可以轻松恢复。

UI/UX 研究

在 UI/UX 设计方面,我们从 Spotify 和新的必应搜索引擎的现代 Web 设计中汲取灵感。我们的目标是创建一个熟悉和直观的用户体验。

图 VII: 应用程序的模拟 | 图片由作者提供

应用程序功能

让我们通过一个具体的场景来了解应用程序的功能。第一队(Matthieu 和 Gabriel)想要与第二队(Wissam 和 Malik)进行比赛。所有玩家都有不同的评级,代表了他们的技能水平,如下所示。

如何为2v2游戏创建数据驱动的Elo评级系统 四海 第19张

计算胜率

在任何比赛之前,玩家首先想要做的是计算他们的获胜概率。

为此,”计算胜率” 视图允许用户使用下拉菜单选择四名玩家,并生成所选队伍的获胜概率。

图 VIII: 计算胜率 | 图片由作者提供

该功能主要用于比赛前,以验证比赛是否平衡,并通知玩家他们的获胜概率。例如,第一队获胜的机会(64.19%)比第二队(35.81%)更高。这个视图向每个玩家提供了赌注和风险的信息。

一旦表单被提交,应用程序仅计算算法的第一部分,即根据选择的四名玩家计算游戏的预期结果。

上传比赛

“上传比赛” 视图是应用程序的主页。它旨在方便用户,使他们能够在打开应用程序后立即上传比赛。

图 IX: 上传比赛和比赛上传成功 | 图片由作者提供

在表单被提交之前,应用程序使用 JavaScript 进行数据验证,以确保:

  • 选择了四名不同的玩家
  • 分数为非负整数
  • 只有一个得分为 11 分的获胜队伍,不允许平局

当验证成功时,应用程序使用完整的算法处理数据,更新数据库中相应的表,并向用户确认上传成功。

“上传比赛结果”视图旨在向用户展示每场比赛对其个人评分的影响。它计算了比赛上传前后玩家评分的差异。

如上所示,游戏对每个玩家的评分影响并不相同。这是因为算法在每个玩家身上有个体化的参数:他们的预期得分、他们的游戏次数、他们的队友和对手队伍。

Elo 排名

“玩家排名”视图允许用户访问实时的月度排名,并与其他玩家进行比较。用户可以查看他们的评分,他们在整个月份内玩的游戏次数,以及他们最后一场游戏的评分。

图 X:玩家排名 | 图片由作者提供

一旦访问“玩家排名”视图或提交新的时期,应用程序将使用 CTE 方法查询数据库。

这涉及到连接所有必要的表,并显示最新的排名更新,使用时期选择器来过滤查询:

def get_latest_player_ratings(month=None, year=None):    now = datetime.now()    default_month = now.month    default_year = now.year    selected_year = int(year) if year else default_year    selected_month = int(month) if month else default_month    start_date = f'{selected_year}-{selected_month:02d}-01 00:00:00'    end_date = f'{selected_year}-{selected_month:02d}-{get_last_day_of_month(selected_month, selected_year):02d} 23:59:59'    query = '''        WITH max_player_rating_timestamp AS (            SELECT                 pm.player_id,                MAX(pr.player_rating_timestamp) as max_timestamp            FROM PlayerMatch pm            JOIN PlayerRating pr ON pm.player_match_id = pr.player_match_id            WHERE pr.player_rating_timestamp BETWEEN %s AND %s            GROUP BY pm.player_id        ),        filtered_player_match AS (            SELECT                 pm.player_id,                pm.match_id            FROM PlayerMatch pm            JOIN max_player_rating_timestamp mprt ON pm.player_id = mprt.player_id        ),        filtered_matches AS (            SELECT match_id            FROM Match            WHERE match_timestamp BETWEEN %s AND %s        )        SELECT             CONCAT(p.first_name, '.', SUBSTRING(p.last_name FROM 1 FOR 1)) as player_name,             pr.rating,             COUNT(DISTINCT fpm.match_id) as num_matches,            pr.player_rating_timestamp        FROM Player p        JOIN max_player_rating_timestamp mprt ON p.player_id = mprt.player_id        JOIN PlayerMatch pm ON p.player_id = pm.player_id        JOIN PlayerRating pr ON pm.player_match_id = pr.player_match_id            AND pr.player_rating_timestamp = mprt.max_timestamp        JOIN filtered_player_match fpm ON p.player_id = fpm.player_id        JOIN filtered_matches fm ON fpm.match_id = fm.match_id        GROUP BY p.player_id, pr.rating, pr.player_rating_timestamp        ORDER BY pr.rating DESC;    '''

数据可视化

开发这个全面的解决方案的主要目标是为用户提供实时的排名系统,作为每个玩家表现的可视化表示。

虽然有强大的工具如 PowerBI 和 Qlik 可供数据可视化使用,但选择了一个完全兼容移动设备的解决方案,让用户能够在设备上获得实时的洞察力,而无需支付许可费用。

为了实现这一目标,采用了两种方法:

  • 首先,使用了 Dash Plotly,这是一个 Python 框架,使开发人员能够在 Flask 应用程序之上构建交互式的数据驱动应用程序
  • 其次,使用了各种 SQL 查询和静态 HTML 页面从数据库中提取信息并显示,以确保用户始终可以访问实时数据

评分变化

这个可视化可让玩家观察每场比赛对他们的排名产生的影响,并识别出更广泛的趋势。例如,他们可以准确地看到何时有人超过他们,或者连续胜利或失败的影响。

图 XI:评分变化 | 图片由作者提供

访问“等级演变”视图时,应用程序会对数据库执行查询,为每个选定的玩家检索最近的排名更新,以每天玩游戏的日期为依据:

SELECT DISTINCT ON (DATE_TRUNC('day', m.match_timestamp))    DATE_TRUNC('day', m.match_timestamp) AS day_start,    CASE WHEN p.first_name = '{player}' THEN pr.rating ELSE NULL END AS ratingFROM PlayerMatch pmJOIN Player p ON pm.player_id = p.player_idJOIN PlayerRating pr ON pm.player_match_id = pr.player_match_idJOIN Match m ON pm.match_id = m.match_idWHERE p.first_name = '{player}'ORDER BY DATE_TRUNC('day', m.match_timestamp) DESC, m.match_timestamp DESC

然后将检索到的数据表转换为线图,使用Dash将列转换为轴。

为了减少数据库负载并简化图表中的数据显示,每天仅显示最新的评级更新。

玩家指标

受到Spotify Wrapped的启发,这个想法是提供从持续数据收集中得出的见解。虽然有很大的潜力来可视化玩家见解,但重点是突出个人表现和玩家之间的联系的指标。

图12:玩家指标 | 图片由作者提供

这些指标分为三个彩色类别:伙伴、游戏和对手,每个指标都附带一个标题、一个值和一个子度量以提供更详细的信息。

游戏指标这些指标以中性的蓝色显示在屏幕中央。它们包括自数据收集开始以来玩的总游戏数量。

伙伴指标伙伴指标显示在屏幕左侧。它们以绿色显示,因为它们具有积极的含义。

  • 顶部的框突出显示了与选定玩家玩过最多游戏的主要伙伴
  • 第二个指标标识了玩家最好的伙伴。这是根据最高的胜率定义的
  • 此类别中的第三个指标是选定玩家最差的伙伴。这是根据最低的胜率(或最高的失败率)计算的

对手指标对手指标以红色显示,表示对抗关系。对手指标代表玩家之间的竞争关系。

  • 顶部的框显示了最常见的对手,而子指标表示他们一起玩过的游戏数量,类似于伙伴指标
  • 第二个指标“最容易的对手”表示玩家的胜率最高的对手。这表示对手较弱
  • 最后一个指标是选定玩家胜率最低的对手。这个指标表示最难的对手

结论

我写这篇文章的时候,应用程序已经使用了6个月,迄今为止的结果如下:

  1. 这个基于Elo系统的排名系统可以预测比赛结果,并根据实际表现准确地对玩家进行排名
  2. 由于数据可视化,玩家变得更加竞争,因为他们现在越来越意识到自己的表现
  3. 由于改进的计算公式奖励冒险的玩家,玩家变得更加包容。平时不会一起玩的玩家现在有了配对的动机

通过采用数据驱动的策略,这个项目突出了数据的深远影响和重要性。

这个项目不仅仅分析了玩家的表现,还在玩家之间的互动以及新来者之间引发了一场转变。数据的力量真正培养了一个更加包容和竞争的环境。

感谢您阅读到这里!希望您觉得这篇文章有用。如果您对阅读完整论文感兴趣,可以在这里找到。此外,所有的代码都可以在Github上找到。

欢迎在评论中分享您的想法 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *