def record_balance(answer, winner, dota_id=None): from app.ladder.models import Player, Match, MatchPlayer from app.ladder.models import LadderSettings players = [p[0] for t in answer.teams for p in t['players']] players = Player.objects.filter(name__in=players) # check that all players from balance exist # (we don't allow CustomBalance results here) if len(players) < 10: return None with transaction.atomic(): match = Match.objects.create( winner=winner, balance=answer, season=LadderSettings.get_solo().current_season, dota_id=dota_id, ) for i, team in enumerate(answer.teams): for player in team['players']: player = next(p for p in players if p.name == player[0]) MatchPlayer.objects.create(match=match, player=player, team=i) MatchManager.add_scores(match) Player.objects.update_ranks() return match
def score_history(self): player = self.object season = LadderSettings.get_solo().current_season score_changes = player.scorechange_set.filter(season=season)\ .select_related( 'match', 'match__match', 'match__match__balance', 'match__match__balance__result' ) max_vals = Player.objects\ .filter(matchplayer__match__season=season).distinct()\ .aggregate(Max('score'), Max('ladder_mmr')) score_max = max(1, max_vals['score__max'] or player.score) mmr_max = max(1, max_vals['ladder_mmr__max'] or player.score) score = mmr = 0 for scoreChange in reversed(score_changes): score += scoreChange.score_change mmr += scoreChange.mmr_change scoreChange.score = score scoreChange.mmr = mmr scoreChange.score_percent = float(score) / score_max * 100 scoreChange.mmr_percent = float(mmr) / mmr_max * 100 return score_changes
def handle(self, *args, **options): with transaction.atomic(): ladder = LadderSettings.get_solo() ladder.current_season += 1 ladder.save() for player in Player.objects.all(): PlayerManager.init_score(player)
def get_object(self, queryset=None): player = super(PlayerDetail, self).get_object(queryset) season = LadderSettings.get_solo().current_season player.matches = player.matchplayer_set\ .filter(match__season=season)\ .select_related('match') return player
def get_queryset(self): qs = super(MatchList, self).get_queryset() season = LadderSettings.get_solo().current_season qs = qs.filter(matchplayer__match__season=season).distinct()\ .prefetch_related(Prefetch( 'matchplayer_set', queryset=MatchPlayer.objects.select_related('match') )) return qs
def add_scores(match): from app.ladder.models import ScoreChange from app.ladder.models import LadderSettings # TODO: make values like win/loss change and underdog bonus changeble in admin panel mmr_diff = match.balance.teams[0]['mmr'] - match.balance.teams[1]['mmr'] underdog = 0 if mmr_diff <= 0 else 1 underdog_bonus = abs( mmr_diff ) // MatchManager.underdog_diff * 15 # 15 mmr points for each 200 mmr diff underdog_bonus = min(15, underdog_bonus) # but no more than 15 mmr print('mmr diff: %d' % mmr_diff) print('underdog: %d' % underdog) print('underdog bonus: %d' % underdog_bonus) print('') for matchPlayer in match.matchplayer_set.all(): is_victory = 1 if matchPlayer.team == match.winner else -1 is_underdog = 1 if matchPlayer.team == underdog else -1 score_change = 1 * is_victory mmr_per_game = LadderSettings.get_solo().mmr_per_game mmr_change = mmr_per_game * is_victory mmr_change += underdog_bonus * is_underdog use_boundary = False # TODO: get this values from LadderSettings if use_boundary: # make sure new ladder mmr is in boundaries player = matchPlayer.player new_mmr = player.ladder_mmr + mmr_change new_mmr = max(player.min_allowed_mmr, min(new_mmr, player.max_allowed_mmr)) mmr_change = new_mmr - player.ladder_mmr ScoreChange.objects.create( player=matchPlayer.player, score_change=score_change, mmr_change=mmr_change, match=matchPlayer, season=LadderSettings.get_solo().current_season, )
def handle(self, *args, **options): with transaction.atomic(): for player in Player.objects.all(): mmr_diff = player.dota_mmr - player.ladder_mmr ScoreChange.objects.create( player=player, mmr_change=mmr_diff, info='Updated MMR system', season=LadderSettings.get_solo().current_season, )
def score_change(instance, **kwargs): player = instance.player season = LadderSettings.get_solo().current_season vals = player.scorechange_set.filter(season=season).aggregate( Sum('mmr_change'), Sum('score_change')) player.ladder_mmr = vals['mmr_change__sum'] player.score = vals['score_change__sum'] player.save()
def balance_custom(teams): from app.balancer.models import BalanceAnswer mmr_exponent = LadderSettings.get_solo().balance_exponent answer = balance_from_teams(teams, mmr_exponent) answer = BalanceAnswer.objects.create( teams=answer['teams'], mmr_diff=answer['mmr_diff'], mmr_diff_exp=answer['mmr_diff_exp'], ) return answer
def get_context_data(self, **kwargs): context = super(LadderStats, self).get_context_data(**kwargs) all_time = Match.objects.all() this_season = Match.objects.filter( season=LadderSettings.get_solo().current_season) last_days = Match.objects.filter(date__gte=datetime.now() - timedelta(days=3)) context.update({ 'all_time': self.get_stats(all_time), 'this_season': self.get_stats(this_season), 'last_days': self.get_stats(last_days), }) return context
def init_score(player, reset_mmr=False): from app.ladder.models import ScoreChange from app.ladder.models import LadderSettings if reset_mmr: initial_mmr = PlayerManager.dota_to_ladder_mmr(player.dota_mmr) else: # take mmr from last season initial_mmr = player.ladder_mmr ScoreChange.objects.create( player=player, score_change=25, mmr_change=initial_mmr, info='Season started', season=LadderSettings.get_solo().current_season, ) player.min_allowed_mmr = initial_mmr - 1000 player.max_allowed_mmr = initial_mmr + 1000 player.save()
def balance_teams(players, role_balancing=True): from app.balancer.models import BalanceResult, BalanceAnswer # balance teams and save result # TODO: make mmr_exponent changable from admin panel mmr_exponent = LadderSettings.get_solo().balance_exponent if role_balancing: answers = role_balance_teams(players, mmr_exponent) else: players = [(p.name, p.ladder_mmr) for p in players] answers = balance_teams(players, mmr_exponent) with transaction.atomic(): result = BalanceResult.objects.create(mmr_exponent=mmr_exponent) for answer in answers: BalanceAnswer.objects.create( teams=answer['teams'], mmr_diff=answer['mmr_diff'], mmr_diff_exp=answer['mmr_diff_exp'], result=result) return result
def update_ranks(self): from app.ladder.models import LadderSettings # recalculate player rankings by particular field (ladder_mmr or score) def update_ranks_by(field): players.sort(key=lambda p: getattr(p, field), reverse=True) ranks = [1] for i in range(1, len(players)): curr_val = getattr(players[i], field) prev_val = getattr(players[i - 1], field) ranks.append(ranks[i - 1] if curr_val == prev_val else i + 1) for i, player in enumerate(players): setattr(player, f'rank_{field}', ranks[i]) player.save() season = LadderSettings.get_solo().current_season players = self.filter(matchplayer__match__season=season).distinct() players = players or self.all() players = list(players) # evaluate update_ranks_by('ladder_mmr') update_ranks_by('score')