From e22c34f2ffb0ee9c76c4905acfe02245a7f87eff Mon Sep 17 00:00:00 2001 From: Mikael Muszynski Date: Sun, 19 Jan 2020 14:58:40 +0100 Subject: [PATCH] Run `mix format` (#1) Add formatter configuration and run `mix format`. Additionally, manually replace leading tabs in documentation examples with four spaces. --- .formatter.exs | 3 + lib/glicko.ex | 459 ++++++++++++++++++++++++------------------- lib/glicko/player.ex | 322 ++++++++++++++++-------------- lib/glicko/result.ex | 101 +++++----- mix.exs | 51 ++--- test/glicko_test.exs | 125 ++++++------ test/player_test.exs | 88 +++++---- test/result_test.exs | 28 +-- 8 files changed, 641 insertions(+), 536 deletions(-) create mode 100644 .formatter.exs diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..d304ff3 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,3 @@ +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/lib/glicko.ex b/lib/glicko.ex index dadff68..6c1ab8c 100644 --- a/lib/glicko.ex +++ b/lib/glicko.ex @@ -1,246 +1,311 @@ defmodule Glicko do - @moduledoc """ - Provides the implementation of the Glicko rating system. + @moduledoc """ + Provides the implementation of the Glicko rating system. - See the [specification](http://www.glicko.net/glicko/glicko2.pdf) for implementation details. + See the [specification](http://www.glicko.net/glicko/glicko2.pdf) for implementation details. - ## Usage + ## Usage - Get a player's new rating after a series of matches in a rating period. + Get a player's new rating after a series of matches in a rating period. - iex> results = [Result.new(Player.new_v1([rating: 1400, rating_deviation: 30]), :win), - ...> Result.new(Player.new_v1([rating: 1550, rating_deviation: 100]), :loss), - ...> Result.new(Player.new_v1([rating: 1700, rating_deviation: 300]), :loss)] - iex> player = Player.new_v1([rating: 1500, rating_deviation: 200]) - iex> Glicko.new_rating(player, results, [system_constant: 0.5]) - {1464.0506705393013, 151.51652412385727} + iex> results = [Result.new(Player.new_v1([rating: 1400, rating_deviation: 30]), :win), + ...> Result.new(Player.new_v1([rating: 1550, rating_deviation: 100]), :loss), + ...> Result.new(Player.new_v1([rating: 1700, rating_deviation: 300]), :loss)] + iex> player = Player.new_v1([rating: 1500, rating_deviation: 200]) + iex> Glicko.new_rating(player, results, [system_constant: 0.5]) + {1464.0506705393013, 151.51652412385727} - Get a player's new rating when they haven't played within a rating period. + Get a player's new rating when they haven't played within a rating period. - iex> player = Player.new_v1([rating: 1500, rating_deviation: 200]) - iex> Glicko.new_rating(player, [], [system_constant: 0.5]) - {1.5e3, 200.27141669877065} + iex> player = Player.new_v1([rating: 1500, rating_deviation: 200]) + iex> Glicko.new_rating(player, [], [system_constant: 0.5]) + {1.5e3, 200.27141669877065} - Calculate the probability of a player winning against an opponent. + Calculate the probability of a player winning against an opponent. - iex> player = Player.new_v1 - iex> opponent = Player.new_v1 - iex> Glicko.win_probability(player, opponent) - 0.5 + iex> player = Player.new_v1 + iex> opponent = Player.new_v1 + iex> Glicko.win_probability(player, opponent) + 0.5 - Calculate the probability of a player drawing against an opponent. + Calculate the probability of a player drawing against an opponent. - iex> player = Player.new_v1 - iex> opponent = Player.new_v1 - iex> Glicko.draw_probability(player, opponent) - 1.0 + iex> player = Player.new_v1 + iex> opponent = Player.new_v1 + iex> Glicko.draw_probability(player, opponent) + 1.0 - """ + """ - alias __MODULE__.{ - Player, - Result, - } + alias __MODULE__.{ + Player, + Result + } - @default_system_constant 0.8 - @default_convergence_tolerance 1.0e-7 + @default_system_constant 0.8 + @default_convergence_tolerance 1.0e-7 - @type new_rating_opts :: [system_constant: float, convergence_tolerance: float] + @type new_rating_opts :: [system_constant: float, convergence_tolerance: float] - @doc """ - Calculates the probability of a player winning against an opponent. + @doc """ + Calculates the probability of a player winning against an opponent. - Returns a value between `0.0` and `1.0`. - """ - @spec win_probability(player :: Player.t, opponent :: Player.t) :: float - def win_probability(player, opponent) do - win_probability(player |> Player.rating(:v2), opponent |> Player.rating(:v2), opponent |> Player.rating_deviation(:v2)) - end + Returns a value between `0.0` and `1.0`. + """ + @spec win_probability(player :: Player.t(), opponent :: Player.t()) :: float + def win_probability(player, opponent) do + win_probability( + player |> Player.rating(:v2), + opponent |> Player.rating(:v2), + opponent |> Player.rating_deviation(:v2) + ) + end - @doc """ - Calculates the probability of a player winning against an opponent from a player rating, opponent rating and opponent rating deviation. + @doc """ + Calculates the probability of a player winning against an opponent from a player rating, opponent rating and opponent rating deviation. - Values provided for the player rating, opponent rating and opponent rating deviation must be *v2* based. + Values provided for the player rating, opponent rating and opponent rating deviation must be *v2* based. - Returns a value between `0.0` and `1.0`. - """ - @spec win_probability(player_rating :: Player.rating, opponent_rating :: Player.rating, opponent_rating_deviation :: Player.rating_deviation) :: float - def win_probability(player_rating, opponent_rating, opponent_rating_deviation) do - calc_e(player_rating, opponent_rating, calc_g(opponent_rating_deviation)) - end + Returns a value between `0.0` and `1.0`. + """ + @spec win_probability( + player_rating :: Player.rating(), + opponent_rating :: Player.rating(), + opponent_rating_deviation :: Player.rating_deviation() + ) :: float + def win_probability(player_rating, opponent_rating, opponent_rating_deviation) do + calc_e(player_rating, opponent_rating, calc_g(opponent_rating_deviation)) + end - @doc """ - Calculates the probability of a player drawing against an opponent. + @doc """ + Calculates the probability of a player drawing against an opponent. - Returns a value between `0.0` and `1.0`. - """ - @spec draw_probability(player :: Player.t, opponent :: Player.t) :: float - def draw_probability(player, opponent) do - draw_probability(player |> Player.rating(:v2), opponent |> Player.rating(:v2), opponent |> Player.rating_deviation(:v2)) - end + Returns a value between `0.0` and `1.0`. + """ + @spec draw_probability(player :: Player.t(), opponent :: Player.t()) :: float + def draw_probability(player, opponent) do + draw_probability( + player |> Player.rating(:v2), + opponent |> Player.rating(:v2), + opponent |> Player.rating_deviation(:v2) + ) + end - @doc """ - Calculates the probability of a player drawing against an opponent from a player rating, opponent rating and opponent rating deviation. + @doc """ + Calculates the probability of a player drawing against an opponent from a player rating, opponent rating and opponent rating deviation. - Values provided for the player rating, opponent rating and opponent rating deviation must be *v2* based. + Values provided for the player rating, opponent rating and opponent rating deviation must be *v2* based. - Returns a value between `0.0` and `1.0`. - """ - @spec draw_probability(player_rating :: Player.rating, opponent_rating :: Player.rating, opponent_rating_deviation :: Player.rating_deviation) :: float - def draw_probability(player_rating, opponent_rating, opponent_rating_deviation) do - 1 - abs(win_probability(player_rating, opponent_rating, opponent_rating_deviation) - 0.5) / 0.5 - end + Returns a value between `0.0` and `1.0`. + """ + @spec draw_probability( + player_rating :: Player.rating(), + opponent_rating :: Player.rating(), + opponent_rating_deviation :: Player.rating_deviation() + ) :: float + def draw_probability(player_rating, opponent_rating, opponent_rating_deviation) do + 1 - + abs(win_probability(player_rating, opponent_rating, opponent_rating_deviation) - 0.5) / 0.5 + end - @doc """ - Generate a new rating from an existing rating and a series (or lack) of results. + @doc """ + Generate a new rating from an existing rating and a series (or lack) of results. - Returns the updated player with the same version given to the function. - """ - @spec new_rating(player :: Player.t, results :: list(Result.t), opts :: new_rating_opts) :: Player.t - def new_rating(player, results, opts \\ []) - def new_rating(player, results, opts) when tuple_size(player) == 3 do - do_new_rating(player, results, opts) - end - def new_rating(player, results, opts) when tuple_size(player) == 2 do - player - |> Player.to_v2 - |> do_new_rating(results, opts) - |> Player.to_v1 - end + Returns the updated player with the same version given to the function. + """ + @spec new_rating(player :: Player.t(), results :: list(Result.t()), opts :: new_rating_opts) :: + Player.t() + def new_rating(player, results, opts \\ []) - defp do_new_rating({player_r, player_pre_rd, player_v}, [], _) do - player_post_rd = calc_player_post_base_rd(:math.pow(player_pre_rd, 2), player_v) + def new_rating(player, results, opts) when tuple_size(player) == 3 do + do_new_rating(player, results, opts) + end - {player_r, player_post_rd, player_v} - end - defp do_new_rating({player_pre_r, player_pre_rd, player_pre_v}, results, opts) do - sys_const = Keyword.get(opts, :system_constant, @default_system_constant) - conv_tol = Keyword.get(opts, :convergence_tolerance, @default_convergence_tolerance) + def new_rating(player, results, opts) when tuple_size(player) == 2 do + player + |> Player.to_v2() + |> do_new_rating(results, opts) + |> Player.to_v1() + end - # Initialization (skips steps 1, 2 and 3) - player_pre_rd_sq = :math.pow(player_pre_rd, 2) - {variance_est, results_effect} = result_calculations(results, player_pre_r) - # Step 4 - delta = calc_delta(results_effect, variance_est) - # Step 5.1 - alpha = calc_alpha(player_pre_v) - # Step 5.2 - k = calc_k(alpha, delta, player_pre_rd_sq, variance_est, sys_const, 1) - {initial_a, initial_b} = iterative_algorithm_initial( - alpha, delta, player_pre_rd_sq, variance_est, sys_const, k - ) - # Step 5.3 - initial_fa = calc_f(alpha, delta, player_pre_rd_sq, variance_est, sys_const, initial_a) - initial_fb = calc_f(alpha, delta, player_pre_rd_sq, variance_est, sys_const, initial_b) - # Step 5.4 - a = iterative_algorithm_body( - alpha, delta, player_pre_rd_sq, variance_est, sys_const, conv_tol, - initial_a, initial_b, initial_fa, initial_fb - ) - # Step 5.5 - player_post_v = calc_new_player_volatility(a) - # Step 6 - player_post_base_rd = calc_player_post_base_rd(player_pre_rd_sq, player_post_v) - # Step 7 - player_post_rd = calc_new_player_rating_deviation(player_post_base_rd, variance_est) - player_post_r = calc_new_player_rating(results_effect, player_pre_r, player_post_rd) + defp do_new_rating({player_r, player_pre_rd, player_v}, [], _) do + player_post_rd = calc_player_post_base_rd(:math.pow(player_pre_rd, 2), player_v) - {player_post_r, player_post_rd, player_post_v} - end + {player_r, player_post_rd, player_v} + end - defp result_calculations(results, player_pre_r) do - {variance_estimate_acc, result_effect_acc} = - Enum.reduce(results, {0.0, 0.0}, fn result, {variance_estimate_acc, result_effect_acc} -> - opponent_rd_g = - result - |> Result.opponent_rating_deviation - |> calc_g + defp do_new_rating({player_pre_r, player_pre_rd, player_pre_v}, results, opts) do + sys_const = Keyword.get(opts, :system_constant, @default_system_constant) + conv_tol = Keyword.get(opts, :convergence_tolerance, @default_convergence_tolerance) - win_probability = calc_e(player_pre_r, Result.opponent_rating(result), opponent_rd_g) + # Initialization (skips steps 1, 2 and 3) + player_pre_rd_sq = :math.pow(player_pre_rd, 2) + {variance_est, results_effect} = result_calculations(results, player_pre_r) + # Step 4 + delta = calc_delta(results_effect, variance_est) + # Step 5.1 + alpha = calc_alpha(player_pre_v) + # Step 5.2 + k = calc_k(alpha, delta, player_pre_rd_sq, variance_est, sys_const, 1) - { - variance_estimate_acc + :math.pow(opponent_rd_g, 2) * win_probability * (1 - win_probability), - result_effect_acc + opponent_rd_g * (Result.score(result) - win_probability) - } - end) + {initial_a, initial_b} = + iterative_algorithm_initial( + alpha, + delta, + player_pre_rd_sq, + variance_est, + sys_const, + k + ) - {:math.pow(variance_estimate_acc, -1), result_effect_acc} - end + # Step 5.3 + initial_fa = calc_f(alpha, delta, player_pre_rd_sq, variance_est, sys_const, initial_a) + initial_fb = calc_f(alpha, delta, player_pre_rd_sq, variance_est, sys_const, initial_b) + # Step 5.4 + a = + iterative_algorithm_body( + alpha, + delta, + player_pre_rd_sq, + variance_est, + sys_const, + conv_tol, + initial_a, + initial_b, + initial_fa, + initial_fb + ) - defp calc_delta(results_effect, variance_est) do - results_effect * variance_est - end + # Step 5.5 + player_post_v = calc_new_player_volatility(a) + # Step 6 + player_post_base_rd = calc_player_post_base_rd(player_pre_rd_sq, player_post_v) + # Step 7 + player_post_rd = calc_new_player_rating_deviation(player_post_base_rd, variance_est) + player_post_r = calc_new_player_rating(results_effect, player_pre_r, player_post_rd) - defp calc_f(alpha, delta, player_pre_rd_sq, variance_est, sys_const, x) do - :math.exp(x) * - (:math.pow(delta, 2) - :math.exp(x) - player_pre_rd_sq - variance_est) / - (2 * :math.pow(player_pre_rd_sq + variance_est + :math.exp(x), 2)) - - (x - alpha) / :math.pow(sys_const, 2) - end + {player_post_r, player_post_rd, player_post_v} + end - defp calc_alpha(player_pre_v) do - :math.log(:math.pow(player_pre_v, 2)) - end + defp result_calculations(results, player_pre_r) do + {variance_estimate_acc, result_effect_acc} = + Enum.reduce(results, {0.0, 0.0}, fn result, {variance_estimate_acc, result_effect_acc} -> + opponent_rd_g = + result + |> Result.opponent_rating_deviation() + |> calc_g - defp calc_new_player_volatility(a) do - :math.exp(a / 2) - end + win_probability = calc_e(player_pre_r, Result.opponent_rating(result), opponent_rd_g) - defp calc_new_player_rating(results_effect, player_pre_r, player_post_rd) do - player_pre_r + :math.pow(player_post_rd, 2) * results_effect - end + { + variance_estimate_acc + + :math.pow(opponent_rd_g, 2) * win_probability * (1 - win_probability), + result_effect_acc + opponent_rd_g * (Result.score(result) - win_probability) + } + end) - defp calc_new_player_rating_deviation(player_post_base_rd, variance_est) do - 1 / :math.sqrt(1 / :math.pow(player_post_base_rd, 2) + 1 / variance_est) - end + {:math.pow(variance_estimate_acc, -1), result_effect_acc} + end - defp calc_player_post_base_rd(player_pre_rd_sq, player_pre_v) do - :math.sqrt((:math.pow(player_pre_v, 2) + player_pre_rd_sq)) - end + defp calc_delta(results_effect, variance_est) do + results_effect * variance_est + end - defp iterative_algorithm_initial(alpha, delta, player_pre_rd_sq, variance_est, sys_const, k) do - initial_a = alpha - initial_b = - if :math.pow(delta, 2) > player_pre_rd_sq + variance_est do - :math.log(:math.pow(delta, 2) - player_pre_rd_sq - variance_est) - else - alpha - k * sys_const - end + defp calc_f(alpha, delta, player_pre_rd_sq, variance_est, sys_const, x) do + :math.exp(x) * + (:math.pow(delta, 2) - :math.exp(x) - player_pre_rd_sq - variance_est) / + (2 * :math.pow(player_pre_rd_sq + variance_est + :math.exp(x), 2)) - + (x - alpha) / :math.pow(sys_const, 2) + end - {initial_a, initial_b} - end + defp calc_alpha(player_pre_v) do + :math.log(:math.pow(player_pre_v, 2)) + end - defp iterative_algorithm_body(alpha, delta, player_pre_rd_sq, variance_est, sys_const, conv_tol, a, b, fa, fb) do - if abs(b - a) > conv_tol do - c = a + (a - b) * fa / (fb - fa) - fc = calc_f(alpha, delta, player_pre_rd_sq, variance_est, sys_const, c) - {a, fa} = - if fc * fb < 0 do - {b, fb} - else - {a, fa / 2} - end - iterative_algorithm_body(alpha, delta, player_pre_rd_sq, variance_est, sys_const, conv_tol, a, c, fa, fc) - else - a - end - end + defp calc_new_player_volatility(a) do + :math.exp(a / 2) + end - defp calc_k(alpha, delta, player_pre_rd_sq, variance_est, sys_const, k) do - if calc_f(alpha, delta, player_pre_rd_sq, variance_est, sys_const, alpha - k * sys_const) < 0 do - calc_k(alpha, delta, player_pre_rd_sq, variance_est, sys_const, k + 1) - else - k - end - end + defp calc_new_player_rating(results_effect, player_pre_r, player_post_rd) do + player_pre_r + :math.pow(player_post_rd, 2) * results_effect + end - # g function - defp calc_g(rd) do - 1 / :math.sqrt(1 + 3 * :math.pow(rd, 2) / :math.pow(:math.pi, 2)) - end + defp calc_new_player_rating_deviation(player_post_base_rd, variance_est) do + 1 / :math.sqrt(1 / :math.pow(player_post_base_rd, 2) + 1 / variance_est) + end - # E function - defp calc_e(player_pre_r, opponent_r, opponent_rd_g) do - 1 / (1 + :math.exp(-1 * opponent_rd_g * (player_pre_r - opponent_r))) - end + defp calc_player_post_base_rd(player_pre_rd_sq, player_pre_v) do + :math.sqrt(:math.pow(player_pre_v, 2) + player_pre_rd_sq) + end + + defp iterative_algorithm_initial(alpha, delta, player_pre_rd_sq, variance_est, sys_const, k) do + initial_a = alpha + + initial_b = + if :math.pow(delta, 2) > player_pre_rd_sq + variance_est do + :math.log(:math.pow(delta, 2) - player_pre_rd_sq - variance_est) + else + alpha - k * sys_const + end + + {initial_a, initial_b} + end + + defp iterative_algorithm_body( + alpha, + delta, + player_pre_rd_sq, + variance_est, + sys_const, + conv_tol, + a, + b, + fa, + fb + ) do + if abs(b - a) > conv_tol do + c = a + (a - b) * fa / (fb - fa) + fc = calc_f(alpha, delta, player_pre_rd_sq, variance_est, sys_const, c) + + {a, fa} = + if fc * fb < 0 do + {b, fb} + else + {a, fa / 2} + end + + iterative_algorithm_body( + alpha, + delta, + player_pre_rd_sq, + variance_est, + sys_const, + conv_tol, + a, + c, + fa, + fc + ) + else + a + end + end + + defp calc_k(alpha, delta, player_pre_rd_sq, variance_est, sys_const, k) do + if calc_f(alpha, delta, player_pre_rd_sq, variance_est, sys_const, alpha - k * sys_const) < 0 do + calc_k(alpha, delta, player_pre_rd_sq, variance_est, sys_const, k + 1) + else + k + end + end + + # g function + defp calc_g(rd) do + 1 / :math.sqrt(1 + 3 * :math.pow(rd, 2) / :math.pow(:math.pi(), 2)) + end + + # E function + defp calc_e(player_pre_r, opponent_r, opponent_rd_g) do + 1 / (1 + :math.exp(-1 * opponent_rd_g * (player_pre_r - opponent_r))) + end end diff --git a/lib/glicko/player.ex b/lib/glicko/player.ex index 4e055f5..dc4f574 100644 --- a/lib/glicko/player.ex +++ b/lib/glicko/player.ex @@ -1,192 +1,214 @@ defmodule Glicko.Player do - @moduledoc """ - Provides convenience functions that handle conversions between Glicko versions one and two. + @moduledoc """ + Provides convenience functions that handle conversions between Glicko versions one and two. - ## Usage + ## Usage - Create a *v1* player with the default values for an unrated player. + Create a *v1* player with the default values for an unrated player. - iex> Player.new_v1 - {1.5e3, 350.0} + iex> Player.new_v1 + {1.5e3, 350.0} - Create a *v2* player with the default values for an unrated player. + Create a *v2* player with the default values for an unrated player. - iex> Player.new_v2 - {0.0, 2.014761872416068, 0.06} + iex> Player.new_v2 + {0.0, 2.014761872416068, 0.06} - Create a player with custom values. + Create a player with custom values. - iex> Player.new_v2([rating: 3.0, rating_deviation: 2.0, volatility: 0.05]) - {3.0, 2.0, 0.05} + iex> Player.new_v2([rating: 3.0, rating_deviation: 2.0, volatility: 0.05]) + {3.0, 2.0, 0.05} - Convert a *v2* player to a *v1*. Note this drops the volatility. + Convert a *v2* player to a *v1*. Note this drops the volatility. - iex> Player.new_v2 |> Player.to_v1 - {1.5e3, 350.0} + iex> Player.new_v2 |> Player.to_v1 + {1.5e3, 350.0} - Convert a *v1* player to a *v2*. + Convert a *v1* player to a *v2*. - iex> Player.new_v1 |> Player.to_v2(0.06) - {0.0, 2.014761872416068, 0.06} + iex> Player.new_v1 |> Player.to_v2(0.06) + {0.0, 2.014761872416068, 0.06} - Note calling `to_v1` with a *v1* player or likewise with `to_v2` and a *v2* player - will pass-through unchanged. The volatility arg in this case is ignored. + Note calling `to_v1` with a *v1* player or likewise with `to_v2` and a *v2* player + will pass-through unchanged. The volatility arg in this case is ignored. - iex> player_v2 = Player.new_v2 - iex> player_v2 == Player.to_v2(player_v2) - true + iex> player_v2 = Player.new_v2 + iex> player_v2 == Player.to_v2(player_v2) + true - """ + """ - @magic_version_scale 173.7178 - @magic_version_scale_rating 1500.0 + @magic_version_scale 173.7178 + @magic_version_scale_rating 1500.0 - @type t :: v1 | v2 + @type t :: v1 | v2 - @type v1 :: {rating, rating_deviation} - @type v2 :: {rating, rating_deviation, volatility} + @type v1 :: {rating, rating_deviation} + @type v2 :: {rating, rating_deviation, volatility} - @type version :: :v1 | :v2 - @type rating :: float - @type rating_deviation :: float - @type volatility :: float + @type version :: :v1 | :v2 + @type rating :: float + @type rating_deviation :: float + @type volatility :: float - @doc """ - The recommended initial rating value for a new player. - """ - @spec initial_rating(version) :: rating - def initial_rating(_version = :v1), do: 1500.0 - def initial_rating(_version = :v2), do: :v1 |> initial_rating |> scale_rating_to(:v2) + @doc """ + The recommended initial rating value for a new player. + """ + @spec initial_rating(version) :: rating + def initial_rating(_version = :v1), do: 1500.0 + def initial_rating(_version = :v2), do: :v1 |> initial_rating |> scale_rating_to(:v2) - @doc """ - The recommended initial rating deviation value for a new player. - """ - @spec initial_rating_deviation(version) :: rating_deviation - def initial_rating_deviation(_version = :v1), do: 350.0 - def initial_rating_deviation(_version = :v2), do: :v1 |> initial_rating_deviation |> scale_rating_deviation_to(:v2) + @doc """ + The recommended initial rating deviation value for a new player. + """ + @spec initial_rating_deviation(version) :: rating_deviation + def initial_rating_deviation(_version = :v1), do: 350.0 - @doc """ - The recommended initial volatility value for a new player. - """ - @spec initial_volatility :: volatility - def initial_volatility, do: 0.06 + def initial_rating_deviation(_version = :v2), + do: :v1 |> initial_rating_deviation |> scale_rating_deviation_to(:v2) - @doc """ - Creates a new v1 player. + @doc """ + The recommended initial volatility value for a new player. + """ + @spec initial_volatility :: volatility + def initial_volatility, do: 0.06 - If not overriden, will use the default values for an unrated player. - """ - @spec new_v1([rating: rating, rating_deviation: rating_deviation]) :: v1 - def new_v1(opts \\ []) when is_list(opts), do: { - Keyword.get(opts, :rating, initial_rating(:v1)), - Keyword.get(opts, :rating_deviation, initial_rating_deviation(:v1)), - } + @doc """ + Creates a new v1 player. - @doc """ - Creates a new v2 player. + If not overriden, will use the default values for an unrated player. + """ + @spec new_v1(rating: rating, rating_deviation: rating_deviation) :: v1 + def new_v1(opts \\ []) when is_list(opts), + do: { + Keyword.get(opts, :rating, initial_rating(:v1)), + Keyword.get(opts, :rating_deviation, initial_rating_deviation(:v1)) + } - If not overriden, will use default values for an unrated player. - """ - @spec new_v2([rating: rating, rating_deviation: rating_deviation, volatility: volatility]) :: v2 - def new_v2(opts \\ []) when is_list(opts), do: { - Keyword.get(opts, :rating, initial_rating(:v2)), - Keyword.get(opts, :rating_deviation, initial_rating_deviation(:v2)), - Keyword.get(opts, :volatility, initial_volatility()), - } + @doc """ + Creates a new v2 player. - @doc """ - Converts a v2 player to a v1. + If not overriden, will use default values for an unrated player. + """ + @spec new_v2(rating: rating, rating_deviation: rating_deviation, volatility: volatility) :: v2 + def new_v2(opts \\ []) when is_list(opts), + do: { + Keyword.get(opts, :rating, initial_rating(:v2)), + Keyword.get(opts, :rating_deviation, initial_rating_deviation(:v2)), + Keyword.get(opts, :volatility, initial_volatility()) + } - A v1 player will pass-through unchanged. + @doc """ + Converts a v2 player to a v1. - Note the volatility field used in a v2 player will be lost in the conversion. - """ - @spec to_v1(player :: t) :: v1 - def to_v1({rating, rating_deviation}), do: {rating, rating_deviation} - def to_v1({rating, rating_deviation, _}), do: { - rating |> scale_rating_to(:v1), - rating_deviation |> scale_rating_deviation_to(:v1), - } + A v1 player will pass-through unchanged. - @doc """ - Converts a v1 player to a v2. + Note the volatility field used in a v2 player will be lost in the conversion. + """ + @spec to_v1(player :: t) :: v1 + def to_v1({rating, rating_deviation}), do: {rating, rating_deviation} - A v2 player will pass-through unchanged with the volatility arg ignored. - """ - @spec to_v2(player :: t, volatility :: volatility) :: v2 - def to_v2(player, volatility \\ initial_volatility()) - def to_v2({rating, rating_deviation, volatility}, _volatility), do: {rating, rating_deviation, volatility} - def to_v2({rating, rating_deviation}, volatility), do: { - rating |> scale_rating_to(:v2), - rating_deviation |> scale_rating_deviation_to(:v2), - volatility, - } + def to_v1({rating, rating_deviation, _}), + do: { + rating |> scale_rating_to(:v1), + rating_deviation |> scale_rating_deviation_to(:v1) + } - @doc """ - A version agnostic method for getting a player's rating. - """ - @spec rating(player :: t, as_version :: version) :: rating - def rating(player, as_version \\ nil) - def rating({rating, _}, nil), do: rating - def rating({rating, _, _}, nil), do: rating - def rating({rating, _}, :v1), do: rating - def rating({rating, _}, :v2), do: rating |> scale_rating_to(:v2) - def rating({rating, _, _}, :v1), do: rating |> scale_rating_to(:v1) - def rating({rating, _, _}, :v2), do: rating + @doc """ + Converts a v1 player to a v2. - @doc """ - A version agnostic method for getting a player's rating deviation. - """ - @spec rating_deviation(player :: t, as_version :: version) :: rating_deviation - def rating_deviation(player, as_version \\ nil) - def rating_deviation({_, rating_deviation}, nil), do: rating_deviation - def rating_deviation({_, rating_deviation, _}, nil), do: rating_deviation - def rating_deviation({_, rating_deviation}, :v1), do: rating_deviation - def rating_deviation({_, rating_deviation}, :v2), do: rating_deviation |> scale_rating_deviation_to(:v2) - def rating_deviation({_, rating_deviation, _}, :v1), do: rating_deviation |> scale_rating_deviation_to(:v1) - def rating_deviation({_, rating_deviation, _}, :v2), do: rating_deviation + A v2 player will pass-through unchanged with the volatility arg ignored. + """ + @spec to_v2(player :: t, volatility :: volatility) :: v2 + def to_v2(player, volatility \\ initial_volatility()) - @doc """ - A version agnostic method for getting a player's volatility. - """ - @spec volatility(player :: t, default_volatility :: volatility) :: volatility - def volatility(player, default_volatility \\ initial_volatility()) - def volatility({_, _}, default_volatility), do: default_volatility - def volatility({_, _, volatility}, _), do: volatility + def to_v2({rating, rating_deviation, volatility}, _volatility), + do: {rating, rating_deviation, volatility} - @doc """ - A convenience function for summarizing a player's strength as a 95% - confidence interval. + def to_v2({rating, rating_deviation}, volatility), + do: { + rating |> scale_rating_to(:v2), + rating_deviation |> scale_rating_deviation_to(:v2), + volatility + } - The lowest value in the interval is the player's rating minus twice the RD, - and the highest value is the player's rating plus twice the RD. - The volatility measure does not appear in the calculation of this interval. + @doc """ + A version agnostic method for getting a player's rating. + """ + @spec rating(player :: t, as_version :: version) :: rating + def rating(player, as_version \\ nil) + def rating({rating, _}, nil), do: rating + def rating({rating, _, _}, nil), do: rating + def rating({rating, _}, :v1), do: rating + def rating({rating, _}, :v2), do: rating |> scale_rating_to(:v2) + def rating({rating, _, _}, :v1), do: rating |> scale_rating_to(:v1) + def rating({rating, _, _}, :v2), do: rating - An example would be if a player's rating is 1850 and the RD is 50, - the interval would range from 1750 to 1950. We would then say that we're 95% - confident that the player's actual strength is between 1750 and 1950. + @doc """ + A version agnostic method for getting a player's rating deviation. + """ + @spec rating_deviation(player :: t, as_version :: version) :: rating_deviation + def rating_deviation(player, as_version \\ nil) + def rating_deviation({_, rating_deviation}, nil), do: rating_deviation + def rating_deviation({_, rating_deviation, _}, nil), do: rating_deviation + def rating_deviation({_, rating_deviation}, :v1), do: rating_deviation - When a player has a low RD, the interval would be narrow, so that we would - be 95% confident about a player’s strength being in a small interval of values. - """ - @spec rating_interval(player :: t) :: {rating_low :: float, rating_high :: float} - def rating_interval(player, as_version \\ nil), do: { - rating(player, as_version) - rating_deviation(player, as_version) * 2, - rating(player, as_version) + rating_deviation(player, as_version) * 2, - } + def rating_deviation({_, rating_deviation}, :v2), + do: rating_deviation |> scale_rating_deviation_to(:v2) - @doc """ - Scales a player's rating. - """ - @spec scale_rating_to(rating :: rating, to_version :: version) :: rating - def scale_rating_to(rating, _version = :v1), do: (rating * @magic_version_scale) + @magic_version_scale_rating - def scale_rating_to(rating, _version = :v2), do: (rating - @magic_version_scale_rating) / @magic_version_scale + def rating_deviation({_, rating_deviation, _}, :v1), + do: rating_deviation |> scale_rating_deviation_to(:v1) - @doc """ - Scales a player's rating deviation. - """ - @spec scale_rating_deviation_to(rating_deviation :: rating_deviation, to_version :: version) :: rating_deviation - def scale_rating_deviation_to(rating_deviation, _version = :v1), do: rating_deviation * @magic_version_scale - def scale_rating_deviation_to(rating_deviation, _version = :v2), do: rating_deviation / @magic_version_scale + def rating_deviation({_, rating_deviation, _}, :v2), do: rating_deviation + @doc """ + A version agnostic method for getting a player's volatility. + """ + @spec volatility(player :: t, default_volatility :: volatility) :: volatility + def volatility(player, default_volatility \\ initial_volatility()) + def volatility({_, _}, default_volatility), do: default_volatility + def volatility({_, _, volatility}, _), do: volatility + + @doc """ + A convenience function for summarizing a player's strength as a 95% + confidence interval. + + The lowest value in the interval is the player's rating minus twice the RD, + and the highest value is the player's rating plus twice the RD. + The volatility measure does not appear in the calculation of this interval. + + An example would be if a player's rating is 1850 and the RD is 50, + the interval would range from 1750 to 1950. We would then say that we're 95% + confident that the player's actual strength is between 1750 and 1950. + + When a player has a low RD, the interval would be narrow, so that we would + be 95% confident about a player’s strength being in a small interval of values. + """ + @spec rating_interval(player :: t) :: {rating_low :: float, rating_high :: float} + def rating_interval(player, as_version \\ nil), + do: { + rating(player, as_version) - rating_deviation(player, as_version) * 2, + rating(player, as_version) + rating_deviation(player, as_version) * 2 + } + + @doc """ + Scales a player's rating. + """ + @spec scale_rating_to(rating :: rating, to_version :: version) :: rating + def scale_rating_to(rating, _version = :v1), + do: rating * @magic_version_scale + @magic_version_scale_rating + + def scale_rating_to(rating, _version = :v2), + do: (rating - @magic_version_scale_rating) / @magic_version_scale + + @doc """ + Scales a player's rating deviation. + """ + @spec scale_rating_deviation_to(rating_deviation :: rating_deviation, to_version :: version) :: + rating_deviation + def scale_rating_deviation_to(rating_deviation, _version = :v1), + do: rating_deviation * @magic_version_scale + + def scale_rating_deviation_to(rating_deviation, _version = :v2), + do: rating_deviation / @magic_version_scale end diff --git a/lib/glicko/result.ex b/lib/glicko/result.ex index f66418a..4b9432e 100644 --- a/lib/glicko/result.ex +++ b/lib/glicko/result.ex @@ -1,68 +1,69 @@ defmodule Glicko.Result do - @moduledoc """ - Provides convenience functions for handling a result against an opponent. + @moduledoc """ + Provides convenience functions for handling a result against an opponent. - ## Usage + ## Usage - iex> opponent = Player.new_v2 - iex> Result.new(opponent, 1.0) - {0.0, 2.014761872416068, 1.0} - iex> Result.new(opponent, :draw) # With shortcut - {0.0, 2.014761872416068, 0.5} + iex> opponent = Player.new_v2 + iex> Result.new(opponent, 1.0) + {0.0, 2.014761872416068, 1.0} + iex> Result.new(opponent, :draw) # With shortcut + {0.0, 2.014761872416068, 0.5} - """ + """ - alias Glicko.Player + alias Glicko.Player - @type t :: {Player.rating, Player.rating_deviation, score} + @type t :: {Player.rating(), Player.rating_deviation(), score} - @type score :: float - @type score_shortcut :: :loss | :draw | :win + @type score :: float + @type score_shortcut :: :loss | :draw | :win - @score_shortcut_map %{loss: 0.0, draw: 0.5, win: 1.0} - @score_shortcuts Map.keys(@score_shortcut_map) + @score_shortcut_map %{loss: 0.0, draw: 0.5, win: 1.0} + @score_shortcuts Map.keys(@score_shortcut_map) - @doc """ - Creates a new result from an opponent rating, opponent rating deviation and score. + @doc """ + Creates a new result from an opponent rating, opponent rating deviation and score. - Values provided for the opponent rating and opponent rating deviation must be *v2* based. + Values provided for the opponent rating and opponent rating deviation must be *v2* based. - Supports passing either `:loss`, `:draw`, or `:win` as shortcuts. - """ - @spec new(Player.rating, Player.rating_deviation, score | score_shortcut) :: t - def new(opponent_rating, opponent_rating_deviation, score) when is_number(score) do - {opponent_rating, opponent_rating_deviation, score} - end - def new(opponent_rating, opponent_rating_deviation, score_type) when is_atom(score_type) and score_type in @score_shortcuts do - {opponent_rating, opponent_rating_deviation, Map.fetch!(@score_shortcut_map, score_type)} - end + Supports passing either `:loss`, `:draw`, or `:win` as shortcuts. + """ + @spec new(Player.rating(), Player.rating_deviation(), score | score_shortcut) :: t + def new(opponent_rating, opponent_rating_deviation, score) when is_number(score) do + {opponent_rating, opponent_rating_deviation, score} + end - @doc """ - Creates a new result from an opponent and score. + def new(opponent_rating, opponent_rating_deviation, score_type) + when is_atom(score_type) and score_type in @score_shortcuts do + {opponent_rating, opponent_rating_deviation, Map.fetch!(@score_shortcut_map, score_type)} + end - Supports passing either `:loss`, `:draw`, or `:win` as shortcuts. - """ - @spec new(opponent :: Player.t, score :: score | score_shortcut) :: t - def new(opponent, score) do - new(Player.rating(opponent, :v2), Player.rating_deviation(opponent, :v2), score) - end + @doc """ + Creates a new result from an opponent and score. - @doc """ - Convenience function for accessing an opponent's rating. - """ - @spec opponent_rating(result :: Result.t) :: Player.rating - def opponent_rating(_result = {rating, _, _}), do: rating + Supports passing either `:loss`, `:draw`, or `:win` as shortcuts. + """ + @spec new(opponent :: Player.t(), score :: score | score_shortcut) :: t + def new(opponent, score) do + new(Player.rating(opponent, :v2), Player.rating_deviation(opponent, :v2), score) + end - @doc """ - Convenience function for accessing an opponent's rating deviation. - """ - @spec opponent_rating_deviation(result :: Result.t) :: Player.rating_deviation - def opponent_rating_deviation(_result = {_, rating_deviation, _}), do: rating_deviation + @doc """ + Convenience function for accessing an opponent's rating. + """ + @spec opponent_rating(result :: Result.t()) :: Player.rating() + def opponent_rating(_result = {rating, _, _}), do: rating - @doc """ - Convenience function for accessing the score. - """ - @spec score(result :: Result.t) :: score - def score(_result = {_, _, score}), do: score + @doc """ + Convenience function for accessing an opponent's rating deviation. + """ + @spec opponent_rating_deviation(result :: Result.t()) :: Player.rating_deviation() + def opponent_rating_deviation(_result = {_, rating_deviation, _}), do: rating_deviation + @doc """ + Convenience function for accessing the score. + """ + @spec score(result :: Result.t()) :: score + def score(_result = {_, _, score}), do: score end diff --git a/mix.exs b/mix.exs index 9f30fe4..426ef03 100644 --- a/mix.exs +++ b/mix.exs @@ -1,30 +1,33 @@ defmodule Glicko.Mixfile do - use Mix.Project + use Mix.Project - @description """ - Implementation of the Glicko rating system - """ + @description """ + Implementation of the Glicko rating system + """ - def project, do: [ - app: :glicko, - version: "0.6.0", - elixir: "~> 1.5", - start_permanent: Mix.env == :prod, - deps: deps(), - package: package(), - description: @description, - ] + def project, + do: [ + app: :glicko, + version: "0.6.0", + elixir: "~> 1.5", + start_permanent: Mix.env() == :prod, + deps: deps(), + package: package(), + description: @description + ] - defp deps, do: [ - {:inch_ex, "~> 0.5", only: :docs}, - {:ex_doc, "~> 0.16", only: :dev, runtime: false}, - {:credo, "~> 0.8", only: [:dev, :test], runtime: false}, - ] + defp deps, + do: [ + {:inch_ex, "~> 0.5", only: :docs}, + {:ex_doc, "~> 0.16", only: :dev, runtime: false}, + {:credo, "~> 0.8", only: [:dev, :test], runtime: false} + ] - defp package, do: [ - name: :glicko, - maintainers: ["James Dyson"], - licenses: ["MIT"], - links: %{"GitHub" => "https://github.com/avitex/elixir-glicko"}, - ] + defp package, + do: [ + name: :glicko, + maintainers: ["James Dyson"], + licenses: ["MIT"], + links: %{"GitHub" => "https://github.com/avitex/elixir-glicko"} + ] end diff --git a/test/glicko_test.exs b/test/glicko_test.exs index eae5461..44b257a 100644 --- a/test/glicko_test.exs +++ b/test/glicko_test.exs @@ -1,68 +1,77 @@ defmodule GlickoTest do - use ExUnit.Case + use ExUnit.Case - alias Glicko.{ - Player, - Result, - } + alias Glicko.{ + Player, + Result + } - doctest Glicko + doctest Glicko - @player [rating: 1500, rating_deviation: 200] |> Player.new_v1 |> Player.to_v2 + @player [rating: 1500, rating_deviation: 200] |> Player.new_v1() |> Player.to_v2() - @results [ - Result.new(Player.new_v1([rating: 1400, rating_deviation: 30]), :win), - Result.new(Player.new_v1([rating: 1550, rating_deviation: 100]), :loss), - Result.new(Player.new_v1([rating: 1700, rating_deviation: 300]), :loss), - ] + @results [ + Result.new(Player.new_v1(rating: 1400, rating_deviation: 30), :win), + Result.new(Player.new_v1(rating: 1550, rating_deviation: 100), :loss), + Result.new(Player.new_v1(rating: 1700, rating_deviation: 300), :loss) + ] - @valid_player_rating_after_results 1464.06 |> Player.scale_rating_to(:v2) - @valid_player_rating_deviation_after_results 151.52 |> Player.scale_rating_deviation_to(:v2) - @valid_player_volatility_after_results 0.05999 + @valid_player_rating_after_results 1464.06 |> Player.scale_rating_to(:v2) + @valid_player_rating_deviation_after_results 151.52 |> Player.scale_rating_deviation_to(:v2) + @valid_player_volatility_after_results 0.05999 - @valid_player_rating_deviation_after_no_results 200.2714 |> Player.scale_rating_deviation_to(:v2) + @valid_player_rating_deviation_after_no_results 200.2714 + |> Player.scale_rating_deviation_to(:v2) - describe "new rating" do - test "with results" do - player = Glicko.new_rating(@player, @results, [system_constant: 0.5]) - - assert_in_delta Player.rating(player), @valid_player_rating_after_results, 1.0e-4 - assert_in_delta Player.rating_deviation(player), @valid_player_rating_deviation_after_results, 1.0e-4 - assert_in_delta Player.volatility(player), @valid_player_volatility_after_results, 1.0e-5 - end - - test "no results" do - player = Glicko.new_rating(@player, []) - - assert_in_delta Player.rating_deviation(player), @valid_player_rating_deviation_after_no_results, 1.0e-4 - end - end + describe "new rating" do + test "with results" do + player = Glicko.new_rating(@player, @results, system_constant: 0.5) - describe "win probability" do - test "with same ratings" do - assert Glicko.win_probability(Player.new_v1, Player.new_v1) == 0.5 - end - - test "with better opponent" do - assert Glicko.win_probability(Player.new_v1([rating: 1500]), Player.new_v1([rating: 1600])) < 0.5 - end - - test "with better player" do - assert Glicko.win_probability(Player.new_v1([rating: 1600]), Player.new_v1([rating: 1500])) > 0.5 - end - end - - describe "draw probability" do - test "with same ratings" do - assert Glicko.draw_probability(Player.new_v1, Player.new_v1) == 1 - end - - test "with better opponent" do - assert Glicko.draw_probability(Player.new_v1([rating: 1500]), Player.new_v1([rating: 1600])) < 1 - end - - test "with better player" do - assert Glicko.draw_probability(Player.new_v1([rating: 1600]), Player.new_v1([rating: 1500])) < 1 - end - end + assert_in_delta Player.rating(player), @valid_player_rating_after_results, 1.0e-4 + + assert_in_delta Player.rating_deviation(player), + @valid_player_rating_deviation_after_results, + 1.0e-4 + + assert_in_delta Player.volatility(player), @valid_player_volatility_after_results, 1.0e-5 + end + + test "no results" do + player = Glicko.new_rating(@player, []) + + assert_in_delta Player.rating_deviation(player), + @valid_player_rating_deviation_after_no_results, + 1.0e-4 + end + end + + describe "win probability" do + test "with same ratings" do + assert Glicko.win_probability(Player.new_v1(), Player.new_v1()) == 0.5 + end + + test "with better opponent" do + assert Glicko.win_probability(Player.new_v1(rating: 1500), Player.new_v1(rating: 1600)) < + 0.5 + end + + test "with better player" do + assert Glicko.win_probability(Player.new_v1(rating: 1600), Player.new_v1(rating: 1500)) > + 0.5 + end + end + + describe "draw probability" do + test "with same ratings" do + assert Glicko.draw_probability(Player.new_v1(), Player.new_v1()) == 1 + end + + test "with better opponent" do + assert Glicko.draw_probability(Player.new_v1(rating: 1500), Player.new_v1(rating: 1600)) < 1 + end + + test "with better player" do + assert Glicko.draw_probability(Player.new_v1(rating: 1600), Player.new_v1(rating: 1500)) < 1 + end + end end diff --git a/test/player_test.exs b/test/player_test.exs index 1665d77..a29cb25 100644 --- a/test/player_test.exs +++ b/test/player_test.exs @@ -1,60 +1,62 @@ defmodule Glicko.PlayerTest do - use ExUnit.Case + use ExUnit.Case - alias Glicko.Player + alias Glicko.Player - doctest Player + doctest Player - @valid_v1_base {1.0, 2.0} - @valid_v2_base {1.0, 2.0, 3.0} + @valid_v1_base {1.0, 2.0} + @valid_v2_base {1.0, 2.0, 3.0} - test "create v1" do - assert @valid_v1_base == Player.new_v1([rating: 1.0, rating_deviation: 2.0]) - end + test "create v1" do + assert @valid_v1_base == Player.new_v1(rating: 1.0, rating_deviation: 2.0) + end - test "create v2" do - assert @valid_v2_base == Player.new_v2([rating: 1.0, rating_deviation: 2.0, volatility: 3.0]) - end + test "create v2" do + assert @valid_v2_base == Player.new_v2(rating: 1.0, rating_deviation: 2.0, volatility: 3.0) + end - test "convert player v1 -> v2" do - assert {Player.scale_rating_to(1.0, :v2), Player.scale_rating_deviation_to(2.0, :v2), 3.0} == Player.to_v2(@valid_v1_base, 3.0) - end + test "convert player v1 -> v2" do + assert {Player.scale_rating_to(1.0, :v2), Player.scale_rating_deviation_to(2.0, :v2), 3.0} == + Player.to_v2(@valid_v1_base, 3.0) + end - test "convert player v2 -> v1" do - assert {Player.scale_rating_to(1.0, :v1), Player.scale_rating_deviation_to(2.0, :v1)} == Player.to_v1(@valid_v2_base) - end + test "convert player v2 -> v1" do + assert {Player.scale_rating_to(1.0, :v1), Player.scale_rating_deviation_to(2.0, :v1)} == + Player.to_v1(@valid_v2_base) + end - test "convert player v1 -> v1" do - assert @valid_v1_base == Player.to_v1(@valid_v1_base) - end + test "convert player v1 -> v1" do + assert @valid_v1_base == Player.to_v1(@valid_v1_base) + end - test "convert player v2 -> v2" do - assert @valid_v2_base == Player.to_v2(@valid_v2_base) - end + test "convert player v2 -> v2" do + assert @valid_v2_base == Player.to_v2(@valid_v2_base) + end - test "scale rating v1 -> v2" do - assert_in_delta Player.scale_rating_to(1673.7178, :v2), 1.0, 0.1 - end + test "scale rating v1 -> v2" do + assert_in_delta Player.scale_rating_to(1673.7178, :v2), 1.0, 0.1 + end - test "scale rating v2 -> v1" do - assert_in_delta Player.scale_rating_to(1.0, :v1), 1673.7178, 0.1 - end + test "scale rating v2 -> v1" do + assert_in_delta Player.scale_rating_to(1.0, :v1), 1673.7178, 0.1 + end - test "scale rating deviation v1 -> v2" do - assert_in_delta Player.scale_rating_deviation_to(173.7178, :v2), 1.0, 0.1 - end + test "scale rating deviation v1 -> v2" do + assert_in_delta Player.scale_rating_deviation_to(173.7178, :v2), 1.0, 0.1 + end - test "scale rating deviation v2 -> v1" do - assert_in_delta Player.scale_rating_deviation_to(1.0, :v1), 173.7178, 0.1 - end + test "scale rating deviation v2 -> v1" do + assert_in_delta Player.scale_rating_deviation_to(1.0, :v1), 173.7178, 0.1 + end - test "rating interval" do - assert {rating_low, rating_high} = - [rating: 1850, rating_deviation: 50] - |> Player.new_v2 - |> Player.rating_interval + test "rating interval" do + assert {rating_low, rating_high} = + [rating: 1850, rating_deviation: 50] + |> Player.new_v2() + |> Player.rating_interval() - assert_in_delta rating_low, 1750, 0.1 - assert_in_delta rating_high, 1950, 0.1 - end + assert_in_delta rating_low, 1750, 0.1 + assert_in_delta rating_high, 1950, 0.1 + end end diff --git a/test/result_test.exs b/test/result_test.exs index 3890321..9bfc166 100644 --- a/test/result_test.exs +++ b/test/result_test.exs @@ -1,22 +1,22 @@ defmodule Glicko.ResultTest do - use ExUnit.Case + use ExUnit.Case - alias Glicko.{ - Player, - Result, - } + alias Glicko.{ + Player, + Result + } - doctest Result + doctest Result - @opponent Player.new_v2 + @opponent Player.new_v2() - @valid_game_result Result.new(@opponent, 0.0) + @valid_game_result Result.new(@opponent, 0.0) - test "create game result" do - assert @valid_game_result == Result.new(@opponent, 0.0) - end + test "create game result" do + assert @valid_game_result == Result.new(@opponent, 0.0) + end - test "create game result with shortcut" do - assert @valid_game_result == Result.new(@opponent, :loss) - end + test "create game result with shortcut" do + assert @valid_game_result == Result.new(@opponent, :loss) + end end