Skip to content

Commit

Permalink
Fix create balance when incomplete data
Browse files Browse the repository at this point in the history
  • Loading branch information
jauggy committed Jun 8, 2024
1 parent 0f6849e commit e261714
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 22 deletions.
39 changes: 36 additions & 3 deletions lib/teiserver/battle/libs/balance_lib.ex
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ defmodule Teiserver.Battle.BalanceLib do

def create_balance(groups, team_count, opts) do
start_time = System.system_time(:microsecond)
groups = standardise_groups(groups)

# We perform all our group calculations here and assign each group
# an ID that's used purely for this run of balance
Expand Down Expand Up @@ -154,6 +155,30 @@ defmodule Teiserver.Battle.BalanceLib do
|> Map.put(:time_taken, System.system_time(:microsecond) - start_time)
end

@doc """
Sometimes groups have missing data so we need to refetch it.
If we go through balancer_server then all the required data should be there
"""
def standardise_groups(groups) do
groups
|> Enum.map(fn group ->
# Iterate over our map
{_ignored, better_map} =
Enum.map_reduce(group, %{}, fn {user_id, value}, acc ->
fixed_value =
cond do
# We're missing data so need to fetch it
is_number(value) -> get_user_rating_rank_old(user_id, value)
true -> value
end

{nil, Map.put(acc, user_id, fixed_value)}
end)

better_map
end)
end

