Skip to content

Commit

Permalink
default balance algo determine whether to pick loser_picks or split_n…
Browse files Browse the repository at this point in the history
…oobs depending on circumstances (beyond-all-reason#429)

* New default algo call split_noobs when there are noobs

* Fix failing test

* Update comments

* Remove split_noobs tip

* Updated after feedback
  • Loading branch information
jauggy authored Aug 27, 2024
1 parent 951c267 commit 000f1ad
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 20 deletions.
63 changes: 63 additions & 0 deletions lib/teiserver/battle/balance/default_balance.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
defmodule Teiserver.Battle.Balance.DefaultBalance do
@moduledoc """
This will call other balancers depending on circumstances
"""
alias Teiserver.Battle.Balance.SplitNoobs
alias Teiserver.Battle.Balance.LoserPicks
alias Teiserver.Battle.Balance.BalanceTypes, as: BT
alias Teiserver.Battle.Balance.DefaultBalanceTypes, as: DB

@doc """
Main entry point used by balance_lib
"""
@spec perform([BT.expanded_group()], non_neg_integer(), list()) :: any()
def perform(expanded_group, team_count, opts \\ []) do
get_balance_algorithm(expanded_group, team_count).perform(expanded_group, team_count, opts)
end

@spec get_balance_algorithm([BT.expanded_group()], integer()) ::
any()
def get_balance_algorithm(expanded_group, team_count) do
cond do
team_count != 2 ->
LoserPicks

true ->
players = flatten_members(expanded_group)
has_noobs? = has_noobs?(players)

cond do
has_noobs? -> SplitNoobs
true -> LoserPicks
end
end
end

@doc """
Converts the input to a simple list of players
"""
@spec flatten_members([BT.expanded_group()]) :: [DB.player()]
def flatten_members(expanded_group) do
# We only care about ranks and uncertainties for now
# However, in the future we may use other data to decide what balance algorithm to use,
# e.g. whether there are parties or not, whether it's a high rating lobby, etc.
for %{
ranks: ranks,
uncertainties: uncertainties
} <- expanded_group,
# Zipping will create binary tuples from 2 lists
{rank, uncertainty} <-
Enum.zip([ranks, uncertainties]),
do: %{
uncertainty: uncertainty,
rank: rank
}
end

@spec has_noobs?([DB.player()]) :: any()
def has_noobs?(players) do
Enum.any?(players, fn x ->
SplitNoobs.is_newish_player?(x.rank, x.uncertainty)
end)
end
end
8 changes: 8 additions & 0 deletions lib/teiserver/battle/balance/default_balance_types.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
defmodule Teiserver.Battle.Balance.DefaultBalanceTypes do
@moduledoc false

@type player :: %{
rank: number(),
uncertainty: number()
}
end
5 changes: 4 additions & 1 deletion lib/teiserver/battle/balance/loser_picks.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ defmodule Teiserver.Battle.Balance.LoserPicks do
alias Teiserver.Battle.Balance.BalanceTypes, as: BT
import Teiserver.Helper.NumberHelper, only: [round: 2]

@splitter "------------------------------------------------------"
@type algorithm_state :: %{
teams: map,
logs: list,
Expand Down Expand Up @@ -81,9 +82,11 @@ defmodule Teiserver.Battle.Balance.LoserPicks do

max_teamsize = (total_members / Enum.count(teams)) |> :math.ceil() |> round()

intial_logs = [@splitter, "Algorithm: loser_picks", @splitter]

state = %{
teams: teams,
logs: group_logs,
logs: intial_logs ++ group_logs,
solo_players: solo_players,
group_pairs: group_pairs,
max_teamsize: max_teamsize,
Expand Down
6 changes: 1 addition & 5 deletions lib/teiserver/battle/balance/split_noobs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -319,11 +319,7 @@ defmodule Teiserver.Battle.Balance.SplitNoobs do
id: id,
uncertainty: uncertainty,
rank: rank,
in_party?:
cond do
count <= 1 -> false
true -> true
end
in_party?: count > 1
}
end

Expand Down
10 changes: 4 additions & 6 deletions lib/teiserver/battle/libs/balance_lib.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ defmodule Teiserver.Battle.BalanceLib do
# which one will get to pick first
@shuffle_first_pick true

@default_balance_algorithm "loser_picks"

@spec defaults() :: map()
def defaults() do
%{
Expand All @@ -43,8 +41,7 @@ defmodule Teiserver.Battle.BalanceLib do
end

def get_default_algorithm() do
# For now it's a constant but this could be moved to a configurable value
@default_balance_algorithm
Config.get_site_config_cache("teiserver.Default balance algorithm")
end

@spec algorithm_modules() :: %{String.t() => module}
Expand All @@ -53,7 +50,8 @@ defmodule Teiserver.Battle.BalanceLib do
"loser_picks" => Teiserver.Battle.Balance.LoserPicks,
"force_party" => Teiserver.Battle.Balance.ForceParty,
"brute_force" => Teiserver.Battle.Balance.BruteForce,
"split_noobs" => Teiserver.Battle.Balance.SplitNoobs
"split_noobs" => Teiserver.Battle.Balance.SplitNoobs,
"default" => Teiserver.Battle.Balance.DefaultBalance
}
end

Expand All @@ -65,7 +63,7 @@ defmodule Teiserver.Battle.BalanceLib do
if(is_moderator) do
Teiserver.Battle.BalanceLib.algorithm_modules() |> Map.keys()
else
mod_only = ["force_party", "brute_force"]
mod_only = ["force_party", "brute_force", "loser_picks"]
Teiserver.Battle.BalanceLib.algorithm_modules() |> Map.drop(mod_only) |> Map.keys()
end
end
Expand Down
10 changes: 10 additions & 0 deletions lib/teiserver/libs/teiserver_configs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,16 @@ defmodule Teiserver.TeiserverConfigs do
default: true
})

