Skip to content

Commit

Permalink
Add outcome override for ladder 1v1 games (#669)
Browse files Browse the repository at this point in the history
  • Loading branch information
cleborys authored Sep 23, 2020
1 parent aecbf8f commit 1e72a29
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 3 deletions.
1 change: 1 addition & 0 deletions server/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ def __init__(self):
self.GEO_IP_LICENSE_KEY = ""
self.GEO_IP_DATABASE_MAX_AGE_DAYS = 22

self.LADDER_1V1_OUTCOME_OVERRIDE = True
self.LADDER_ANTI_REPETITION_LIMIT = 3
self.LADDER_SEARCH_EXPANSION_MAX = 0.25
self.LADDER_SEARCH_EXPANSION_STEP = 0.05
Expand Down
9 changes: 8 additions & 1 deletion server/games/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,11 @@ async def resolve_game_results(self) -> EndedGameInfo:
{self.get_player_outcome(player) for player in team}
for team in basic_info.teams
]
team_outcomes = resolve_game(team_player_partial_outcomes)
#TODO Remove override once game result messages are reliable
team_outcomes = (
self._outcome_override_hook()
or resolve_game(team_player_partial_outcomes)
)
except GameResolutionError:
await self.mark_invalid(ValidityState.UNKNOWN_RESULT)

Expand All @@ -439,6 +443,9 @@ async def resolve_game_results(self) -> EndedGameInfo:
basic_info, self.validity, team_outcomes, commander_kills
)

def _outcome_override_hook(self) -> Optional[List[GameOutcome]]:
return None

async def load_results(self):
"""
Load results from the database
Expand Down
19 changes: 18 additions & 1 deletion server/games/ladder_game.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import logging
from typing import List, Optional

from server.abc.base_game import InitMode
from server.config import config
from server.players import Player
from server.rating import RatingType

Expand All @@ -20,7 +22,7 @@ def __init__(self, id_, *args, **kwargs):
new_kwargs = {
"game_mode": FeaturedModType.LADDER_1V1,
"rating_type": RatingType.LADDER_1V1,
"max_players": 2
"max_players": 2,
}
new_kwargs.update(kwargs)
super().__init__(id_, *args, **new_kwargs)
Expand All @@ -34,3 +36,18 @@ def get_army_score(self, army: int) -> int:
as 1 for win and 0 for anything else.
"""
return self._results.victory_only_score(army)

def _outcome_override_hook(self) -> Optional[List[GameOutcome]]:
if not config.LADDER_1V1_OUTCOME_OVERRIDE or len(self.players) > 2:
return None
team_sets = self.get_team_sets()
army_scores = [
self._results.score(self.get_player_option(team_set.pop().id, "Army"))
for team_set in team_sets
]
if army_scores[0] > army_scores[1]:
return [GameOutcome.VICTORY, GameOutcome.DEFEAT]
elif army_scores[0] < army_scores[1]:
return [GameOutcome.DEFEAT, GameOutcome.VICTORY]
else:
return [GameOutcome.DRAW, GameOutcome.DRAW]
90 changes: 89 additions & 1 deletion tests/unit_tests/test_game_rating.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,11 @@ async def test_on_game_end_global_ratings(custom_game, players):
assert results.rating_type is RatingType.GLOBAL
assert players.hosting.id in results.ratings
assert players.joining.id in results.ratings
assert results.outcomes[players.hosting.id] is GameOutcome.VICTORY
assert results.outcomes[players.joining.id] is GameOutcome.DEFEAT


async def test_on_game_end_ladder_ratings(ladder_game, players):
async def test_on_game_end_ladder_ratings_(ladder_game, players):
rating_service = ladder_game.game_service._rating_service

ladder_game.state = GameState.LOBBY
Expand All @@ -206,6 +208,86 @@ async def test_on_game_end_ladder_ratings(ladder_game, players):
assert results.rating_type is RatingType.LADDER_1V1
assert players.hosting.id in results.ratings
assert players.joining.id in results.ratings
assert results.outcomes[players.hosting.id] is GameOutcome.VICTORY
assert results.outcomes[players.joining.id] is GameOutcome.DEFEAT


async def test_on_game_end_ladder_ratings_without_score_override(
ladder_game, players, mocker
):
mocker.patch("server.games.ladder_game.config.LADDER_1V1_OUTCOME_OVERRIDE", False)
rating_service = ladder_game.game_service._rating_service

ladder_game.state = GameState.LOBBY
add_connected_players(ladder_game, [players.hosting, players.joining])
ladder_game.set_player_option(players.hosting.id, "Team", 1)
ladder_game.set_player_option(players.joining.id, "Team", 1)

