ranking 0.2.1

A simple library to rank players.

This package contains functions to rank players, given the results of their games.

For static rankings, where players don't change their skill-level, the Bradley-Terry model computes a score for each player that predicts the probability at which one player would win against another.

For dynamic rankings, where players change their skill-level over time, the ELO ranking system provides a score that dynamically adjust with the results of the players.

Usage #

A simple usage example for Bradley-Terry:

import 'package:ranking/ranking.dart';

main() {
  // In the following list the first entry in the pair (representing a game)
  // won that game.
  var games = [
    ["Player 2", "Player 1"],  // Player 2 won over Player 1.
    ["Player 2", "Player 3"],
    ["Player 3", "Player 2"],
    ["Player 3", "Player 2"],
  ];
  var scores = computeBradleyTerryScores(games);
  // The `scores` map contains a score for each player:
  //   Player 1 -> 0.000
  //   Player 2 -> 0.333
  //   Player 3 -> 0.667
}

A simple usage example for ELO:

import 'package:ranking/ranking.dart';

main() {
  // The game-scope (3rd column) indicates the result of the game:
  //   * 1.0: the first player won decisively.
  //   * 0.5: a draw.
  //   * 0.0: the second player won decisively.
  var games = [
    ["Player 2", "Player 1", 1.0],
    ["Player 3", "Player 2", 0.5],
    ["Player 3", "Player 1", 0.0],
    ["Player 3", "Player 2", 1.0],
    ["Player 2", "Player 3", 1.0],
  ];

  var elo = new Elo(
      defaultInitialRating: 100,
      // With an `n` of 30, a difference of 30 in score means that the
      // stronger player is 10 times more likely to win.
      n:  30,
      // How much new game-results move the score. With 10, players with
      // the same score will have a difference of 10 after a decisive
      // victory/loss.
      kFactor: 10
  );

  games.forEach((list) { elo.recordResult(list[0], list[1], list[2]); });

  var ratings = elo.ratings;
  // The `rating` map contains a rating for each player:
  //   Player 1 -> 101
  //   Player 2 -> 103
  //   Player 3 ->  95
}

Features and bugs #

Please file feature requests and bugs at the issue tracker.

0.2.1 #

Update the README after the API change.

0.2.0 #

Changed the API slightly. No need to register all players for ELO now.

0.1.0 #

Initial version. Basic ELO and Bradley-Terry models implemented.

example/ranking_example.dart

// Copyright 2019 Florian Loitsch
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import "package:ranking/ranking.dart";

var players = [
  "Novak",
  "Rafael",
  "Roger",
  "Dominic",
  "Alexander",
  "Stefanos",
];

