Skip to content

bstummer/openskill.lua

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

openskill.lua

openskill.lua is a fast, lightweight, and license-free skill rating system for Roblox. Based on the Weng-Lin Bayesian ranking model, it serves as a direct, open-source alternative to Microsoft's TrueSkill algorithm.

Whether you are building 1v1 competitive arenas, large-scale free-for-alls, or asymmetrical team games, this Luau port of openskill.js provides highly accurate player skill tracking to keep your matchmaking fair and engaging.


Features

  • License-Free TrueSkill Alternative: Avoid the legal and commercial restrictions of TrueSkill by using the open-source Weng-Lin Bayesian ranking.
  • Multiple Rating Models: Choose between Plackett-Luce (default, logistic distribution) or Thurstone-Mosteller (Gaussian distribution).
  • Flexible Matchmaking: Accurately calculate both Win Probability and Draw Probability between multiple teams to ensure fair matches before they even start.
  • Custom Scoring & Ranks: Rate matches based on custom team placements, or input raw game scores directly (fully supports tie handling).
  • Multi-Player Teams: Supports asymmetric team sizes (e.g., 2v3 or free-for-all) and distributes skill adjustments accurately across all grouped players.
  • Safe Leaderboard Sorting: Uses an Ordinal system (mu - 3 * sigma) to represent a player's conservative skill floor, perfect for displaying public ranks.

Installation

Get the module here and insert it into your game (preferably in ServerStorage).

Alternatively, you can paste this directly into your Roblox Studio command bar:

game:GetObjects("rbxassetid://8134663273")[1].Parent=game.ServerStorage

Quick Start

1. Require the Module

Create a script and require the module:

local OpenSkill = require(game.ServerStorage.OpenSkill)

2. Create Ratings

You can create a rating for every player to describe their skill. Ratings are represented as a Gaussian curve with two properties:

  • mu: The average skill of the player.
  • sigma: The degree of uncertainty in the player's skill.

Maintaining an uncertainty (sigma) allows the system to make large changes to skill estimates early on, but smaller, more stable changes after a series of consistent games.

local a1 = OpenSkill.Rating() --> {mu = 25, sigma = 8.333}
local a2 = OpenSkill.Rating(32.444) --> {mu = 32.444, sigma = 10.814}
local b1 = OpenSkill.Rating(nil, 2.421) --> {mu = 25, sigma = 2.421}
local b2 = OpenSkill.Rating(25.188, 6.211) --> {mu = 25.188, sigma = 6.211}

3. Rate a Match

If a1 and a2 form a team and win against a team of b1 and b2, you can update their skill ratings:

OpenSkill.Rate({{a1, a2}, {b1, b2}})

4. Displaying Ratings

When displaying a rating or sorting a leaderboard, use Ordinal. By default, this returns mu - 3 * sigma, showing a rating for which there is a 99.7% likelihood the player's true rating is higher. In early games, a player's ordinal rating will usually go up, even if they lose!

OpenSkill.Ordinal(a1) --> 0 (before rating)
OpenSkill.Ordinal(a1) --> 2.3245624871094 (after winning)

Advanced Match Results

Custom Ranks

If your teams are listed in one order but your ranking is in a different order, you can specify a rank option. Lower ranks are considered better.

local a = OpenSkill.Rating()
local b = OpenSkill.Rating()
local c = OpenSkill.Rating()
local d = OpenSkill.Rating()

OpenSkill.Rate({{a}, {b}, {c}, {d}}, { --4 teams consisting of 1 player
	rank = {4, 1, 3, 2}
})

In this example, team b placed 1st, d placed 2nd, c placed 3rd, and a placed 4th.

Custom Scores

You can also provide a score instead, where higher is better. These can just be raw scores from the game.

OpenSkill.Rate({{a}, {b}, {c}, {d}}, {
	score = {37, 19, 37, 42}
})

Note: Ties should have either an equivalent rank or score.


Rating Models

openskill.lua provides two rating models: PlackettLuce and ThurstoneMosteller.

  • Plackett-Luce (Default): A generalized Bradley-Terry model for k ≥ 3 teams which scales best. It follows a logistic distribution over a player's skill, similar to Glicko.
  • Thurstone-Mosteller: Follows a Gaussian distribution, similar to TrueSkill. Accuracy is usually slightly lower than Plackett-Luce, but can be tuned with an alternative gamma function.

Note: openskill.lua uses full pairing which yields highly accurate ratings. However, in games with an extremely high number of teams (100+), calculations become computationally expensive due to joint probability integration.

You can change the global default model or pass it per-match:

-- Global
OpenSkill.Settings.DefaultModel = "ThurstoneMosteller"

-- Per-match
OpenSkill.Rate({{a}, {b}, {c}, {d}}, {
	model = "ThurstoneMosteller"
})

API Reference

OpenSkill.DefaultModel : string

Determines the model which is used by default.

OpenSkill.Rating(mu : number?, sigma : number?, options : any?): rating

Creates a rating object, which describes a player's skill. Ratings are kept as an object which represent a gaussian curve, with properties where mu represents the mean, and sigma represents the spread or standard deviation. mu is the average skill of the player and sigma is the degree of uncertainty in the player's skill. Maintaining an uncertainty allows the system to make big changes to the skill estimates early on but small changes after a series of consistent games has been played. If omitted, mu defaults to 25 and sigma defaults to 25 / 3.

OpenSkill.Ordinal(rating : rating, options : any?): number

Represents a player's skill estimate as a single number. By default, this returns mu - 3 * sigma, showing a rating for which there's a 99.7% likelihood the player's true rating is higher. So in early games, a player's ordinal rating will usually go up and could go up even if that player loses.

OpenSkill.Rate(teams : {{rating}}, options : any?): {{{number}}}

Takes an array of teams (which are arrays of ratings) and updates their values based on the outcome of the match. Returns the new rating values in identically structured arrays.

OpenSkill.WinProbability(teams : {{rating}}, options : any?): {number}

Calculates the probability of each team winning the match.

OpenSkill.DrawProbability(teams : {{rating}}, options : any?): number

Calculates the probability of a draw between the teams. This is extremely useful for determining fair team compositions in matchmaking.


Contributing

Contributions, issues, and feature requests are greatly appreciated!

About

A Luau implementation of the Weng-Lin Bayesian ranking system, a license-free alternative to TrueSkill. Ported from openskill.js and designed specifically for Roblox game development.

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages