Initial commit master 0.001
authorMarius Gavrilescu <marius@ieval.ro>
Wed, 30 Nov 2016 23:44:13 +0000 (23:44 +0000)
committerMarius Gavrilescu <marius@ieval.ro>
Wed, 30 Nov 2016 23:44:13 +0000 (23:44 +0000)
Changes [new file with mode: 0644]
MANIFEST [new file with mode: 0644]
Makefile.PL [new file with mode: 0644]
README [new file with mode: 0644]
lib/Games/Ratings/LogisticElo.pm [new file with mode: 0644]
t/Games-Ratings-LogisticElo.t [new file with mode: 0644]

diff --git a/Changes b/Changes
new file mode 100644 (file)
index 0000000..87f79d3
--- /dev/null
+++ b/Changes
@@ -0,0 +1,4 @@
+Revision history for Perl extension Games::Ratings::Elo.
+
+0.001 2016-11-30T23:44+00:00
+ - Initial release
diff --git a/MANIFEST b/MANIFEST
new file mode 100644 (file)
index 0000000..ac2c65b
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,6 @@
+Changes
+Makefile.PL
+MANIFEST
+README
+t/Games-Ratings-LogisticElo.t
+lib/Games/Ratings/LogisticElo.pm
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644 (file)
index 0000000..7e80656
--- /dev/null
@@ -0,0 +1,21 @@
+use 5.014000;
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+       NAME              => 'Games::Ratings::LogisticElo',
+       VERSION_FROM      => 'lib/Games/Ratings/LogisticElo.pm',
+       ABSTRACT_FROM     => 'lib/Games/Ratings/LogisticElo.pm',
+       AUTHOR            => 'Marius Gavrilescu <marius@ieval.ro>',
+       MIN_PERL_VERSION  => '5.14.0',
+       LICENSE           => 'perl',
+       SIGN              => 1,
+       PREREQ_PM         => {
+               qw/Games::Ratings 0/,
+       },
+       META_ADD           => {
+               dynamic_config => 0,
+               resources      => {
+                       repository => 'https://git.ieval.ro/?p=games-ratings-logisticelo.git',
+               },
+       }
+);
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..0f7c24e
--- /dev/null
+++ b/README
@@ -0,0 +1,36 @@
+Games-Ratings-LogisticElo version 0.001
+===============================
+
+Games-Ratings-LogisticElo provides methods to calculate Elo rating
+changes. Unlike L<Games::Ratings::Chess::FIDE>, this Elo
+implementation uses the logistic distribution instead of the standard
+distribution.
+
+Games-Ratings-LogisticElo can be used both for a single player who
+played multiple rated games, and for a single competition with an
+arbitrary number of players.
+
+INSTALLATION
+
+To install this module type the following:
+
+   perl Makefile.PL
+   make
+   make test
+   make install
+
+DEPENDENCIES
+
+This module requires these other modules and libraries:
+
+* Games::Ratings
+
+COPYRIGHT AND LICENCE
+
+Copyright (C) 2016 by Marius Gavrilescu
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.22.2 or,
+at your option, any later version of Perl 5 you may have available.
+
+
diff --git a/lib/Games/Ratings/LogisticElo.pm b/lib/Games/Ratings/LogisticElo.pm
new file mode 100644 (file)
index 0000000..25e4e6d
--- /dev/null
@@ -0,0 +1,159 @@
+package Games::Ratings::LogisticElo;
+
+use 5.014000;
+use strict;
+use warnings;
+use parent qw/Exporter Games::Ratings/;
+
+our @EXPORT_OK = qw/multi_elo/;
+our @EXPORT = qw//;
+our $VERSION = '0.001';
+
+use List::Util qw/sum/;
+
+sub get_rating_change {
+       my ($self) = @_;
+
+       my $own_rating = $self->get_rating;
+       my $K = $self->get_coefficient;
+
+       my $expected = sum map {
+               my $exp = ($_->{opponent_rating} - $own_rating) / 400;
+               1 / (1 + 10 ** $exp)
+       } $self->get_all_games;
+
+       my $actual = sum map {
+               Games::Ratings::_get_numerical_result($_->{result})
+       } $self->get_all_games;
+
+       $K * ($actual - $expected)
+}
+
+sub get_new_rating {
+       my ($self) = @_;
+       $self->get_rating + $self->get_rating_change
+}
+
+sub multi_elo {
+       my @args = @_;
+       my $K = ref $args[0] ? 15 : shift @args;
+
+       my @newratings = map {
+               my $player = __PACKAGE__->new;
+               $player->set_rating($_->[0]);
+               $player->set_coefficient($K);
+               for my $opponent (@args) {
+                       $player->add_game({
+                               opponent_rating => $opponent->[0],
+                               result =>
+                                 $_->[1] > $opponent->[1] ? 'win' :
+                                 $_->[1] < $opponent->[1] ? 'loss' : 'draw'
+                       })
+               }
+               $player->get_new_rating
+       } @args;
+
+       wantarray ? @newratings : \@newratings
+}
+
+1;
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+Games::Ratings::LogisticElo - calculate changes to logistic curve Elo ratings
+
+=head1 SYNOPSIS
+
+  use Games::Ratings::LogisticElo;
+  my $player = Games::Ratings::LogisticElo->new;
+  $player->set_rating(2240);
+  $player->set_coefficient(15);
+  $player->add_game({
+    opponent_rating => 2114,
+    result => 'win', ## or 'draw' or 'loss'
+  });
+  say 'Rating change: ' . $player->get_rating_change;
+  say 'New rating: ' . $player->get_new_rating;
+
+  use Games::Ratings::LogisticElo qw/multi_elo/;
+  my @results = [2240, 3], [2114, 2], [2300, 1];
+  my @new_ratings = multi_elo 15, @results;
+  say 'Rating changes for this comp: ', join ', ',
+    map { $new_ratings[$_] - $results[$_]->[0] } 0 .. $#results;
+
+=head1 DESCRIPTION
+
+This module provides methods to calculate Elo rating changes. Unlike
+L<Games::Ratings::Chess::FIDE>, this Elo implementation uses the
+logistic distribution instead of the standard distribution.
+
+This module can be used both for a single player who played multiple
+rated games, and for a single competition with an arbitrary number of
+players.
+
+=head1 FUNCTIONS
+
+Games::Ratings::LogisticElo inherits from L<Games::Ratings>, see that
+module's documentation for information about the inherited methods.
+
+Nothing is exported by default, the function B<multi_elo> can be
+exported on request.
+
+=over
+
+=item B<$self>->I<get_rating_change>
+
+Computes and returns how much a player's rating changes after the
+games added.
+
+=item B<$self>->I<get_new_rating>
+
+Adds the result of I<get_rating_change> to the old rating of the
+player and returns this.
+
+=item B<multi_elo> [$coefficient], @results
+
+Computes the ratings after a competition with an arbitrary number of
+players.
+
+The first argument is the coefficient. It is optional, with the
+default coefficient being 15. The next arguments are the results of
+the players. Each result is a 2-element arrayref, the first element
+being the Elo rating of the player, and the second element being the
+score that player obtained. The scores are only used to compare
+players, their absolute values are irrelevant.
+
+The return value is a list (in list context) or arrayref (in scalar
+context) of ratings of all players after the competition, in the same
+order as the arguments.
+
+This function computes the ratings by considering that each player
+played a game with every other player, with the winner of every game
+being the player who got the highest score.
+
+=back
+
+
+=head1 SEE ALSO
+
+L<Games::Ratings::Chess::FIDE>, L<Games::Ratings>
+
+L<https://en.wikipedia.org/wiki/Elo_rating>
+
+=head1 AUTHOR
+
+Marius Gavrilescu <marius@ieval.ro>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2016 by Marius Gavrilescu
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.22.2 or,
+at your option, any later version of Perl 5 you may have available.
+
+
+=cut
diff --git a/t/Games-Ratings-LogisticElo.t b/t/Games-Ratings-LogisticElo.t
new file mode 100644 (file)
index 0000000..e256010
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+use Test::More tests => 7;
+BEGIN { use_ok('Games::Ratings::LogisticElo', 'multi_elo') };
+
+my $player = Games::Ratings::LogisticElo->new;
+$player->set_rating(2240);
+$player->set_coefficient(15);
+$player->add_game({
+  opponent_rating => 2114,
+  result => 'win',
+});
+my $rating_change = int ($player->get_rating_change + 0.5);
+my $new_rating = int ($player->get_new_rating + 0.5);
+
+is $rating_change, 5, 'rating change';
+is $new_rating, 2245, 'new rating';
+
+
+sub clean_ratings {
+       map { int ($_ + 0.5) } @_
+}
+
+my @ratings = clean_ratings multi_elo [2240, 5];
+is_deeply [@ratings], [2240], 'multi_elo - 1 arg';
+
+@ratings = clean_ratings multi_elo [2240, 5], [2114, 2];
+is_deeply [@ratings], [2245, 2109], 'multi_elo - 2 args';
+
+@ratings = clean_ratings multi_elo 30, [2114, 2], [2240, 5];
+is_deeply [@ratings], [2104, 2250], 'multi_elo - 2 args, custom K';
+
+
+$player = Games::Ratings::LogisticElo->new;
+$player->set_rating(2240);
+$player->set_coefficient(15);
+$player->add_game({
+  opponent_rating => 2240,
+  result => 'draw',
+}) for 1 .. 100;
+$rating_change = int ($player->get_rating_change + 0.5);
+is $rating_change, 0, 'rating change (only draws against self)';
This page took 0.015885 seconds and 4 git commands to generate.