add_site_config_type(%{
key: "teiserver.Default balance algorithm",
section: "Lobbies",
type: "select",
default: "loser_picks",
permissions: ["Admin"],
description: "The default balance algorithm",
opts: [choices: ["loser_picks", "default"]]
})

add_site_config_type(%{
key: "teiserver.Curse word score A",
section: "Lobbies",
Expand Down
2 changes: 1 addition & 1 deletion lib/teiserver/lobby/commands/explain_command.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ defmodule Teiserver.Lobby.Commands.ExplainCommand do
alias Teiserver.{Account, Battle, Coordinator}
import Teiserver.Helper.NumberHelper, only: [round: 2]

@splitter "---------------------------"
@splitter "------------------------------------------------------"

@impl true
@spec name() :: String.t()
Expand Down
3 changes: 0 additions & 3 deletions lib/teiserver/lobby/libs/lobby_restrictions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,6 @@ defmodule Teiserver.Lobby.LobbyRestrictions do
"To restrict this lobby to players who are new, use either:",
"!maxchevlevel <chevlevel>",
"!maxratinglevel <rating>",
"",
"To ensure new players are distributed evenly across teams:",
"!balancealgorithm split_noobs",
""
]

Expand Down
4 changes: 2 additions & 2 deletions test/teiserver/battle/balance_lib_internal_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ defmodule Teiserver.Battle.BalanceLibInternalTest do
is_moderator = true
result = BalanceLib.get_allowed_algorithms(is_moderator)

assert result == ["brute_force", "force_party", "loser_picks", "split_noobs"]
assert result == ["brute_force", "default", "force_party", "loser_picks", "split_noobs"]

is_moderator = false
result = BalanceLib.get_allowed_algorithms(is_moderator)
assert result == ["loser_picks", "split_noobs"]
assert result == ["default", "split_noobs"]
end

defp create_test_users do
Expand Down
4 changes: 2 additions & 2 deletions test/teiserver/lobby/commands/explain_command_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ defmodule Teiserver.Lobby.Commands.ExplainCommandTest do
channel: "teiserver_client_messages:#{user.id}",
event: :received_direct_message,
message_content: [
"---------------------------",
"------------------------------------------------------",
"No balance has been created for this room",
"---------------------------"
"------------------------------------------------------"
],
sender_id: Coordinator.get_coordinator_userid()
}
Expand Down

0 comments on commit 000f1ad

Please sign in to comment.