# Removes various keys we don't care about
defp cleanup_result(result) do
Map.take(
Expand All @@ -164,7 +189,8 @@ defmodule Teiserver.Battle.BalanceLib do

# Only take keys we need
defp clean_groups(groups) do
groups |> Enum.map(fn x->
groups
|> Enum.map(fn x ->
Map.take(x, ~w(members count group_rating ratings)a)
end)
end
Expand All @@ -179,7 +205,7 @@ defmodule Teiserver.Battle.BalanceLib do
true ->
balance_result.teams
|> Map.new(fn {team_id, groups} ->
{team_id, Enum.reverse(clean_groups((groups)))}
{team_id, Enum.reverse(clean_groups(groups))}
end)
end

Expand Down Expand Up @@ -541,7 +567,6 @@ defmodule Teiserver.Battle.BalanceLib do
get_user_rating_value(userid, rating_type_id)
end


# Used to get the rating value of the user for internal balance purposes which might be
# different from public/reporting
@spec get_user_balance_rating_value(T.userid(), String.t() | non_neg_integer()) ::
Expand Down Expand Up @@ -582,6 +607,14 @@ defmodule Teiserver.Battle.BalanceLib do
%{rating: rating, rank: rank, name: name}
end

@doc """
This is used by some screens to calculate a theoretical balance based on old ratings
"""
def get_user_rating_rank_old(userid, rating_value) do
%{rank: rank, name: name} = Account.get_user_by_id(userid)
%{rating: rating_value, rank: rank, name: name}
end

defp fuzz_rating(rating, multiplier) do
# Generate something between -1 and 1
modifier = 1 - :rand.uniform() * 2
Expand Down
2 changes: 2 additions & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ defmodule Teiserver.MixProject do
def application do
# get that with mix app.tree nostrum
nostrum_extras = [:certifi, :gun, :inets, :jason, :kcl, :mime]

[
mod: {Teiserver.Application, []},
included_applications: [:nostrum],
Expand Down Expand Up @@ -90,6 +91,7 @@ defmodule Teiserver.MixProject do
{:libcluster, "~> 3.3"},
{:tzdata, "~> 1.1"},
{:ex_ulid, "~> 0.1.0"},
{:mock, "~> 0.3.0", only: :test},

# Teiserver libs
{:openskill, git: "https://github.com/StanczakDominik/openskill.ex.git", branch: "master"},
Expand Down
2 changes: 2 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,12 @@
"libring": {:hex, :libring, "1.6.0", "d5dca4bcb1765f862ab59f175b403e356dec493f565670e0bacc4b35e109ce0d", [:mix], [], "hexpm", "5e91ece396af4bce99953d49ee0b02f698cd38326d93cd068361038167484319"},
"logger_file_backend": {:hex, :logger_file_backend, "0.0.13", "df07b14970e9ac1f57362985d76e6f24e3e1ab05c248055b7d223976881977c2", [:mix], [], "hexpm", "71a453a7e6e899ae4549fb147b1c6621f4233f8f48f58ca10a64ec67b6c50018"},
"math": {:hex, :math, "0.6.0", "69325af99e600123f6d994833502f6d063a2fa8f2786a3c0461fe6c6123a5166", [:mix], [], "hexpm", "2c73a64d64f719ee1f2821d382f3ed63e8e9564e5176d1c8aa777aac49b41bf7"},
"meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"},
"merkle_map": {:hex, :merkle_map, "0.2.1", "01a88c87a6b9fb594c67c17ebaf047ee55ffa34e74297aa583ed87148006c4c8", [:mix], [], "hexpm", "fed4d143a5c8166eee4fa2b49564f3c4eace9cb252f0a82c1613bba905b2d04d"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"mock": {:hex, :mock, "0.3.8", "7046a306b71db2488ef54395eeb74df0a7f335a7caca4a3d3875d1fc81c884dd", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "7fa82364c97617d79bb7d15571193fc0c4fe5afd0c932cef09426b3ee6fe2022"},
"nostrum": {:hex, :nostrum, "0.8.0", "36f5a08e99c3df3020523be9e1c650ad926a63becc5318562abfe782d586e078", [:mix], [{:certifi, "~> 2.8", [hex: :certifi, repo: "hexpm", optional: false]}, {:gun, "~> 2.0", [hex: :gun, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:kcl, "~> 1.4", [hex: :kcl, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm", "ce6861391ff346089d32a243fa71c0cb8bff79ab86ad53e8bf72808267899aee"},
"oban": {:hex, :oban, "2.15.2", "8f934a49db39163633965139c8846d8e24c2beb4180f34a005c2c7c3f69a6aa2", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0f4a579ea48fc7489e0d84facf8b01566e142bdc6542d7dabce32c10e664f1e9"},
"openskill": {:git, "https://github.com/StanczakDominik/openskill.ex.git", "163a72f7423b8aa964909ea6aa3e9943739d87a6", [branch: "master"]},
Expand Down
59 changes: 59 additions & 0 deletions test/teiserver/battle/balance_lib_internal_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
defmodule Teiserver.Battle.BalanceLibInternalTest do
@moduledoc """
Can run all balance tests via
mix test --only balance_test
"""
use ExUnit.Case
import Mock
@moduletag :balance_test
alias Teiserver.Battle.BalanceLib

setup_with_mocks([
{Teiserver.Account, [:passthrough],
[get_user_by_id: fn member_id -> get_user_by_id_mock(member_id) end]}
]) do
:ok
end

test "Able to standardise groups with incomplete data" do
groups = [
%{1 => 19, 2 => 20},
%{3 => 18},
%{4 => 15},
%{5 => 11}
]

fixed_groups = BalanceLib.standardise_groups(groups)

assert fixed_groups == [
%{
1 => %{name: "User_1", rank: 0, rating: 19},
2 => %{name: "User_2", rank: 0, rating: 20}
},
%{3 => %{name: "User_3", rank: 0, rating: 18}},
%{4 => %{name: "User_4", rank: 0, rating: 15}},
%{5 => %{name: "User_5", rank: 0, rating: 11}}
]

# loser_picks algo will hit the databases so let's just test with split_one_chevs
result = BalanceLib.create_balance(fixed_groups, 2, algorithm: "split_one_chevs")
assert result != nil
end

test "Handle groups with incomplete data in create_balance" do
groups = [
%{1 => 19, 2 => 20},
%{3 => 18},
%{4 => 15},
%{5 => 11}
]

# loser_picks algo will hit the databases so let's just test with split_one_chevs
result = BalanceLib.create_balance(groups, 2, algorithm: "split_one_chevs")
assert result != nil
end

defp get_user_by_id_mock(user_id) do
%{rank: 0, name: "User_#{user_id}"}
end
end
52 changes: 35 additions & 17 deletions test/teiserver/battle/split_one_chevs_internal_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,50 @@ defmodule Teiserver.Battle.SplitOneChevsInternalTest do
Can run tests in this file only by
mix test test/teiserver/battle/split_one_chevs_internal_test.exs
"""
use Teiserver.DataCase, async: true
use ExUnit.Case
@moduletag :balance_test
alias Teiserver.Battle.Balance.SplitOneChevs


test "perform" do
expanded_group = [
%{count: 2, members: ["Pro1", "Noob1"], group_rating: 13, ratings: [8, 5], ranks: [1,0], names: ["Pro1", "Noob1"],},
%{count: 1, members: ["Noob2"], group_rating: 6, ratings: [6], ranks: [0], names: ["Noob2"]},
%{count: 1, members: ["Noob3"], group_rating: 7, ratings: [17], ranks: [0], names: ["Noob3"]}
%{
count: 2,
members: ["Pro1", "Noob1"],
group_rating: 13,
ratings: [8, 5],
ranks: [1, 0],
names: ["Pro1", "Noob1"]
},
%{
count: 1,
members: ["Noob2"],
group_rating: 6,
ratings: [6],
ranks: [0],
names: ["Noob2"]
},
%{
count: 1,
members: ["Noob3"],
group_rating: 7,
ratings: [17],
ranks: [0],
names: ["Noob3"]
}
]

result = SplitOneChevs.perform(expanded_group, 2)

assert result.team_groups == %{
1 => [
%{count: 1, group_rating: 6, members: ["Noob2"], ratings: [6]},
%{count: 1, group_rating: 8, members: ["Pro1"], ratings: [8]}
],
2 => [
%{count: 1, group_rating: 5, members: ["Noob1"], ratings: [5]},
%{count: 1, group_rating: 17, members: ["Noob3"], ratings: [17]}
]
}
assert result.team_groups == %{
1 => [
%{count: 1, group_rating: 6, members: ["Noob2"], ratings: [6]},
%{count: 1, group_rating: 8, members: ["Pro1"], ratings: [8]}
],
2 => [
%{count: 1, group_rating: 5, members: ["Noob1"], ratings: [5]},
%{count: 1, group_rating: 17, members: ["Noob3"], ratings: [17]}
]
}
end

test "sort members" do
Expand Down Expand Up @@ -85,6 +105,4 @@ defmodule Teiserver.Battle.SplitOneChevsInternalTest do
%{members: [], team_id: 3}
]
end


end
3 changes: 1 addition & 2 deletions test/teiserver/battle/split_one_chevs_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule Teiserver.Battle.SplitOneChevsTest do
Can run tests in this file only by
mix test test/teiserver/battle/split_one_chevs_test.exs
"""
use Teiserver.DataCase, async: true
use ExUnit.Case
@moduletag :balance_test
alias Teiserver.Battle.BalanceLib

Expand Down Expand Up @@ -85,7 +85,6 @@ defmodule Teiserver.Battle.SplitOneChevsTest do
assert result.team_players == %{1 => [4, 1], 2 => [2, 3]}
end


test "logs FFA" do
result =
BalanceLib.create_balance(
Expand Down

0 comments on commit e261714

Please sign in to comment.