var games = [
  ["Stefanos", "Dominic", 0.0, "2018-01-01"], // quarter
  ["Stefanos", "Dominic", 0.0, "2018-03-05"], // ro64
  ["Alexander", "Rafael", 0.0, "2018-04-02"], // round robin.
  ["Dominic", "Novak", 1.0, "2018-04-16"],
  ["Stefanos", "Dominic", 1.0, "2018-04-16"], // quarter
  ["Dominic", "Rafael", 0.0, "2018-04-16"],
  ["Stefanos", "Rafael", 0.0, "2018-04-23"], // final
  ["Dominic", "Rafael", 1.0, "2018-05-07"],
  ["Alexander", "Dominic", 1.0, "2018-05-07"],
  ["Rafael", "Novak", 1.0, "2018-05-14"],
  ["Alexander", "Rafael", 0.0, "2018-05-14"],
  ["Stefanos", "Dominic", 0.0, "2018-05-28"], // ro64
  ["Alexander", "Dominic", 0.0, "2018-05-28"], // quarter
  ["Dominic", "Rafael", 0.0, "2018-05-28"],
  ["Rafael", "Novak", 0.0, "2018-07-02"],
  ["Stefanos", "Alexander", 0.0, "2018-07-30"], // semi
  ["Stefanos", "Dominic", 1.0, "2018-08-06"], // ro32
  ["Stefanos", "Novak", 1.0, "2018-08-06"], // round of 16
  ["Stefanos", "Alexander", 1.0, "2018-08-06"], // quarter
  ["Stefanos", "Rafael", 0.0, "2018-08-06"], // final
  ["Roger", "Novak", 0.0, "2018-08-13"],
  ["Dominic", "Rafael", 0.0, "2018-08-27"],
  ["Alexander", "Novak", 0.0, "2018-10-08"],
  ["Roger", "Novak", 0.0, "2018-10-29"],
  ["Alexander", "Novak", 0.0, "2018-11-12"], // Earlier round robin.
  ["Dominic", "Roger", 0.0, "2018-11-12"], // round robin
  ["Alexander", "Roger", 1.0, "2018-11-12"],
  ["Alexander", "Novak", 1.0, "2018-11-12"],
  ["Roger", "Rafael", 1.0, "2019-03-04"],
  ["Dominic", "Roger", 1.0, "2019-03-04"],
  ["Stefanos", "Roger", 1.0, "2019-01-14"], // round of 16
  ["Stefanos", "Rafael", 0.0, "2019-01-14"], // semi
  ["Rafael", "Novak", 0.0, "2019-01-14"], // final
  ["Stefanos", "Roger", 0.0, "2019-02-25"], // final
  ["Dominic", "Rafael", 1.0, "2019-04-22"],
  ["Stefanos", "Alexander", 1.0, "2019-05-06"], // quarter
  ["Dominic", "Roger", 1.0, "2019-05-06"],
  ["Stefanos", "Rafael", 1.0, "2019-05-06"], // semi
  ["Dominic", "Novak", 0.0, "2019-05-06"], // semi
  ["Stefanos", "Novak", 0.0, "2019-05-06"], // final
  ["Stefanos", "Roger", 1.0, "2019-05-13"], // quarter
  ["Stefanos", "Rafael", 0.0, "2019-05-13"], // semi
  ["Rafael", "Novak", 1.0, "2019-05-13"], // final
];

void bradleyTerryExample() {
  // Bradley-Terry is better suited for players that don't change their skill
  // level. As such, using the data of Tennis games isn't great, since
  // Tennis players change their skill level over time.
  // However, it's still interesting to compare the result to the ELO algorithm.
  var input = games.map((list) {
    if (list[2] == 1.0) return [list[0], list[1]];
    return [list[1], list[0]];
  }).toList();

  var scores = computeBradleyTerryScores(input);

  var ranked = players.toList()
    ..sort((a, b) => -scores[a].compareTo(scores[b]));
  print("Bradley-Terry Scores:");
  for (var player in ranked) {
    print("  $player: ${scores[player]}");
  }
}

void eloExample() {
  // The data we are using is from Tennis.
  //
  // The ranking changes over time, whenever a new game has been played.
  // Players are naturally changing their skills (relative to others) over time
  // and as such order matters. This means that `ELO` is a good model to
  // rank the players. (At least, if we used all played games, and not just
  // a tiny subset of them).

  var elo = Elo(defaultInitialRating: 100, n: 70, kFactor: 15);

  for (var game in games) {
    elo.recordResult(game[0], game[1], game[2]);
  }
  var ratings = elo.ratings;
  var ranked = players.toList()
    ..sort((a, b) => -ratings[a].compareTo(ratings[b]));
  print("ELO Scores:");
  for (var player in ranked) {
    print("  $player: ${ratings[player]}");
  }
}

void main() {
  eloExample();
  print("");
  bradleyTerryExample();
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  ranking: ^0.2.1

2. Install it

You can install packages from the command line:

with pub:


$ pub get

with Flutter:


$ flutter pub get

Alternatively, your editor might support pub get or flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:ranking/ranking.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
14
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
81
Overall:
Weighted score of the above. [more]
53
Learn more about scoring.

We analyzed this package on Jul 17, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.4.0
  • pana: 0.12.19

Platforms

Detected platforms: Flutter, web, other

No platform restriction found in primary library package:ranking/ranking.dart.

Maintenance suggestions

The package description is too short. (-19 points)

Add more detail to the description field of pubspec.yaml. Use 60 to 180 characters to describe the package, what it does, and its target use case.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.2.0 <3.0.0
Dev dependencies
pedantic ^1.0.0
test ^1.0.0

Admin