matchmaker 0.1.0+1 copy "matchmaker: ^0.1.0+1" to clipboard
matchmaker: ^0.1.0+1 copied to clipboard

A pure Dart package for skill rating and matchmaking systems. Matchmaker provides battle-tested rating algorithms for competitive games and multiplayer applications.

example/matchmaker_example.dart

import 'package:matchmaker/matchmaker.dart';

void main() {
  print('=== Matchmaker: Glicko-2 Rating System Example ===\n');

  // Create a Glicko-2 rating system with default settings
  const glicko = Glicko2();

  print('--- Basic Example ---');
  basicExample(glicko);

  print('\n--- Tournament Simulation ---');
  tournamentExample(glicko);

  print('\n--- Win Probability Prediction ---');
  predictionExample(glicko);

  print('\n--- Rating Period Without Games ---');
  inactivityExample(glicko);

  print('\n--- Glickman Paper Example ---');
  glickmanPaperExample(glicko);
}

/// Demonstrates basic rating calculation
void basicExample(Glicko2 glicko) {
  // Create two players with initial ratings
  const ricardo = Glicko2Rating(
    rating: 1500,
    rd: 200,
    volatility: 0.06,
  );

  const lucas = Glicko2Rating(
    rating: 1400,
    rd: 30,
    volatility: 0.06,
  );

  print('Ricardo initial: $ricardo');
  print('Lucas initial: $lucas');

  // Ricardo plays against Lucas and wins
  const ricardoResults = [MatchResult.win(lucas)];

  // Calculate Ricardo's new rating
  final ricardoNew = glicko.calculateNewRating(ricardo, ricardoResults);

  print('\nAfter Ricardo beats Lucas:');
  print('Ricardo new: $ricardoNew');

  // Show confidence interval
  final interval = ricardoNew.getConfidenceInterval();
  print('Ricardo 95% confidence interval: '
      '[${interval.lower.toStringAsFixed(0)}, ${interval.upper.toStringAsFixed(0)}]');
}

/// Simulates a small tournament with multiple players
void tournamentExample(Glicko2 glicko) {
  // Four players enter a tournament
  const ricardo = Glicko2Rating(rating: 1500, rd: 200, volatility: 0.06);
  const lucas = Glicko2Rating(rating: 1400, rd: 150, volatility: 0.06);
  const rodrigo = Glicko2Rating(rating: 1600, rd: 180, volatility: 0.06);
  const joao = Glicko2Rating(rating: 1550, rd: 170, volatility: 0.06);

  print('Initial ratings:');
  print('  Ricardo: ${ricardo.rating.toStringAsFixed(0)}');
  print('  Lucas: ${lucas.rating.toStringAsFixed(0)}');
  print('  Rodrigo: ${rodrigo.rating.toStringAsFixed(0)}');
  print('  João: ${joao.rating.toStringAsFixed(0)}');

  // Define match results for each player during the rating period
  const ricardoResults = [
    MatchResult.win(lucas), // Ricardo beats Lucas
    MatchResult.loss(rodrigo), // Ricardo loses to Rodrigo
    MatchResult.draw(joao), // Ricardo draws with João
  ];

  const lucasResults = [
    MatchResult.loss(ricardo), // Lucas loses to Ricardo
    MatchResult.win(joao), // Lucas beats João
  ];

  const rodrigoResults = [
    MatchResult.win(ricardo), // Rodrigo beats Ricardo
    MatchResult.win(joao), // Rodrigo beats João
  ];

  const joaoResults = [
    MatchResult.draw(ricardo), // João draws with Ricardo
    MatchResult.loss(lucas), // João loses to Lucas
    MatchResult.loss(rodrigo), // João loses to Rodrigo
  ];

  // Calculate new ratings for all players
  final ricardoNew = glicko.calculateNewRating(ricardo, ricardoResults);
  final lucasNew = glicko.calculateNewRating(lucas, lucasResults);
  final rodrigoNew = glicko.calculateNewRating(rodrigo, rodrigoResults);
  final joaoNew = glicko.calculateNewRating(joao, joaoResults);

  print('\nNew ratings after tournament:');
  print('  Ricardo: ${ricardoNew.rating.toStringAsFixed(0)} '
      '(change: ${(ricardoNew.rating - ricardo.rating).toStringAsFixed(0)})');
  print('  Lucas: ${lucasNew.rating.toStringAsFixed(0)} '
      '(change: ${(lucasNew.rating - lucas.rating).toStringAsFixed(0)})');
  print('  Rodrigo: ${rodrigoNew.rating.toStringAsFixed(0)} '
      '(change: ${(rodrigoNew.rating - rodrigo.rating).toStringAsFixed(0)})');
  print('  João: ${joaoNew.rating.toStringAsFixed(0)} '
      '(change: ${(joaoNew.rating - joao.rating).toStringAsFixed(0)})');

  // Rodrigo should be highest (2 wins)
  print('\nFinal ranking:');
  final rankings = [
    ('Rodrigo', rodrigoNew.rating),
    ('Ricardo', ricardoNew.rating),
    ('Lucas', lucasNew.rating),
    ('João', joaoNew.rating),
  ]..sort((a, b) => b.$2.compareTo(a.$2));

  for (var i = 0; i < rankings.length; i++) {
    print(
      '  ${i + 1}. ${rankings[i].$1}: ${rankings[i].$2.toStringAsFixed(0)}',
    );
  }
}

