#rating #skill #elo #glicko #trueskill

skillratings

使用 Elo、Glicko、Glicko-2、TrueSkill 等算法计算玩家的技能评级

45 个版本 (26 个破坏性版本)

0.27.0 2024年6月8日
0.26.0 2023年10月8日
0.25.1 2023年10月5日
0.25.0 2023年6月4日
0.21.0 2022年11月24日

#49 in 数学

Download history 24/week @ 2024-04-22 5/week @ 2024-04-29 9/week @ 2024-05-06 36/week @ 2024-05-13 78/week @ 2024-05-20 50/week @ 2024-05-27 251/week @ 2024-06-03 150/week @ 2024-06-10 68/week @ 2024-06-17 95/week @ 2024-06-24 12/week @ 2024-07-01 82/week @ 2024-07-08 21/week @ 2024-07-15 18/week @ 2024-07-22 23/week @ 2024-07-29 12/week @ 2024-08-05

每月下载量 84

MIT/Apache

470KB
8K SLoC

skillratings

Skillratings 提供了一组著名的(和不太著名的)技能评级算法,允许您即时评估玩家的技能水平。
您可以在 1 对 1 比赛、团队对抗比赛、自由对抗或多队比赛,或在锦标赛/评级期间,轻松地即时计算技能评级。
这个库非常轻量(默认无依赖),用户友好,当然,速度极快。

当前支持的算法

这些大多数都源于在线多人游戏中的使用。
点击上述链接的文档,了解更多关于特定评级算法及其优缺点的信息。

目录

安装

如果您使用的是 Rust 1.62 或更高版本,请使用 cargo add 安装最新版本

cargo add skillratings

或者,您可以手动将以下内容添加到您的 Cargo.toml 文件中

[dependencies]
skillratings = "0.27"

Serde 支持

Serde 支持由 serde 功能控制。您可以通过以下方式启用它

使用 cargo add

cargo add skillratings --features serde

通过手动编辑 Cargo.toml

[dependencies]
skillratings = {version = "0.27", features = ["serde"]}

用法和示例

以下是一些关于此crate用例的基本示例。
还有许多其他评分算法,这里没有涵盖许多有用的功能。
更多信息,请参阅文档

玩家对玩家

这里包含的每个评分算法都可以用于1v1游戏。
以下示例中我们使用的是Glicko-2

use skillratings::{
    glicko2::{glicko2, Glicko2Config, Glicko2Rating},
    Outcomes,
};

// Initialise a new player rating.
// The default values are: 1500, 350, and 0.06.
let player_one = Glicko2Rating::new();

// Or you can initialise it with your own values of course.
// Imagine these numbers being pulled from a database.
let (some_rating, some_deviation, some_volatility) = (1325.0, 230.0, 0.05932);
let player_two = Glicko2Rating {
    rating: some_rating,
    deviation: some_deviation,
    volatility: some_volatility,
};

// The outcome of the match is from the perspective of player one.
let outcome = Outcomes::WIN;

// The config allows you to specify certain values in the Glicko-2 calculation.
let config = Glicko2Config::new();

// The glicko2 function will calculate the new ratings for both players and return them.
let (new_player_one, new_player_two) = glicko2(&player_one, &player_two, &outcome, &config);

// The first players rating increased by ~112 points.
assert_eq!(new_player_one.rating.round(), 1612.0);

团队对团队

一些算法,如TrueSkill或Weng-Lin,还允许您对基于团队的比赛进行评分。
以下示例展示了使用TrueSkill进行的3v3比赛。

use skillratings::{
    trueskill::{trueskill_two_teams, TrueSkillConfig, TrueSkillRating},
    Outcomes,
};

// We initialise Team One as a Vec of multiple TrueSkillRatings.
// The default values for the rating are: 25, 25/3 ≈ 8.33.
let team_one = vec![
    TrueSkillRating {
        rating: 33.3,
        uncertainty: 3.3,
    },
    TrueSkillRating {
        rating: 25.1,
        uncertainty: 1.2,
    },
    TrueSkillRating {
        rating: 43.2,
        uncertainty: 2.0,
    },
];

// Team Two will be made up of 3 new players, for simplicity.
// Note that teams do not necessarily have to be the same size.
let team_two = vec![
    TrueSkillRating::new(),
    TrueSkillRating::new(),
    TrueSkillRating::new(),
];

// The outcome of the match is from the perspective of team one.
let outcome = Outcomes::LOSS;

// The config allows you to specify certain values in the TrueSkill calculation.
let config = TrueSkillConfig::new();

// The trueskill_two_teams function will calculate the new ratings for both teams and return them.
let (new_team_one, new_team_two) = trueskill_two_teams(&team_one, &team_two, &outcome, &config);

// The rating of the first player on team one decreased by around ~1.2 points.
assert_eq!(new_team_one[0].rating.round(), 32.0);

自由对抗和多队比赛

Weng-Lin和TrueSkill算法还支持对多队比赛进行评分。
以下是一个3队比赛,每队3个玩家的示例。

use skillratings::{
    weng_lin::{weng_lin_multi_team, WengLinConfig, WengLinRating},
    MultiTeamOutcome,
};

// Initialise the teams as Vecs of WengLinRatings.
// Note that teams do not necessarily have to be the same size.
// The default values for the rating are: 25, 25/3 ≈ 8.33.
let team_one = vec![
    WengLinRating {
        rating: 25.1,
        uncertainty: 5.0,
    },
    WengLinRating {
        rating: 24.0,
        uncertainty: 1.2,
    },
    WengLinRating {
        rating: 18.0,
        uncertainty: 6.5,
    },
];

