mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-18 22:33:56 -07:00
Reviewers: elexis, Dunedan Fixes: #5914 Differential Revision: https://code.wildfiregames.com/D2630 This was SVN commit r25424.
150 lines
6.8 KiB
Python
150 lines
6.8 KiB
Python
# Copyright (C) 2021 Wildfire Games.
|
|
# This file is part of 0 A.D.
|
|
#
|
|
# 0 A.D. is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# 0 A.D. is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
"""Tests for the ELO-implementation."""
|
|
|
|
from unittest import TestCase
|
|
|
|
from hypothesis import assume, example, given
|
|
from hypothesis import strategies as st
|
|
from parameterized import parameterized
|
|
|
|
from xpartamupp.elo import (get_rating_adjustment, ANTI_INFLATION, ELO_K_FACTOR_CONSTANT_RATING,
|
|
ELO_SURE_WIN_DIFFERENCE, VOLATILITY_CONSTANT)
|
|
|
|
|
|
class TestELO(TestCase):
|
|
"""Test behavior of ELO calculation."""
|
|
|
|
@parameterized.expand([
|
|
([1000, 1000, 0, 0, 1], 82),
|
|
([1000, 1000, 0, 0, -1], -83),
|
|
([1000, 1000, 0, 0, 0], 0),
|
|
([1200, 1200, 0, 0, 1], 78),
|
|
([1200, 1200, 0, 0, -1], -78),
|
|
([1200, 1200, 0, 0, 0], 0),
|
|
([1200, 1200, 1, 0, 1], 65),
|
|
([1200, 1200, 1, 0, 0], 0),
|
|
([1200, 1200, 1, 0, -1], -65),
|
|
([1200, 1200, 100, 0, 1], 16),
|
|
([1200, 1200, 100, 0, 0], 0),
|
|
([1200, 1200, 100, 0, -1], -16),
|
|
([1200, 1200, 1000, 0, 1], 16),
|
|
([1200, 1200, 1000, 0, 0], 0),
|
|
([1200, 1200, 1000, 0, -1], -16),
|
|
([1200, 1200, 0, 1, 1], 78),
|
|
([1200, 1200, 0, 1, 0], 0),
|
|
([1200, 1200, 0, 1, -1], -78),
|
|
([1200, 1200, 0, 100, 1], 78),
|
|
([1200, 1200, 0, 100, 0], 0),
|
|
([1200, 1200, 0, 100, -1], -78),
|
|
([1200, 1200, 0, 1000, 1], 78),
|
|
([1200, 1200, 0, 1000, 0], 0),
|
|
([1200, 1200, 0, 1000, -1], -78),
|
|
([1400, 1000, 0, 0, 1], 24),
|
|
([1400, 1000, 0, 0, 0], -49),
|
|
([1400, 1000, 0, 0, -1], -122),
|
|
([1000, 1400, 0, 0, 1], 137),
|
|
([1000, 1400, 0, 0, 0], 55),
|
|
([1000, 1400, 0, 0, -1], -28),
|
|
([2200, 2300, 0, 0, 1], 70),
|
|
([2200, 2300, 0, 0, 0], 10),
|
|
([2200, 2300, 0, 0, -1], -50),
|
|
])
|
|
def test_valid_adjustments(self, args, expected_adjustment):
|
|
"""Test correctness of valid rating adjustments."""
|
|
self.assertEqual(get_rating_adjustment(*args), expected_adjustment)
|
|
|
|
@given(st.integers(min_value=ELO_K_FACTOR_CONSTANT_RATING),
|
|
st.integers(min_value=-2099, max_value=ELO_SURE_WIN_DIFFERENCE - 1), st.integers(),
|
|
st.integers(),
|
|
st.integers(min_value=-1, max_value=1))
|
|
@example(ELO_K_FACTOR_CONSTANT_RATING + 300, 0, 0, 0, 1)
|
|
def test_constant_rating(self, rating_player1, difference_player2, played_games_player1,
|
|
played_games_player2, result):
|
|
"""Test that points gained are constant above a threshold."""
|
|
volatility = 50.0 * (min(max(0, played_games_player1), VOLATILITY_CONSTANT) /
|
|
VOLATILITY_CONSTANT + 0.25) / 1.25
|
|
rating_adjustment = (difference_player2 + result * ELO_SURE_WIN_DIFFERENCE) / volatility \
|
|
- ANTI_INFLATION
|
|
if result == 1:
|
|
expected_adjustment = max(0.0, rating_adjustment)
|
|
elif result == -1:
|
|
expected_adjustment = min(0.0, rating_adjustment)
|
|
else:
|
|
expected_adjustment = rating_adjustment
|
|
|
|
self.assertEqual(get_rating_adjustment(rating_player1, rating_player1 + difference_player2,
|
|
played_games_player1, played_games_player2, result),
|
|
round(expected_adjustment))
|
|
|
|
@given(st.data())
|
|
def test_sure_win(self, data):
|
|
"""Test behavior if winning player 1 has >600 points more.
|
|
|
|
In this case the winning player shouldn't gain points, as it
|
|
was a "sure win" and the loosing player shouldn't loose
|
|
points.
|
|
"""
|
|
rating_player1 = data.draw(st.integers(min_value=-1599))
|
|
difference_player2 = data.draw(st.integers(min_value=ELO_SURE_WIN_DIFFERENCE))
|
|
assume(rating_player1 - difference_player2 > -2200)
|
|
played_games_player1 = data.draw(st.integers())
|
|
played_games_player2 = data.draw(st.integers())
|
|
|
|
self.assertEqual(get_rating_adjustment(rating_player1,
|
|
rating_player1 - difference_player2,
|
|
played_games_player1, played_games_player2, 1),
|
|
0)
|
|
self.assertEqual(get_rating_adjustment(rating_player1 - difference_player2,
|
|
rating_player1, played_games_player2,
|
|
played_games_player1, -1), 0)
|
|
|
|
@given(st.integers(min_value=-2199), st.integers(min_value=ELO_SURE_WIN_DIFFERENCE),
|
|
st.integers(),
|
|
st.integers())
|
|
@example(1000, ELO_SURE_WIN_DIFFERENCE, 0, 0)
|
|
def test_sure_loss(self, rating_player1, difference_player2, played_games_player1,
|
|
played_games_player2):
|
|
"""Test behavior if winning player 2 has >600 points more.
|
|
|
|
In this case the winning player shouldn't gain points, as it
|
|
was a "sure win" and the loosing player shouldn't loose
|
|
points.
|
|
"""
|
|
self.assertEqual(get_rating_adjustment(rating_player1,
|
|
rating_player1 - difference_player2 * -1,
|
|
played_games_player1, played_games_player2, -1),
|
|
0)
|
|
self.assertEqual(get_rating_adjustment(rating_player1 - difference_player2 * -1,
|
|
rating_player1, played_games_player2,
|
|
played_games_player1, 1), 0)
|
|
|
|
@given(st.integers(max_value=-2200), st.integers(),
|
|
st.integers(),
|
|
st.integers(),
|
|
st.one_of(st.just(1), st.just(-1)))
|
|
@example(-2200, 2000, 0, 0, 1)
|
|
@example(2000, -2200, 0, 0, 1)
|
|
def test_minus_2200_bug_workaround(self, rating_player1, rating_player2,
|
|
played_games_player1, played_games_player2, result):
|
|
"""Test workaround for -2200 bug."""
|
|
with self.assertRaises(ValueError):
|
|
get_rating_adjustment(rating_player1, rating_player2, played_games_player1,
|
|
played_games_player2, result)
|
|
with self.assertRaises(ValueError):
|
|
get_rating_adjustment(rating_player2, rating_player1, played_games_player1,
|
|
played_games_player2, result)
|