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

Split Team ratings into Small team and big team #296

Merged
merged 26 commits into from
Jun 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ runtime/
/source_deploy.tar.gz
central.tar.gz
/bin
.vscode/launch.json

# I'm storing server output here, don't want to push it to git
example_output
Expand Down
3 changes: 1 addition & 2 deletions lib/teiserver/account/reports/mapping_report.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ defmodule Teiserver.Game.MappingReport do
search: [
started_after: start_date |> Timex.to_datetime(),
started_before: end_date |> Timex.to_datetime(),
# game_type_in: ["Duel", "Team", "FFA", "Team FFA"],
game_type_in: ["Duel", "Team"],
game_type_in: ["Duel", "Small Team", "Big Team"],
of_interest: true,
has_winning_team: true
],
Expand Down
2 changes: 1 addition & 1 deletion lib/teiserver/account/reports/open_skill_report.ex
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ defmodule Teiserver.Account.OpenSkillReport do
defp apply_defaults(params) do
Map.merge(
%{
"rating_type" => "Team",
"rating_type" => "Big Team",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd make an issue to improve this report and make this a selector but I have a started branch with that anyway

"metric" => "Game Rating",
"last_active" => "7 days",
"uncertainty" => "5"
Expand Down
4 changes: 3 additions & 1 deletion lib/teiserver/account/reports/user_age_report.ex
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ defmodule Teiserver.Account.UserAgeReport do
type_where =
case params["game_type"] do
"Duel" -> "AND m.game_type = 'Duel'"
"Team" -> "AND m.game_type = 'Team'"
"Team" -> "AND m.game_type IN ('Small Team', 'Big Team')"
"Small Team" -> "AND m.game_type = 'Small Team'"
"Big Team" -> "AND m.game_type = 'Big Team'"
"FFA" -> "AND m.game_type = 'FFA'"
"Raptors" -> "AND m.game_type = 'Raptors'"
"Scavengers" -> "AND m.game_type = 'Scavengers'"
Expand Down
15 changes: 12 additions & 3 deletions lib/teiserver/account/tasks/recalculate_user_cache_task.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ defmodule Teiserver.Account.RecacheUserStatsTask do
case match.game_type do
"Duel" -> do_match_processed_duel(userid)
"FFA" -> do_match_processed_duel(userid)
"Team" -> do_match_processed_team(userid)
"Small Team" -> do_match_processed_team_small(userid)
"Big Team" -> do_match_processed_team_big(userid)
_ -> :ok
end

Expand Down Expand Up @@ -105,8 +106,16 @@ defmodule Teiserver.Account.RecacheUserStatsTask do
:ok
end

def do_match_processed_team(userid) do
filter_type_id = MatchRatingLib.rating_type_name_lookup()["Team"]
def do_match_processed_team_big(userid) do
do_match_processed_team(userid, "Big Team")
end

def do_match_processed_team_small(userid) do
do_match_processed_team(userid, "Small Team")
end

defp do_match_processed_team(userid, team_subtype) do
filter_type_id = MatchRatingLib.rating_type_name_lookup()[team_subtype]

logs =
Game.list_rating_logs(
Expand Down
65 changes: 37 additions & 28 deletions lib/teiserver/battle/libs/match_lib.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Teiserver.Battle.MatchLib do
@moduledoc false
use TeiserverWeb, :library
alias Teiserver.{Battle, Account}
alias Teiserver.{Config, Battle, Account}
alias Teiserver.Battle.{Match, MatchMembership}
alias Teiserver.Data.Types, as: T
require Logger
Expand All @@ -12,42 +12,36 @@ defmodule Teiserver.Battle.MatchLib do
@spec colours :: atom
def colours, do: :success2

@spec game_type(T.lobby(), map()) :: <<_::24, _::_*8>>
def game_type(lobby, teams) do
bots = Battle.get_bots(lobby.id)
def game_type(team_size, team_count) do
game_type(team_size, team_count, %{})
end

def game_type(team_size, team_count, bots) do
max_small_team_size = Config.get_site_config_cache("lobby.Small team game limit")

bot_names =
bots
|> Map.keys()
|> Enum.join(" ")

# It is possible for it to be purely bots v bots which will make it appear to be empty teams
# this would cause an error with Enum.max, hence the case statement
max_team_size =
case Enum.map(teams, fn {_, team} -> Enum.count(team) end) do
[] ->
0

counts ->
Enum.max(counts)
end

cond do
String.contains?(bot_names, "Scavenger") -> "Scavengers"
String.contains?(bot_names, "Chicken") -> "Raptors"
String.contains?(bot_names, "Raptor") -> "Raptors"
Enum.empty?(bots) == false -> "Bots"
Enum.count(teams) == 2 and max_team_size == 1 -> "Duel"
Enum.count(teams) == 2 -> "Team"
max_team_size == 1 -> "FFA"
team_count == 2 and team_size == 1 -> "Duel"
team_count == 2 and team_size <= max_small_team_size -> "Small Team"
team_count == 2 and team_size > max_small_team_size -> "Big Team"
team_size == 1 -> "FFA"
true -> "Team FFA"
end
end

def list_game_types() do
[
"Duel",
"Team",
"Small Team",
"Big Team",
"FFA",
"Team FFA",
"Raptors",
Expand All @@ -59,7 +53,8 @@ defmodule Teiserver.Battle.MatchLib do
def list_rated_game_types() do
[
"Duel",
"Team",
"Small Team",
"Big Team",
"FFA",
"Team FFA"
]
Expand All @@ -85,18 +80,25 @@ defmodule Teiserver.Battle.MatchLib do
|> Enum.group_by(fn c -> c.team_number end)

if teams != %{} do
the_game_type = game_type(lobby, teams)
team_count = teams |> count

team_size =
teams
|> Enum.map(fn {_, t} -> t |> count end)
|> Enum.max(fn -> 0 end)

game_type = game_type(team_size, team_count, bots)

match = %{
uuid: match_uuid,
server_uuid: server_uuid,
map: lobby.map_name,
data: nil,
tags: modoptions,
team_count: Enum.count(teams),
team_size: Enum.max(Enum.map(teams, fn {_, t} -> Enum.count(t) end)),
team_count: team_count,
team_size: team_size,
passworded: lobby.passworded,
game_type: the_game_type,
game_type: game_type,
founder_id: lobby.founder_id,
bots: bots,
queue_id: queue_id,
Expand Down Expand Up @@ -141,10 +143,17 @@ defmodule Teiserver.Battle.MatchLib do

def make_match_name(match) do
case match.game_type do
"Duel" -> "Duel on #{match.map}"
"Team" -> "#{match.team_size}v#{match.team_size} on #{match.map}"
"FFA" -> "#{match.team_count} way FFA on #{match.map}"
t -> "#{t} game on #{match.map}"
"Duel" ->
"Duel on #{match.map}"

type when type in ["Small Team", "Big Team"] ->
"#{match.team_size}v#{match.team_size} on #{match.map}"

"FFA" ->
"#{match.team_count} way FFA on #{match.map}"

t ->
"#{t} game on #{match.map}"
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/teiserver/battle/schemas/match.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ defmodule Teiserver.Battle.Match do
field :team_size, :integer
field :passworded, :boolean
field :processed, :boolean, default: false
# Scavengers, Raptors, Bots, Duel, Team, FFA, Team FFA
# Scavengers, Raptors, Bots, Duel, Small Team, Big Team, FFA, Team FFA
field :game_type, :string

belongs_to :founder, Teiserver.Account.User
Expand Down
3 changes: 2 additions & 1 deletion lib/teiserver/battle/tasks/breakdown_match_data_task.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ defmodule Teiserver.Battle.Tasks.BreakdownMatchDataTask do

%{
duel: get_subset_data(matches, game_type: "Duel"),
team: get_subset_data(matches, game_type: "Team"),
small_team: get_subset_data(matches, game_type: "Small Team"),
big_team: get_subset_data(matches, game_type: "Big Team"),
ffa: get_subset_data(matches, game_type: "FFA"),
team_ffa: get_subset_data(matches, game_type: "Team FFA"),
scavengers: get_subset_data(matches, game_type: "Scavengers"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ defmodule Teiserver.Battle.ExportRawMatchMetricsTask do
defp do_output(data, _params) do
data
|> Stream.filter(fn match ->
match.game_type == "Team"
match.game_type in ["Small Team", "Big Team"]
end)
|> Stream.map(fn match ->
members =
Expand Down
5 changes: 3 additions & 2 deletions lib/teiserver/coordinator/consul_server.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ defmodule Teiserver.Coordinator.ConsulServer do
alias Teiserver.Lobby.{ChatLib}
import Teiserver.Helper.NumberHelper, only: [int_parse: 1]
alias Phoenix.PubSub
alias Teiserver.Battle.BalanceLib
alias Teiserver.Battle.{BalanceLib, MatchLib}
alias Teiserver.Data.Types, as: T
alias Teiserver.Coordinator.{ConsulCommands, CoordinatorLib, SpadsParser}

Expand Down Expand Up @@ -835,9 +835,10 @@ defmodule Teiserver.Coordinator.ConsulServer do
@spec user_allowed_to_play?(T.user(), T.client(), map()) :: boolean()
defp user_allowed_to_play?(user, client, state) do
player_list = list_players(state)
rating_type = MatchLib.game_type(state.host_teamsize, state.host_teamcount)

{player_rating, player_uncertainty} =
BalanceLib.get_user_rating_value_uncertainty_pair(user.id, "Team")
BalanceLib.get_user_rating_value_uncertainty_pair(user.id, rating_type)

player_rating = max(player_rating, 1)
avoid_status = Account.check_avoid_status(user.id, player_list)
Expand Down
8 changes: 4 additions & 4 deletions lib/teiserver/game/libs/match_rating_lib.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ defmodule Teiserver.Game.MatchRatingLib do
alias Teiserver.Battle.{BalanceLib, MatchLib}
require Logger

@rated_match_types ["Team", "Duel", "FFA", "Team FFA", "Partied Team"]
@rated_match_types ["Small Team", "Big Team", "Duel", "Team", "FFA", "Team FFA", "Partied Team"]
# TODO Remove "Team" from here once the split is done

@spec rating_type_list() :: [String.t()]
def rating_type_list() do
Expand Down Expand Up @@ -192,7 +193,7 @@ defmodule Teiserver.Game.MatchRatingLib do
rate_result = rate_with_ids([winner_ratings, loser_ratings], as_map: true)

status_lookup =
if match.game_type == "Team" do
if match.game_type in ["Small Team", "Big Team"] do
match.members
|> Map.new(fn membership ->
{membership.user_id,
Expand Down Expand Up @@ -358,7 +359,7 @@ defmodule Teiserver.Game.MatchRatingLib do
win_result = Map.new(win_result)

status_lookup =
if Enum.member?(["Team", "Team FFA"], match.game_type) do
if Enum.member?(["Small Team", "Big Team", "Team FFA"], match.game_type) do
match.members
|> Map.new(fn membership ->
{membership.user_id,
Expand Down Expand Up @@ -772,7 +773,6 @@ defmodule Teiserver.Game.MatchRatingLib do
results =
Battle.list_matches(
search: [
# game_type_in: ["Team"],
game_type_in: @rated_match_types,
processed: true,
started_after: Timex.now() |> Timex.shift(days: -31)
Expand Down
37 changes: 17 additions & 20 deletions lib/teiserver/game/servers/balancer_server.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule Teiserver.Game.BalancerServer do
alias Teiserver.Data.Types, as: T
alias Teiserver.Battle.BalanceLib
alias Teiserver.{Battle, Coordinator}
alias Teiserver.Battle.MatchLib

@tick_interval 2_000

Expand Down Expand Up @@ -161,28 +162,25 @@ defmodule Teiserver.Game.BalancerServer do

@spec do_make_balance(non_neg_integer(), [T.client()], List.t()) :: map()
defp do_make_balance(team_count, players, opts) do
player_count = Enum.count(players)

rating_type =
cond do
player_count == 2 ->
"Duel"
teams =
players
|> Enum.group_by(fn c -> c.team_number end)

team_count > 2 ->
if player_count > team_count, do: "Team", else: "FFA"
team_size =
teams
|> Enum.map(fn {_, t} -> Enum.count(t) end)
|> Enum.max(fn -> 0 end)

true ->
"Team"
end
game_type = MatchLib.game_type(team_size, team_count)

if opts[:allow_groups] do
party_result = make_grouped_balance(team_count, players, rating_type, opts)
party_result = make_grouped_balance(team_count, players, game_type, opts)

if party_result.deviation > opts[:max_deviation] do
make_solo_balance(
team_count,
players,
rating_type,
game_type,
[
"Tried grouped mode, got a deviation of #{party_result.deviation} and reverted to solo mode"
],
Expand All @@ -192,12 +190,12 @@ defmodule Teiserver.Game.BalancerServer do
party_result
end
else
make_solo_balance(team_count, players, rating_type, [], opts)
make_solo_balance(team_count, players, game_type, [], opts)
end
end

@spec make_grouped_balance(non_neg_integer(), [T.client()], String.t(), list()) :: map()
defp make_grouped_balance(team_count, players, rating_type, opts) do
defp make_grouped_balance(team_count, players, game_type, opts) do
# Group players into parties
partied_players =
players
Expand All @@ -212,15 +210,14 @@ defmodule Teiserver.Game.BalancerServer do
player_id_list
|> Enum.map(fn userid ->
%{
userid =>
BalanceLib.get_user_rating_rank(userid, rating_type, opts[:fuzz_multiplier])
userid => BalanceLib.get_user_rating_rank(userid, game_type, opts[:fuzz_multiplier])
}
end)

{_party_id, player_id_list} ->
player_id_list
|> Map.new(fn userid ->
{userid, BalanceLib.get_user_rating_rank(userid, rating_type, opts[:fuzz_multiplier])}
{userid, BalanceLib.get_user_rating_rank(userid, game_type, opts[:fuzz_multiplier])}
end)
end)
|> List.flatten()
Expand All @@ -231,12 +228,12 @@ defmodule Teiserver.Game.BalancerServer do

@spec make_solo_balance(non_neg_integer(), [T.client()], String.t(), [String.t()], list()) ::
map()
defp make_solo_balance(team_count, players, rating_type, initial_logs, opts) do
defp make_solo_balance(team_count, players, game_type, initial_logs, opts) do
groups =
players
|> Enum.map(fn %{userid: userid} ->
%{
userid => BalanceLib.get_user_rating_rank(userid, rating_type, opts[:fuzz_multiplier])
userid => BalanceLib.get_user_rating_rank(userid, game_type, opts[:fuzz_multiplier])
}
end)

Expand Down
9 changes: 9 additions & 0 deletions lib/teiserver/libs/teiserver_configs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,15 @@ defmodule Teiserver.TeiserverConfigs do
"The percentage of players who would need to avoid someone to prevent them becoming a player",
default: 50
})

add_site_config_type(%{
key: "lobby.Small team game limit",
section: "Lobbies",
type: "integer",
permissions: ["Admin"],
description: "Maximum team size to be considered as a small team game",
default: 5
})
end

defp discord_configs() do
Expand Down
Loading
Loading