let team_two = vec![
    WengLinRating {
        rating: 44.0,
        uncertainty: 1.2,
    },
    WengLinRating {
        rating: 32.0,
        uncertainty: 2.0,
    },
    WengLinRating {
        rating: 12.0,
        uncertainty: 3.2,
    },
];

// Using the default rating for team three for simplicity.
let team_three = vec![
    WengLinRating::new(),
    WengLinRating::new(),
    WengLinRating::new(),
];

// Every team is assigned a rank, depending on their placement. The lower the rank, the better.
// If two or more teams tie with each other, assign them the same rank.
let rating_groups = vec![
    (&team_one[..], MultiTeamOutcome::new(1)),      // team one takes the 1st place.
    (&team_two[..], MultiTeamOutcome::new(3)),      // team two takes the 3rd place.
    (&team_three[..], MultiTeamOutcome::new(2)),    // team three takes the 2nd place.
];

// The weng_lin_multi_team function will calculate the new ratings for all teams and return them.
let new_teams = weng_lin_multi_team(&rating_groups, &WengLinConfig::new());

// The rating of the first player of team one increased by around ~2.9 points.
assert_eq!(new_teams[0][0].rating.round(), 28.0);

预期结果

每个评分算法都有一个expected_score函数,您可以使用它来预测比赛的结局。
以下示例使用Glicko不是Glicko-2)进行演示。

use skillratings::glicko::{expected_score, GlickoRating};

// Initialise a new player rating.
// The default values are: 1500, and 350.
let player_one = GlickoRating::new();

// Initialising a new rating with custom numbers.
let player_two = GlickoRating {
    rating: 1812.0,
    deviation: 195.0,
};

// The expected_score function will return two floats between 0 and 1 for each player.
// A value of 1 means guaranteed victory, 0 means certain loss.
// Values near 0.5 mean draws are likely to occur.
let (exp_one, exp_two) = expected_score(&player_one, &player_two);

// The expected score for player one is ~0.25.
// If these players would play 100 games, player one is expected to score around 25 points.
// (Win = 1 point, Draw = 0.5, Loss = 0)
assert_eq!((exp_one * 100.0).round(), 25.0);

评分周期

这里包含的每个评分算法都有一个..._rating_period,允许您使用结果列表计算玩家的新评分。
这可以在锦标赛中使用,或者在您只在某个评分周期结束时更新评分的情况下很有用,正如其名称所示。
以下示例中我们使用的是Elo评分算法。

use skillratings::{
    elo::{elo_rating_period, EloConfig, EloRating},
    Outcomes,
};

// We initialise a new Elo Rating here.
// The default rating value is 1000.
let player = EloRating { rating: 1402.1 };

// We need a list of results to pass to the elo_rating_period function.
let mut results = Vec::new();

// And then we populate the list with tuples containing the opponent,
// and the outcome of the match from our perspective.
results.push((EloRating::new(), Outcomes::WIN));
results.push((EloRating { rating: 954.0 }, Outcomes::DRAW));
results.push((EloRating::new(), Outcomes::LOSS));

// The elo_rating_period function calculates the new rating for the player and returns it.
let new_player = elo_rating_period(&player, &results, &EloConfig::new());

// The rating of the player decreased by around ~40 points.
assert_eq!(new_player.rating.round(), 1362.0);

在不同评级系统之间切换

如果您想在不同评分系统之间切换,例如比较结果或进行科学分析,我们提供特质,以便尽可能快速地切换。
您只需要提供正确的评分系统配置即可。

免责声明:为了更精确和微调的计算,建议您直接使用评分系统模块。
特质主要适用于系统之间的比较。

以下示例中,我们使用Glicko-2与RatingSystem(1v1)特质。

use skillratings::{
    glicko2::{Glicko2, Glicko2Config},
    Outcomes, Rating, RatingSystem,
 ;

// Initialise a new player rating with a rating value and uncertainty value.
// Not every rating system has an uncertainty value, so it may be discarded.
// Some rating systems might consider other values too (volatility, age, matches played etc.).
// If that is the case, we will use the default values for those.
let player_one = Rating::new(Some(1200.0), Some(120.0));
// Some rating systems might use widely different scales for measuring a player's skill.
// So if you always want the default values for every rating system, use None instead.
let player_two = Rating::new(None, None);

// The config needs to be specific to the rating system.
// When you swap rating systems, make sure to update the config.
let config = Glicko2Config::new();

// We want to rate 1v1 matches here so we are using the `RatingSystem` trait.
// You may also need to use a type annotation here for the compiler.
let rating_system: Glicko2 = RatingSystem::new(config);

// The outcome of the match is from the perspective of player one.
let outcome = Outcomes::WIN;

// Calculate the expected score of the match.
let expected_score = rating_system.expected_score(&player_one, &player_two);
// Calculate the new ratings.
let (new_one, new_two) = rating_system.rate(&player_one, &player_two, &outcome);

// After that, access new ratings and uncertainties with the functions below.
assert_eq!(new_one.rating().round(), 1241.0);
// Note that because not every rating system has an uncertainty value,
// the uncertainty function returns an Option<f64>.
assert_eq!(new_one.uncertainty().unwrap().round(), 118.0);

贡献

欢迎任何形式的贡献!

发现了错误或有什么功能请求?请提交新问题
或者,如果您想添加功能或修复错误,请发起拉取请求
当然,留下其他反馈也受到欢迎。

感谢所有抽出时间贡献的人。

许可

本项目采用MIT许可证Apache许可证,版本2.0

依赖关系