Skip to content

Commit

Permalink
Add function to calculate seasonal uncertainty
Browse files Browse the repository at this point in the history
  • Loading branch information
jauggy committed Dec 9, 2024
1 parent d7df6d3 commit ef594b2
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
defmodule Teiserver.Repo.Migrations.SeasonUncertaintyFunction do
use Ecto.Migration

def up do
query = """
create or replace function calculate_season_uncertainty(current_uncertainty float, last_updated timestamp, min_uncertainty float default 5)
returns float
language plpgsql
as
$$
declare
-- variable declaration
default_uncertainty float;
days_not_played float;
one_month float;
one_year float;
interpolated_uncertainty float;
min_days float;
max_days float;
max_uncertainty float;
begin
-- Your new uncertainty will be: greatest(target_uncertainty, current_uncertainty)
-- Where target_uncertainty will be default if you have not played for over a year
-- 5 (min_uncertainty) if you have played within one month
-- And use linear interpolation for values in between
one_year = 365.0;
default_uncertainty = 25.0/3;
one_month = one_year / 12;
days_not_played = abs(DATE_PART('day', (now()- last_updated )));
min_days = one_month;
max_days = one_year;
max_uncertainty = default_uncertainty;
if(days_not_played >= max_days) then
return default_uncertainty;
elsif days_not_played <= min_days then
return GREATEST(current_uncertainty, min_uncertainty);
else
-- Use linear interpolation
interpolated_uncertainty = min_uncertainty +(days_not_played - min_days) * (max_uncertainty - min_uncertainty) /(max_days - min_days);
return GREATEST(current_uncertainty, interpolated_uncertainty);
end if;
end;
$$;
"""

execute(query)
end

def down do
query = "drop function calculate_season_uncertainty"
execute(query)
end
end
76 changes: 76 additions & 0 deletions test/teiserver/sql/season_uncertainty_reset_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
defmodule Teiserver.Sql.SeasonUncertaintyResetTest do
@moduledoc false
use Teiserver.DataCase

test "it can calculate seasonal uncertainty reset target" do
# Start by removing all anon properties
{:ok, now} = DateTime.now("Etc/UTC")

result = calculate_seasonal_uncertainty(now)
assert result == 5

# Now 30 days ago
month = 365 / 12
last_updated = DateTime.add(now, -month |> trunc(), :day)
result = calculate_seasonal_uncertainty(last_updated)

# assert_in_delta checks that the result is close to expected. Helps deal with rounding issues
assert_in_delta(result, 5, 0.1)

# Now 2 months ago
last_updated = DateTime.add(now, (-2 * month) |> trunc(), :day)
result = calculate_seasonal_uncertainty(last_updated)
assert_in_delta(result, 5.294728102947281, 0.1)

# Now 3 months ago
last_updated = DateTime.add(now, (-3 * month) |> trunc(), :day)
result = calculate_seasonal_uncertainty(last_updated)
assert_in_delta(result, 5.6035699460357, 0.1)

# Now 6 months ago
last_updated = DateTime.add(now, (-6 * month) |> trunc(), :day)
result = calculate_seasonal_uncertainty(last_updated)
assert_in_delta(result, 6.510170195101702, 0.1)

# Now 9 months ago
last_updated = DateTime.add(now, (-9 * month) |> trunc(), :day)
result = calculate_seasonal_uncertainty(last_updated)
assert_in_delta(result, 7.416770444167705, 0.1)

# Now 11 months ago
last_updated = DateTime.add(now, (-11 * month) |> trunc(), :day)
result = calculate_seasonal_uncertainty(last_updated)
assert_in_delta(result, 8.024491490244916, 0.1)

# Now 12 months ago
last_updated = DateTime.add(now, (-12 * month) |> trunc(), :day)
result = calculate_seasonal_uncertainty(last_updated)
assert_in_delta(result, 8.333333333333334, 0.1)

# Now 13 months ago
last_updated = DateTime.add(now, (-13 * month) |> trunc(), :day)

result = calculate_seasonal_uncertainty(last_updated)
assert_in_delta(result, 8.333333333333334, 0.1)
end

# This will calculate the new uncertainty during a season reset
# The longer your last_updated is from today, the more your uncertainty will be reset
# The number should range from min_uncertainty and default_uncertainty (8.333)
# Your new_uncertainty can grow from current_uncertainty but never reduce
# Full details in comments of the sql function calculate_season_uncertainty
defp calculate_seasonal_uncertainty(last_updated) do
current_uncertainty = 1
min_uncertainty = 5
query = "SELECT calculate_season_uncertainty($1, $2, $3);"

results =
Ecto.Adapters.SQL.query!(Repo, query, [current_uncertainty, last_updated, min_uncertainty])

[new_uncertainty] =
results.rows
|> Enum.at(0)

new_uncertainty
end
end

0 comments on commit ef594b2

Please sign in to comment.