await ladder_game.launch()
await ladder_game.add_result(players.hosting.id, 0, "victory", 0)
await ladder_game.add_result(players.joining.id, 1, "defeat", 0)

await ladder_game.on_game_end()
await rating_service._join_rating_queue()

results = get_persisted_results(rating_service)
assert results.rating_type is RatingType.LADDER_1V1
assert players.hosting.id in results.ratings
assert players.joining.id in results.ratings
assert results.outcomes[players.hosting.id] is GameOutcome.VICTORY
assert results.outcomes[players.joining.id] is GameOutcome.DEFEAT


async def test_on_game_end_ladder_ratings_uses_score_override(
ladder_game, players, mocker
):
mocker.patch("server.games.ladder_game.config.LADDER_1V1_OUTCOME_OVERRIDE", True)
rating_service = ladder_game.game_service._rating_service

ladder_game.state = GameState.LOBBY
add_connected_players(ladder_game, [players.hosting, players.joining])
ladder_game.set_player_option(players.hosting.id, "Team", 1)
ladder_game.set_player_option(players.joining.id, "Team", 1)

await ladder_game.launch()
await ladder_game.add_result(players.hosting.id, 0, "defeat", 1)
await ladder_game.add_result(players.joining.id, 1, "defeat", 0)

await ladder_game.on_game_end()
await rating_service._join_rating_queue()

results = get_persisted_results(rating_service)
assert results.rating_type is RatingType.LADDER_1V1
assert players.hosting.id in results.ratings
assert players.joining.id in results.ratings
assert results.outcomes[players.hosting.id] is GameOutcome.VICTORY
assert results.outcomes[players.joining.id] is GameOutcome.DEFEAT


async def test_on_game_end_ladder_ratings_score_override_draw(
ladder_game, players, mocker
):
mocker.patch("server.games.ladder_game.config.LADDER_1V1_OUTCOME_OVERRIDE", True)
rating_service = ladder_game.game_service._rating_service

ladder_game.state = GameState.LOBBY
add_connected_players(ladder_game, [players.hosting, players.joining])
ladder_game.set_player_option(players.hosting.id, "Team", 1)
ladder_game.set_player_option(players.joining.id, "Team", 1)

await ladder_game.launch()
await ladder_game.add_result(players.hosting.id, 0, "defeat", 0)
await ladder_game.add_result(players.joining.id, 1, "defeat", 0)

await ladder_game.on_game_end()
await rating_service._join_rating_queue()

results = get_persisted_results(rating_service)
assert results.rating_type is RatingType.LADDER_1V1
assert players.hosting.id in results.ratings
assert players.joining.id in results.ratings
assert results.outcomes[players.hosting.id] is GameOutcome.DRAW
assert results.outcomes[players.joining.id] is GameOutcome.DRAW


async def test_on_game_end_rating_type_not_set(game, players):
Expand Down Expand Up @@ -306,10 +388,12 @@ async def test_rate_game_sum_of_scores_edge_case(custom_game, player_factory):
assert results.ratings[player.id] > Rating(
*player.ratings[RatingType.GLOBAL]
)
assert results.outcomes[player.id] is GameOutcome.VICTORY
else:
assert results.ratings[player.id] < Rating(
*player.ratings[RatingType.GLOBAL]
)
assert results.outcomes[player.id] is GameOutcome.DEFEAT


async def test_rate_game_only_one_survivor(custom_game, player_factory):
Expand Down Expand Up @@ -351,10 +435,12 @@ async def test_rate_game_only_one_survivor(custom_game, player_factory):
assert results.ratings[player.id] > Rating(
*player.ratings[RatingType.GLOBAL]
)
assert results.outcomes[player.id] is GameOutcome.VICTORY
else:
assert results.ratings[player.id] < Rating(
*player.ratings[RatingType.GLOBAL]
)
assert results.outcomes[player.id] is GameOutcome.DEFEAT


async def test_rate_game_two_player_FFA(custom_game, player_factory):
Expand Down Expand Up @@ -592,10 +678,12 @@ async def test_compute_rating_works_with_partially_unknown_results(
assert results.ratings[player.id] > Rating(
*player.ratings[RatingType.GLOBAL]
)
assert results.outcomes[player.id] is GameOutcome.VICTORY
else:
assert results.ratings[player.id] < Rating(
*player.ratings[RatingType.GLOBAL]
)
assert results.outcomes[player.id] is GameOutcome.DEFEAT


async def test_rate_game_single_ffa_vs_single_team(custom_game, player_factory):
Expand Down

0 comments on commit 1e72a29

Please sign in to comment.