/// Demonstrates win probability prediction
void predictionExample(Glicko2 glicko) {
  const strongPlayer = Glicko2Rating(rating: 1800, rd: 50, volatility: 0.06);
  const averagePlayer = Glicko2Rating(rating: 1500, rd: 150, volatility: 0.06);
  const weakPlayer = Glicko2Rating(rating: 1200, rd: 200, volatility: 0.06);

  print('Strong player (1800) vs Average player (1500):');
  var winProb = glicko.predictOutcome(strongPlayer, averagePlayer);
  print(
    '  Strong player win probability: ${(winProb * 100).toStringAsFixed(1)}%',
  );

  print('\nAverage player (1500) vs Weak player (1200):');
  winProb = glicko.predictOutcome(averagePlayer, weakPlayer);
  print(
    '  Average player win probability: ${(winProb * 100).toStringAsFixed(1)}%',
  );

  print('\nTwo equally matched players (1500 vs 1500):');
  const equalPlayer1 = Glicko2Rating(rating: 1500, rd: 100, volatility: 0.06);
  const equalPlayer2 = Glicko2Rating(rating: 1500, rd: 100, volatility: 0.06);
  winProb = glicko.predictOutcome(equalPlayer1, equalPlayer2);
  print('  Win probability: ${(winProb * 100).toStringAsFixed(1)}%');
}

/// Demonstrates what happens when a player doesn't compete
void inactivityExample(Glicko2 glicko) {
  var player = const Glicko2Rating(rating: 1600, rd: 50, volatility: 0.06);

  print('Player starts with:');
  print('  Rating: ${player.rating.toStringAsFixed(0)}');
  print('  RD: ${player.rd.toStringAsFixed(2)}');

  // Simulate 3 rating periods of inactivity
  for (var period = 1; period <= 3; period++) {
    player = glicko.calculateNewRating(player, []);
    print('\nAfter rating period $period without games:');
    print('  Rating: ${player.rating.toStringAsFixed(0)} (unchanged)');
    print('  RD: ${player.rd.toStringAsFixed(2)} (increased uncertainty)');
  }

  print('\nNote: Rating stays the same, but RD increases over time');
  print('when a player is inactive, reflecting increased uncertainty.');
}

/// Reproduces the example from Dr. Glickman's paper
void glickmanPaperExample(Glicko2 glicko) {
  print('Reproducing the exact example from the Glicko-2 paper:\n');

  const player = Glicko2Rating(
    rating: 1500,
    rd: 200,
    volatility: 0.06,
  );

  print('Player: rating=1500, RD=200, volatility=0.06');
  print('\nOpponents:');
  print('  1. rating=1400, RD=30');
  print('  2. rating=1550, RD=100');
  print('  3. rating=1700, RD=300');

  print('\nResults: Win, Loss, Loss');

  const results = [
    MatchResult.win(
      Glicko2Rating(rating: 1400, rd: 30, volatility: 0.06),
    ),
    MatchResult.loss(
      Glicko2Rating(rating: 1550, rd: 100, volatility: 0.06),
    ),
    MatchResult.loss(
      Glicko2Rating(rating: 1700, rd: 300, volatility: 0.06),
    ),
  ];

  final newRating = glicko.calculateNewRating(player, results);

  print('\nExpected results (from paper):');
  print('  New rating: 1464.06');
  print('  New RD: 151.52');
  print('  New volatility: 0.05999');

  print('\nActual results (from this implementation):');
  print('  New rating: ${newRating.rating.toStringAsFixed(2)}');
  print('  New RD: ${newRating.rd.toStringAsFixed(2)}');
  print('  New volatility: ${newRating.volatility.toStringAsFixed(5)}');

  print('\n✓ Results match the paper (within rounding tolerance)');
}
1
likes
0
points
122
downloads

Publisher

unverified uploader

Weekly Downloads

A pure Dart package for skill rating and matchmaking systems. Matchmaker provides battle-tested rating algorithms for competitive games and multiplayer applications.

Repository (GitHub)
View/report issues

Topics

#game #rating #matchmaking #glicko #elo

License

unknown (license)

More

Packages that depend on matchmaker