Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve season uncertainty reset by considering players who have not played for a while #539

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 44 additions & 4 deletions lib/teiserver/battle/tasks/seasonal_uncertainty_reset_task.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ defmodule Teiserver.Battle.SeasonalUncertaintyResetTask do
start_time = System.system_time(:millisecond)

new_last_updated = Timex.now()
{_skill, new_uncertainty} = Openskill.rating()

ratings_count =
Account.list_ratings(limit: :infinity)
|> Enum.map(fn rating ->
reset_rating(rating, new_uncertainty, new_last_updated)
reset_rating(rating, new_last_updated)
1
end)
|> Enum.count()
Expand All @@ -25,9 +24,13 @@ defmodule Teiserver.Battle.SeasonalUncertaintyResetTask do
)
end

defp reset_rating(existing, _new_uncertainty, new_last_updated) do
defp reset_rating(existing, new_last_updated) do
# Use the greater of the existing uncertainty or the minimum value (5.0)
new_uncertainty = max(existing.uncertainty, 5.0)
current_uncertainty = existing.uncertainty
# datetime
last_updated = existing.last_updated

new_uncertainty = calculate_new_uncertainty(current_uncertainty, last_updated)

new_rating_value = BalanceLib.calculate_rating_value(existing.skill, new_uncertainty)

Expand Down Expand Up @@ -59,4 +62,41 @@ defmodule Teiserver.Battle.SeasonalUncertaintyResetTask do

{:ok, _} = Game.create_rating_log(log_params)
end

def calculate_new_uncertainty(current_uncertainty, last_update_datetime) do
days_not_played = abs(DateTime.diff(last_update_datetime, Timex.now(), :day))
target_uncertainty = calculate_target_uncertainty(days_not_played)
# The new uncertainty can increase but never decrease
max(target_uncertainty, current_uncertainty)
end

# This is the player's new target uncertainty
# If the player hasn't played for a while, their target uncertainty will be higher
def calculate_target_uncertainty(days_not_played) do
# If you haven't played for more than a year reset uncertainty to default
# If you have played within one month, then the target uncertainty equals min_uncertainty
# If it's something in between one month and a year, use linear interpolation
# Linear interpolation formula: https://www.cuemath.com/linear-interpolation-formula/
one_year = 365
one_month = one_year / 12
min_uncertainty = 5
{_skill, max_uncertainty} = Openskill.rating()

cond do
days_not_played >= one_year ->
max_uncertainty

days_not_played <= one_month ->
min_uncertainty

true ->
max_days = one_year
min_days = one_month

# linear interpolation will give a value between min_uncertainty and max_uncertainty
min_uncertainty +
(days_not_played - min_days) * (max_uncertainty - min_uncertainty) /
(max_days - min_days)
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
defmodule Teiserver.Battle.SeasonalUncertaintyResetTaskTest do
@moduledoc """
Can run all balance tests via
mix test --only balance_test
"""
use ExUnit.Case
@moduletag :balance_test
alias Teiserver.Battle.SeasonalUncertaintyResetTask

test "can calculate target uncertainty" do
result = SeasonalUncertaintyResetTask.calculate_target_uncertainty(400)
assert result == 8.333333333333334

one_year = 365

one_month = one_year / 12

# If you played yesterday then it should pick the min uncertainty of 5
result = SeasonalUncertaintyResetTask.calculate_target_uncertainty(1)
assert result == 5

result = SeasonalUncertaintyResetTask.calculate_target_uncertainty(one_month)
assert result == 5

result = SeasonalUncertaintyResetTask.calculate_target_uncertainty(one_month * 2)
assert result == 5.303030303030303

result = SeasonalUncertaintyResetTask.calculate_target_uncertainty(one_month * 3)
assert result == 5.6060606060606063

result = SeasonalUncertaintyResetTask.calculate_target_uncertainty(one_month * 6)
assert result == 6.515151515151516

result = SeasonalUncertaintyResetTask.calculate_target_uncertainty(one_month * 9)
assert result == 7.424242424242426

result = SeasonalUncertaintyResetTask.calculate_target_uncertainty(one_month * 11)
assert result == 8.030303030303031

result = SeasonalUncertaintyResetTask.calculate_target_uncertainty(one_year)
assert result == 8.333333333333334

{_, end_date} = DateTime.new(~D[2016-05-24], ~T[13:26:08.003], "Etc/UTC")
{_, start_date} = DateTime.new(~D[2016-04-24], ~T[13:26:08.003], "Etc/UTC")
days = abs(DateTime.diff(start_date, end_date, :day))
assert days == 30
end
end
Loading