mirror of
https://github.com/avitex/elixir-glicko
synced 2024-11-21 18:59:57 +00:00
Support using tuples
This commit is contained in:
parent
168ec234dc
commit
b6dd4c2dee
141
lib/glicko.ex
141
lib/glicko.ex
@ -6,11 +6,14 @@ defmodule Glicko do
|
||||
|
||||
## Usage
|
||||
|
||||
Players can be represented by either the convenience `Glicko.Player` module or a tuple (see `player_t`).
|
||||
Results can be represented by either the convenience `Glicko.Result` module or a tuple (see `result_t`).
|
||||
|
||||
Get a players new rating after a series of matches in a rating period.
|
||||
|
||||
iex> results = [GameResult.new(Player.new_v1([rating: 1400, rating_deviation: 30]), :win),
|
||||
...> GameResult.new(Player.new_v1([rating: 1550, rating_deviation: 100]), :loss),
|
||||
...> GameResult.new(Player.new_v1([rating: 1700, rating_deviation: 300]), :loss)]
|
||||
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])
|
||||
%Glicko.Player{version: :v1, rating: 1464.0506705393013, rating_deviation: 151.51652412385727, volatility: nil}
|
||||
@ -25,12 +28,24 @@ defmodule Glicko do
|
||||
|
||||
alias __MODULE__.{
|
||||
Player,
|
||||
GameResult,
|
||||
Result,
|
||||
}
|
||||
|
||||
@default_system_constant 0.8
|
||||
@default_convergence_tolerance 1.0e-7
|
||||
|
||||
@type version_t :: :v1 | :v2
|
||||
@type rating_t :: float
|
||||
@type rating_deviation_t :: float
|
||||
@type volatility_t :: float
|
||||
@type score_t :: float
|
||||
|
||||
@type player_t :: player_v1_t | player_v2_t
|
||||
@type player_v1_t :: {rating :: rating_t, rating_deviation :: rating_deviation_t}
|
||||
@type player_v2_t :: {rating :: rating_t, rating_deviation :: rating_deviation_t, volatility :: volatility_t}
|
||||
|
||||
@type result_t :: {opponent :: player_t, score :: score_t}
|
||||
|
||||
@type new_rating_opts_t :: [system_constant: float, convergence_tolerance: float]
|
||||
|
||||
@doc """
|
||||
@ -38,50 +53,34 @@ defmodule Glicko do
|
||||
|
||||
Returns the updated player with the same version given to the function.
|
||||
"""
|
||||
@spec new_rating(player :: Player.t, results :: list(GameResult.t), opts :: new_rating_opts_t) :: Player.t
|
||||
def new_rating(player, results, opts \\ [])
|
||||
def new_rating(player = %Player{version: :v1}, results, opts) do
|
||||
player
|
||||
|> Player.to_v2
|
||||
|> do_new_rating(results, opts)
|
||||
|> Player.to_v1
|
||||
end
|
||||
def new_rating(player = %Player{version: :v2}, results, opts) do
|
||||
do_new_rating(player, results, opts)
|
||||
@spec new_rating(player :: player_t | Player.t, results :: list(result_t | Result.t), opts :: new_rating_opts_t) :: player_t | Player.t
|
||||
def new_rating(player, results, opts \\ []) do
|
||||
{cast_from, internal_player} = cast_player_to_internal(player)
|
||||
internal_player = do_new_rating(internal_player, results, opts)
|
||||
cast_internal_to_player({cast_from, internal_player})
|
||||
end
|
||||
|
||||
defp do_new_rating(player, [], _) do
|
||||
player_post_rating_deviation =
|
||||
Map.new
|
||||
|> Map.put(:player_rating_deviation_squared, :math.pow(player.rating_deviation, 2))
|
||||
|> calc_player_pre_rating_deviation(player.volatility)
|
||||
defp do_new_rating({player_rating, player_rating_deviation, player_volatility}, [], _) do
|
||||
player_post_rating_deviation = calc_player_pre_rating_deviation(
|
||||
:math.pow(player_rating_deviation, 2),
|
||||
player_volatility
|
||||
)
|
||||
|
||||
%{player | rating_deviation: player_post_rating_deviation}
|
||||
{player_rating, player_post_rating_deviation, player_volatility}
|
||||
end
|
||||
defp do_new_rating(player, results, opts) do
|
||||
results = Enum.map(results, fn result ->
|
||||
opponent = Player.to_v2(result.opponent)
|
||||
|
||||
result =
|
||||
Map.new
|
||||
|> Map.put(:score, result.score)
|
||||
|> Map.put(:opponent_rating, opponent.rating)
|
||||
|> Map.put(:opponent_rating_deviation, opponent.rating_deviation)
|
||||
|> Map.put(:opponent_rating_deviation_g, calc_g(opponent.rating_deviation))
|
||||
|
||||
Map.put(result, :e, calc_e(player.rating, result))
|
||||
end)
|
||||
|
||||
defp do_new_rating({player_rating, player_rating_deviation, player_volatility}, results, opts) do
|
||||
ctx =
|
||||
Map.new
|
||||
|> Map.put(:system_constant, Keyword.get(opts, :system_constant, @default_system_constant))
|
||||
|> Map.put(:convergence_tolerance, Keyword.get(opts, :convergence_tolerance, @default_convergence_tolerance))
|
||||
|> Map.put(:results, results)
|
||||
|> Map.put(:player_rating, player.rating)
|
||||
|> Map.put(:player_volatility, player.volatility)
|
||||
|> Map.put(:player_rating_deviation, player.rating_deviation)
|
||||
|> Map.put(:player_rating_deviation_squared, :math.pow(player.rating_deviation, 2))
|
||||
|> Map.put(:player_rating, player_rating)
|
||||
|> Map.put(:player_volatility, player_volatility)
|
||||
|> Map.put(:player_rating_deviation, player_rating_deviation)
|
||||
|> Map.put(:player_rating_deviation_squared, :math.pow(player_rating_deviation, 2))
|
||||
|
||||
# Init
|
||||
ctx = Map.put(ctx, :results, Enum.map(results, &build_internal_result(ctx, &1)))
|
||||
ctx = Map.put(ctx, :results_effect, calc_results_effect(ctx))
|
||||
# Step 3
|
||||
ctx = Map.put(ctx, :variance_estimate, calc_variance_estimate(ctx))
|
||||
# Step 4
|
||||
@ -102,21 +101,35 @@ defmodule Glicko do
|
||||
# Step 5.5
|
||||
ctx = Map.put(ctx, :new_player_volatility, calc_new_player_volatility(ctx))
|
||||
# Step 6
|
||||
ctx = Map.put(ctx, :player_pre_rating_deviation, calc_player_pre_rating_deviation(ctx, ctx.new_player_volatility))
|
||||
ctx = Map.put(ctx, :player_pre_rating_deviation, calc_player_pre_rating_deviation(
|
||||
ctx.player_rating_deviation_squared, ctx.new_player_volatility
|
||||
))
|
||||
# Step 7
|
||||
ctx = Map.put(ctx, :new_player_rating_deviation, calc_new_player_rating_deviation(ctx))
|
||||
ctx = Map.put(ctx, :new_player_rating, calc_new_player_rating(ctx))
|
||||
|
||||
Player.new_v2([
|
||||
rating: ctx.new_player_rating,
|
||||
rating_deviation: ctx.new_player_rating_deviation,
|
||||
volatility: ctx.new_player_volatility,
|
||||
])
|
||||
{ctx.new_player_rating, ctx.new_player_rating_deviation, ctx.new_player_volatility}
|
||||
end
|
||||
|
||||
defp build_internal_result(ctx, {opponent, score}) do
|
||||
{_, {opponent_rating, opponent_rating_deviation, _}} = cast_player_to_internal(opponent)
|
||||
|
||||
result =
|
||||
Map.new
|
||||
|> Map.put(:score, score)
|
||||
|> Map.put(:opponent_rating, opponent_rating)
|
||||
|> Map.put(:opponent_rating_deviation, opponent_rating_deviation)
|
||||
|> Map.put(:opponent_rating_deviation_g, calc_g(opponent_rating_deviation))
|
||||
|
||||
Map.put(result, :e, calc_e(ctx.player_rating, result))
|
||||
end
|
||||
defp build_internal_result(ctx, %Result{score: score, opponent: opponent}) do
|
||||
build_internal_result(ctx, {opponent, score})
|
||||
end
|
||||
|
||||
# Calculation of the estimated variance of the player's rating based on game outcomes
|
||||
defp calc_variance_estimate(%{results: results}) do
|
||||
results
|
||||
defp calc_variance_estimate(ctx) do
|
||||
ctx.results
|
||||
|> Enum.reduce(0.0, fn result, acc ->
|
||||
acc + :math.pow(result.opponent_rating_deviation_g, 2) * result.e * (1 - result.e)
|
||||
end)
|
||||
@ -124,7 +137,7 @@ defmodule Glicko do
|
||||
end
|
||||
|
||||
defp calc_delta(ctx) do
|
||||
calc_results_effect(ctx) * ctx.variance_estimate
|
||||
ctx.results_effect * ctx.variance_estimate
|
||||
end
|
||||
|
||||
defp calc_f(ctx, x) do
|
||||
@ -142,22 +155,22 @@ defmodule Glicko do
|
||||
:math.exp(a / 2)
|
||||
end
|
||||
|
||||
defp calc_results_effect(%{results: results}) do
|
||||
Enum.reduce(results, 0.0, fn result, acc ->
|
||||
defp calc_results_effect(ctx) do
|
||||
Enum.reduce(ctx.results, 0.0, fn result, acc ->
|
||||
acc + result.opponent_rating_deviation_g * (result.score - result.e)
|
||||
end)
|
||||
end
|
||||
|
||||
defp calc_new_player_rating(ctx) do
|
||||
ctx.player_rating + :math.pow(ctx.new_player_rating_deviation, 2) * calc_results_effect(ctx)
|
||||
ctx.player_rating + :math.pow(ctx.new_player_rating_deviation, 2) * ctx.results_effect
|
||||
end
|
||||
|
||||
defp calc_new_player_rating_deviation(ctx) do
|
||||
1 / :math.sqrt(1 / :math.pow(ctx.player_pre_rating_deviation, 2) + 1 / ctx.variance_estimate)
|
||||
end
|
||||
|
||||
defp calc_player_pre_rating_deviation(ctx, player_volatility) do
|
||||
:math.sqrt((:math.pow(player_volatility, 2) + ctx.player_rating_deviation_squared))
|
||||
defp calc_player_pre_rating_deviation(player_rating_deviation_squared, player_volatility) do
|
||||
:math.sqrt((:math.pow(player_volatility, 2) + player_rating_deviation_squared))
|
||||
end
|
||||
|
||||
defp iterative_algorithm_initial(ctx) do
|
||||
@ -205,4 +218,26 @@ defmodule Glicko do
|
||||
defp calc_e(player_rating, result) do
|
||||
1 / (1 + :math.exp(-1 * result.opponent_rating_deviation_g * (player_rating - result.opponent_rating)))
|
||||
end
|
||||
|
||||
defp cast_player_to_internal(player) when is_tuple(player) and tuple_size(player) == 2 do
|
||||
{:v1, Player.new_v1(player) |> Player.to_v2 |> cast_player_to_internal |> elem(1)}
|
||||
end
|
||||
defp cast_player_to_internal(player) when is_tuple(player) and tuple_size(player) == 3 do
|
||||
{:v2, player}
|
||||
end
|
||||
defp cast_player_to_internal(player = %Player{version: :v1}) do
|
||||
{:player_v1, player |> Player.to_v2 |> cast_player_to_internal |> elem(1)}
|
||||
end
|
||||
defp cast_player_to_internal(player = %Player{version: :v2}) do
|
||||
{:player_v2, {player.rating, player.rating_deviation, player.volatility}}
|
||||
end
|
||||
|
||||
defp cast_internal_to_player({:v1, {rating, rating_deviation, _}}), do: {
|
||||
rating |> Player.scale_rating_to(:v1),
|
||||
rating_deviation |> Player.scale_rating_deviation_to(:v1),
|
||||
}
|
||||
defp cast_internal_to_player({:v2, player}), do: player
|
||||
defp cast_internal_to_player({:player_v1, player}), do: Player.new_v2(player) |> Player.to_v1
|
||||
defp cast_internal_to_player({:player_v2, player}), do: Player.new_v2(player)
|
||||
|
||||
end
|
||||
|
@ -1,41 +0,0 @@
|
||||
defmodule Glicko.GameResult do
|
||||
@moduledoc """
|
||||
Provides a representation of a game result against an opponent.
|
||||
|
||||
## Usage
|
||||
|
||||
iex> opponent = Player.new_v2
|
||||
iex> GameResult.new(opponent, 0.0)
|
||||
%GameResult{score: 0.0, opponent: %Player{version: :v2, rating: 0.0, rating_deviation: 2.014761872416068, volatility: 0.06}}
|
||||
iex> GameResult.new(opponent, :win) # With shortcut
|
||||
%GameResult{score: 1.0, opponent: %Player{version: :v2, rating: 0.0, rating_deviation: 2.014761872416068, volatility: 0.06}}
|
||||
|
||||
"""
|
||||
|
||||
alias Glicko.Player
|
||||
|
||||
defstruct [
|
||||
:score,
|
||||
:opponent,
|
||||
]
|
||||
|
||||
@type t :: %__MODULE__{score: float, opponent: Player.t}
|
||||
|
||||
@type result_type_t :: :loss | :draw | :win
|
||||
|
||||
@result_type_map %{loss: 0.0, draw: 0.5, win: 1.0}
|
||||
|
||||
@doc """
|
||||
Creates a new GameResult against an opponent.
|
||||
|
||||
Supports passing either `:loss`, `:draw`, or `:win` as shortcuts.
|
||||
"""
|
||||
@spec new(opponent :: Player.t, result_type_t | float) :: t
|
||||
def new(opponent, result_type) when is_atom(result_type) and result_type in [:loss, :draw, :win] do
|
||||
new(opponent, Map.fetch!(@result_type_map, result_type))
|
||||
end
|
||||
def new(opponent, score) when is_number(score), do: %__MODULE__{
|
||||
score: score,
|
||||
opponent: opponent,
|
||||
}
|
||||
end
|
@ -36,15 +36,21 @@ defmodule Glicko.Player do
|
||||
@magic_version_scale 173.7178
|
||||
@magic_version_scale_rating 1500.0
|
||||
|
||||
@default_v1_rating 1500.0
|
||||
@default_v1_rating_deviation 350.0
|
||||
|
||||
@default_v2_volatility 0.06
|
||||
|
||||
@type t :: v1_t | v2_t
|
||||
|
||||
@type v1_t :: %__MODULE__{version: :v1, rating: float, rating_deviation: float, volatility: nil}
|
||||
@type v2_t :: %__MODULE__{version: :v2, rating: float, rating_deviation: float, volatility: float}
|
||||
@type v1_t :: %__MODULE__{
|
||||
version: :v1,
|
||||
rating: Glicko.rating_t,
|
||||
rating_deviation: Glicko.rating_deviation_t,
|
||||
volatility: nil,
|
||||
}
|
||||
|
||||
@type v2_t :: %__MODULE__{
|
||||
version: :v2,
|
||||
rating: Glicko.rating_t,
|
||||
rating_deviation: Glicko.rating_deviation_t,
|
||||
volatility: Glicko.volatility_t,
|
||||
}
|
||||
|
||||
defstruct [
|
||||
:version,
|
||||
@ -53,31 +59,68 @@ defmodule Glicko.Player do
|
||||
:volatility,
|
||||
]
|
||||
|
||||
@doc """
|
||||
The recommended initial rating value for a new player.
|
||||
"""
|
||||
@spec initial_rating(Glicko.version_t) :: Glicko.rating_t
|
||||
def initial_rating(:v1), do: 1500.0
|
||||
def initial_rating(:v2), do: initial_rating(:v1) |> scale_rating_to(:v2)
|
||||
|
||||
@doc """
|
||||
The recommended initial rating deviation value for a new player.
|
||||
"""
|
||||
@spec initial_rating_deviation(Glicko.version_t) :: Glicko.rating_deviation_t
|
||||
def initial_rating_deviation(:v1), do: 350.0
|
||||
def initial_rating_deviation(:v2), do: initial_rating_deviation(:v1) |> scale_rating_deviation_to(:v2)
|
||||
|
||||
@doc """
|
||||
The recommended initial volatility value for a new player.
|
||||
"""
|
||||
@spec initial_v2_volatility :: Glicko.volatility_t
|
||||
def initial_v2_volatility, do: 0.06
|
||||
|
||||
@doc """
|
||||
Creates a new v1 player.
|
||||
|
||||
If not overriden, will use default values for an unrated player.
|
||||
If not overriden, will use the default values for an unrated player.
|
||||
"""
|
||||
@spec new_v1([rating: float, rating_deviation: float]) :: v1_t
|
||||
def new_v1(opts \\ []), do: %__MODULE__{
|
||||
@spec new_v1(
|
||||
{Glicko.rating_t, Glicko.rating_deviation_t} |
|
||||
[rating: Glicko.rating_t, rating_deviation: Glicko.rating_deviation_t]
|
||||
) :: v1_t
|
||||
def new_v1(opts \\ [])
|
||||
def new_v1({rating, rating_deviation}), do: %__MODULE__{
|
||||
version: :v1,
|
||||
rating: Keyword.get(opts, :rating, @default_v1_rating),
|
||||
rating_deviation: Keyword.get(opts, :rating_deviation, @default_v1_rating_deviation),
|
||||
rating: rating,
|
||||
rating_deviation: rating_deviation,
|
||||
volatility: nil,
|
||||
}
|
||||
def new_v1(opts), do: new_v1({
|
||||
Keyword.get(opts, :rating, initial_rating(:v1)),
|
||||
Keyword.get(opts, :rating_deviation, initial_rating_deviation(:v1)),
|
||||
})
|
||||
|
||||
@doc """
|
||||
Creates a new v2 player.
|
||||
|
||||
If not overriden, will use default values for an unrated player.
|
||||
"""
|
||||
@spec new_v2([rating: float, rating_deviation: float, volatility: float]) :: v2_t
|
||||
def new_v2(opts \\ []), do: %__MODULE__{
|
||||
@spec new_v2(
|
||||
{Glicko.rating_t, Glicko.rating_deviation_t, Glicko.volatility_t} |
|
||||
[rating: Glicko.rating_t, rating_deviation: Glicko.rating_deviation_t, volatility: Glicko.volatility_t]
|
||||
) :: v2_t
|
||||
def new_v2(opts \\ [])
|
||||
def new_v2({rating, rating_deviation, volatility}), do: %__MODULE__{
|
||||
version: :v2,
|
||||
rating: Keyword.get(opts, :rating, @default_v1_rating |> scale_rating_to(:v2)),
|
||||
rating_deviation: Keyword.get(opts, :rating_deviation, @default_v1_rating_deviation |> scale_rating_deviation_to(:v2)),
|
||||
volatility: Keyword.get(opts, :volatility, @default_v2_volatility),
|
||||
rating: rating,
|
||||
rating_deviation: rating_deviation,
|
||||
volatility: volatility,
|
||||
}
|
||||
def new_v2(opts), do: new_v2({
|
||||
Keyword.get(opts, :rating, initial_rating(:v2)),
|
||||
Keyword.get(opts, :rating_deviation, initial_rating_deviation(:v2)),
|
||||
Keyword.get(opts, :volatility, initial_v2_volatility()),
|
||||
})
|
||||
|
||||
@doc """
|
||||
Converts a v2 player to a v1.
|
||||
@ -99,7 +142,7 @@ defmodule Glicko.Player do
|
||||
A v2 player will pass-through unchanged with the volatility arg ignored.
|
||||
"""
|
||||
@spec to_v2(player :: t, volatility :: float) :: v2_t
|
||||
def to_v2(player, volatility \\ @default_v2_volatility)
|
||||
def to_v2(player, volatility \\ initial_v2_volatility())
|
||||
def to_v2(player = %__MODULE__{version: :v2}, _volatility), do: player
|
||||
def to_v2(player = %__MODULE__{version: :v1}, volatility), do: new_v2([
|
||||
rating: player.rating |> scale_rating_to(:v2),
|
||||
@ -131,14 +174,15 @@ defmodule Glicko.Player do
|
||||
@doc """
|
||||
Scales a players rating.
|
||||
"""
|
||||
@spec scale_rating_to(rating :: float, to_version :: :v1 | :v2) :: float
|
||||
@spec scale_rating_to(rating :: Glicko.rating_t, to_version :: Glicko.version_t) :: Glicko.rating_t
|
||||
def scale_rating_to(rating, :v1), do: (rating * @magic_version_scale) + @magic_version_scale_rating
|
||||
def scale_rating_to(rating, :v2), do: (rating - @magic_version_scale_rating) / @magic_version_scale
|
||||
|
||||
@doc """
|
||||
Scales a players rating deviation.
|
||||
"""
|
||||
@spec scale_rating_deviation_to(rating_deviation :: float, to_version :: :v1 | :v2) :: float
|
||||
@spec scale_rating_deviation_to(rating_deviation :: Glicko.rating_deviation_t, to_version :: Glicko.version_t) :: Glicko.rating_deviation_t
|
||||
def scale_rating_deviation_to(rating_deviation, :v1), do: rating_deviation * @magic_version_scale
|
||||
def scale_rating_deviation_to(rating_deviation, :v2), do: rating_deviation / @magic_version_scale
|
||||
|
||||
end
|
||||
|
41
lib/glicko/result.ex
Normal file
41
lib/glicko/result.ex
Normal file
@ -0,0 +1,41 @@
|
||||
defmodule Glicko.Result do
|
||||
@moduledoc """
|
||||
A convenience wrapper representing a result against an opponent.
|
||||
|
||||
## Usage
|
||||
|
||||
iex> opponent = Player.new_v2
|
||||
iex> Result.new(opponent, 0.0)
|
||||
%Result{score: 0.0, opponent: %Player{version: :v2, rating: 0.0, rating_deviation: 2.014761872416068, volatility: 0.06}}
|
||||
iex> Result.new(opponent, :win) # With shortcut
|
||||
%Result{score: 1.0, opponent: %Player{version: :v2, rating: 0.0, rating_deviation: 2.014761872416068, volatility: 0.06}}
|
||||
|
||||
"""
|
||||
|
||||
alias Glicko.Player
|
||||
|
||||
defstruct [
|
||||
:score,
|
||||
:opponent,
|
||||
]
|
||||
|
||||
@type t :: %__MODULE__{score: Glicko.score_t, opponent: Glicko.player_t | Player.t}
|
||||
|
||||
@type result_type_t :: :loss | :draw | :win
|
||||
|
||||
@result_type_map %{loss: 0.0, draw: 0.5, win: 1.0}
|
||||
|
||||
@doc """
|
||||
Creates a new Result against an opponent.
|
||||
|
||||
Supports passing either `:loss`, `:draw`, or `:win` as shortcuts.
|
||||
"""
|
||||
@spec new(opponent :: Glicko.player_t | Player.t, result_type_t | float) :: t
|
||||
def new(opponent, result_type) when is_atom(result_type) and result_type in [:loss, :draw, :win] do
|
||||
new(opponent, Map.fetch!(@result_type_map, result_type))
|
||||
end
|
||||
def new(opponent, score) when is_number(score), do: %__MODULE__{
|
||||
score: score,
|
||||
opponent: opponent,
|
||||
}
|
||||
end
|
@ -1,22 +0,0 @@
|
||||
defmodule Glicko.GameResultTest do
|
||||
use ExUnit.Case
|
||||
|
||||
alias Glicko.{
|
||||
Player,
|
||||
GameResult,
|
||||
}
|
||||
|
||||
doctest GameResult
|
||||
|
||||
@opponent Player.new_v2
|
||||
|
||||
@valid_game_result %GameResult{opponent: @opponent, score: 0.0}
|
||||
|
||||
test "create game result" do
|
||||
assert @valid_game_result == GameResult.new(@opponent, 0.0)
|
||||
end
|
||||
|
||||
test "create game result with shortcut" do
|
||||
assert @valid_game_result == GameResult.new(@opponent, :loss)
|
||||
end
|
||||
end
|
@ -3,7 +3,7 @@ defmodule GlickoTest do
|
||||
|
||||
alias Glicko.{
|
||||
Player,
|
||||
GameResult,
|
||||
Result,
|
||||
}
|
||||
|
||||
doctest Glicko
|
||||
@ -11,9 +11,9 @@ defmodule GlickoTest do
|
||||
@player Player.new_v1([rating: 1500, rating_deviation: 200]) |> Player.to_v2
|
||||
|
||||
@results [
|
||||
GameResult.new(Player.new_v1([rating: 1400, rating_deviation: 30]), :win),
|
||||
GameResult.new(Player.new_v1([rating: 1550, rating_deviation: 100]), :loss),
|
||||
GameResult.new(Player.new_v1([rating: 1700, rating_deviation: 300]), :loss),
|
||||
{{1400, 30}, 1.0},
|
||||
Result.new({1550, 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)
|
||||
|
22
test/result_test.exs
Normal file
22
test/result_test.exs
Normal file
@ -0,0 +1,22 @@
|
||||
defmodule Glicko.ResultTest do
|
||||
use ExUnit.Case
|
||||
|
||||
alias Glicko.{
|
||||
Player,
|
||||
Result,
|
||||
}
|
||||
|
||||
doctest Result
|
||||
|
||||
@opponent Player.new_v2
|
||||
|
||||
@valid_game_result %Result{opponent: @opponent, score: 0.0}
|
||||
|
||||
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
|
||||
end
|
Loading…
Reference in New Issue
Block a user