diff --git a/lib/glicko.ex b/lib/glicko.ex index b2435fc..dadff68 100644 --- a/lib/glicko.ex +++ b/lib/glicko.ex @@ -28,6 +28,13 @@ defmodule Glicko do iex> Glicko.win_probability(player, opponent) 0.5 + 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 + """ alias __MODULE__.{ @@ -41,7 +48,7 @@ defmodule Glicko do @type new_rating_opts :: [system_constant: float, convergence_tolerance: float] @doc """ - Calculates the probability of a player winning against an opponent from a player and an opponent. + Calculates the probability of a player winning against an opponent. Returns a value between `0.0` and `1.0`. """ @@ -62,6 +69,28 @@ defmodule Glicko do calc_e(player_rating, opponent_rating, calc_g(opponent_rating_deviation)) end + @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 + + @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. + + 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. diff --git a/test/glicko_test.exs b/test/glicko_test.exs index 2785f47..eae5461 100644 --- a/test/glicko_test.exs +++ b/test/glicko_test.exs @@ -22,29 +22,47 @@ defmodule GlickoTest do @valid_player_rating_deviation_after_no_results 200.2714 |> Player.scale_rating_deviation_to(:v2) - test "new rating (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 + 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 - test "new rating (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 + 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 - - test "win probability with same ratings" do - assert Glicko.win_probability(Player.new_v1, Player.new_v1) == 0.5 - end - - test "win probability with better opponent" do - assert Glicko.win_probability(Player.new_v1([rating: 1500]), Player.new_v1([rating: 1600])) < 0.5 - end - - test "win probability with better player" do - assert Glicko.win_probability(Player.new_v1([rating: 1600]), Player.new_v1([rating: 1500])) > 0.5 + + 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