def test_make_teams_single_2v2_large_pool(player_factory): """ When we have a large number of players all with similar ratings, we want teams to be formed by putting players with the same rating on the same team. """ # Large enough so the test is unlikely to pass by chance num = 40 searches = [ Search([player_factory(random.uniform(950, 1050), 10, name=f"p{i}")]) for i in range(num) ] searches += [ Search([player_factory(random.uniform(450, 550), 10, name=f"p{i}")]) for i in range(num) ] matched, non_matched = algorithm.make_teams_from_single(searches, size=2) assert matched != [] assert non_matched == [] for search in matched: p1, p2 = search.players p1_mean, _ = p1.ratings[RatingType.LADDER_1V1] p2_mean, _ = p2.ratings[RatingType.LADDER_1V1] # assert math.fabs(p1_mean - p2_mean) <= 100
async def test_cancel_twice(ladder_service: LadderService, player_factory): p1 = player_factory('Dostya', player_id=1, ladder_rating=(1500, 500), ladder_games=0) p2 = player_factory('Brackman', player_id=2, ladder_rating=(2000, 500), ladder_games=0) search = Search([p1]) search2 = Search([p2]) await ladder_service.start_search(p1, search, 'ladder1v1') await ladder_service.start_search(p2, search2, 'ladder1v1') searches = ladder_service._cancel_existing_searches(p1) assert search.is_cancelled assert searches == [search] assert not search2.is_cancelled searches = ladder_service._cancel_existing_searches(p1) assert searches == [] searches = ladder_service._cancel_existing_searches(p2) assert search2.is_cancelled assert searches == [search2]
def test_make_teams_single_2v2_small_pool(player_factory): """ When we have a small number of players, we want teams to be formed by distributing players of equal skill to different teams so that we can maximize the chances of getting a match. """ # Try a bunch of times so it is unlikely to pass by chance for _ in range(20): searches = [ Search([player_factory(random.gauss(1000, 5), 10, name=f"p{i}")]) for i in range(2) ] searches += [ Search([player_factory(random.gauss(500, 5), 10, name=f"r{i}")]) for i in range(2) ] matched, non_matched = algorithm.make_teams_from_single(searches, size=2) assert matched != [] assert non_matched == [] for search in matched: p1, p2 = search.players # Order doesn't matter if p1.ratings[RatingType.LADDER_1V1][0] > 900: assert p2.ratings[RatingType.LADDER_1V1][0] < 600 else: assert p1.ratings[RatingType.LADDER_1V1][0] < 600 assert p2.ratings[RatingType.LADDER_1V1][0] > 900
async def test_queue_mid_cancel(matchmaker_queue, matchmaker_players_all_match): # Turn list of players into map from ids to players. _, p1, p2, p3, _ = matchmaker_players_all_match (s1, s2, s3) = (Search([p1]), Search([p2]), Search([p3])) asyncio.create_task(matchmaker_queue.search(s1)) asyncio.create_task(matchmaker_queue.search(s2)) s1.cancel() async def find_matches(): await asyncio.sleep(0.01) await matchmaker_queue.find_matches() try: await asyncio.gather( asyncio.wait_for(matchmaker_queue.search(s3), 0.1), asyncio.create_task(find_matches())) except CancelledError: pass assert not s1.is_matched assert s2.is_matched assert s3.is_matched assert len(matchmaker_queue._queue) == 0 matchmaker_queue.on_match_found.assert_called_once_with( s2, s3, matchmaker_queue)
async def test_cancel_twice(ladder_service: LadderService): p1 = mock.create_autospec(Player('Dostya', id=1)) p1.ladder_rating = (1500, 500) p1.numGames = 0 p2 = mock.create_autospec(Player('Brackman', id=1)) p2.ladder_rating = (2000, 50) p2.numGames = 0 search = Search([p1]) search2 = Search([p2]) ladder_service.start_search(p1, search, 'ladder1v1') ladder_service.start_search(p2, search2, 'ladder1v1') searches = ladder_service._cancel_existing_searches(p1) assert search.is_cancelled assert searches == [search] assert not search2.is_cancelled searches = ladder_service._cancel_existing_searches(p1) assert searches == [] searches = ladder_service._cancel_existing_searches(p2) assert search2.is_cancelled assert searches == [search2]
async def test_queue_mid_cancel(mocker, player_service, matchmaker_queue, matchmaker_players_all_match): # Turn list of players into map from ids to players. player_service.players = dict( map(lambda x: (x.id, x), list(matchmaker_players_all_match))) p0, p1, p2, p3, _ = matchmaker_players_all_match (s1, s2, s3) = (Search([p1]), Search([p2]), Search([p3])) asyncio.ensure_future(matchmaker_queue.search(s1)) asyncio.ensure_future(matchmaker_queue.search(s2)) s1.cancel() async def find_matches(): await asyncio.sleep(0.01) matchmaker_queue.find_matches() try: await asyncio.gather( asyncio.wait_for(matchmaker_queue.search(s3), 0.1), asyncio.ensure_future(find_matches())) except CancelledError: pass assert not s1.is_matched assert s2.is_matched assert s3.is_matched assert len(matchmaker_queue) == 0
def test_unmatched_newbies_do_not_forcefully_match_teams(p): newbie = Search([p(1500, 500, ladder_games=0)]) team = Search([p(1500, 100), p(1500, 100)]) searches = [newbie, team] matches = algorithm.RandomlyMatchNewbies().find(searches) assert len(matches) == 0
def test_unmatched_newbies_do_notforcefully_match_top_players(p): newbie = Search([p(1500, 500, ladder_games=0)]) top_player = Search([p(2500, 10, ladder_games=100)]) searches = [newbie, top_player] matches = algorithm.RandomlyMatchNewbies().find(searches) assert len(matches) == 0
def test_unmatched_newbies_forcefully_match_pros(p): newbie = Search([p(1500, 500, ladder_games=0)]) pro = Search([p(1400, 10, ladder_games=100)]) searches = [newbie, pro] matches = algorithm.RandomlyMatchNewbies().find(searches) assert len(matches) == 2
async def test_search_await(mocker, loop, matchmaker_players): p1, p2, _, _, _, _ = matchmaker_players s1, s2 = Search([p1]), Search([p2]) assert not s1.matches_with(s2) await_coro = asyncio.ensure_future(s1.await_match()) s1.match(s2) await asyncio.wait_for(await_coro, 1) assert await_coro.done()
def test_search_await(mocker, loop, matchmaker_players): p1, p2, _, _, _ = matchmaker_players s1, s2 = Search(p1), Search(p2) assert not s1.matches_with(s2) await_coro = asyncio. async (s1.await_match()) s1.match(s2) yield from asyncio.wait_for(await_coro, 1) assert await_coro.done()
def test_combined_search_attributes(matchmaker_players): p1, p2, p3, _, _, _ = matchmaker_players search = CombinedSearch(Search([p1, p2]), Search([p3])) assert search.players == [p1, p2, p3] assert search.raw_ratings == [ p1.ratings[RatingType.LADDER_1V1], p2.ratings[RatingType.LADDER_1V1], p3.ratings[RatingType.LADDER_1V1] ]
async def test_search_await(matchmaker_players): p1, p2, _, _, _, _ = matchmaker_players s1, s2 = Search([p1]), Search([p2]) assert not s1.matches_with(s2) await_coro = asyncio.create_task(s1.await_match()) s1.match(s2) await asyncio.wait_for(await_coro, 1) assert await_coro.done()
def test_remove_isolated(player_factory): s1 = Search([player_factory(1500, 64, ladder_games=20)]) s2 = Search([player_factory(1500, 63, ladder_games=20)]) s3 = Search([player_factory(1600, 75, ladder_games=50)]) ranks = add_graph_edge_weights({s1: [s3], s2: [], s3: [s1]}) algorithm._MatchingGraph.remove_isolated(ranks) assert ranks == add_graph_edge_weights({s1: [s3], s3: [s1]})
def test_matchmaker_random_only(player_factory): newbie1 = Search([player_factory(1550, 500, ladder_games=1)]) newbie2 = Search([player_factory(200, 400, ladder_games=9)]) searches = (newbie1, newbie2) match_pairs = algorithm.make_matches(searches) match_sets = [set(pair) for pair in match_pairs] assert {newbie1, newbie2} in match_sets
def test_unmatched_newbies_do_not_forcefully_match_top_players(player_factory): newbie = Search([player_factory(1500, 500, ladder_games=0)]) top_player = Search([player_factory(2500, 10, ladder_games=100)]) top_player.register_failed_matching_attempt() searches = [newbie, top_player] matches = algorithm.RandomlyMatchNewbies().find(searches) assert len(matches) == 0
def test_remove_isolated_2(player_factory): s1 = Search([player_factory(1500, 64, ladder_games=20)]) s2 = Search([player_factory(1500, 63, ladder_games=20)]) s3 = Search([player_factory(1600, 75, ladder_games=50)]) ranks = {s1: [], s2: [], s3: []} algorithm._MatchingGraph.remove_isolated(ranks) assert ranks == {}
def test_match_graph_will_not_include_matches_below_threshold_quality( player_factory, build_func): s1 = Search([player_factory(1500, 500)]) s2 = Search([player_factory(2000, 300)]) searches = [s1, s2] ranks = build_func(searches) assert ranks == {s1: [], s2: []}
def test_newbies_are_forcefully_matched_with_newbies(p): newbie1 = Search([p(0, 500, ladder_games=9)]) newbie2 = Search([p(1500, 500, ladder_games=9)]) pro = Search([p(1500, 10, ladder_games=100)]) searches = [newbie1, pro, newbie2] matches = algorithm.RandomlyMatchNewbies().find(searches) assert matches[newbie1] == newbie2 assert matches[newbie2] == newbie1
def test_search_quality_equivalence(player_factory, rating1, rating2): p1 = player_factory("Player1", ladder_rating=rating1, with_lobby_connection=False) p2 = player_factory("Player2", ladder_rating=rating2, with_lobby_connection=False) s1 = Search([p1]) s2 = Search([p2]) assert s1.quality_with(s2) == s2.quality_with(s1)
def unmatched_newbie_teams_do_not_forcefully_match_pros(p): newbie_team = Search( [p(1500, 500, ladder_games=0), p(1500, 500, ladder_games=0)]) pro = Search([p(1800, 10, ladder_games=100)]) searches = [newbie_team, pro] matches = algorithm.RandomlyMatchNewbies().find(searches) assert len(matches) == 0
def test_odd_number_of_unmatched_newbies(p): newbie1 = Search([p(-250, 500, ladder_games=9)]) newbie2 = Search([p(750, 500, ladder_games=9)]) newbie3 = Search([p(1500, 500, ladder_games=9)]) pro = Search([p(1500, 10, ladder_games=100)]) searches = [newbie1, pro, newbie2, newbie3] matches = algorithm.RandomlyMatchNewbies().find(searches) assert len(matches) == 4
def test_make_matches_will_not_match_low_quality_games(p): s1 = Search([p(100, 64, name='p1')]) s2 = Search([p(2000, 64, name='p2')]) searches = [s1, s2] matches = algorithm.make_matches(searches) assert (s1, s2) not in matches assert (s2, s1) not in matches
def test_make_matches_will_not_match_low_quality_games(player_factory): s1 = Search([player_factory(100, 64, name="p1")]) s2 = Search([player_factory(2000, 64, name="p2")]) searches = [s1, s2] matches = algorithm.make_matches(searches) assert (s1, s2) not in matches assert (s2, s1) not in matches
def test_odd_number_of_unmatched_newbies(player_factory): newbie1 = Search([player_factory(-250, 500, ladder_games=9)]) newbie2 = Search([player_factory(750, 500, ladder_games=9)]) newbie3 = Search([player_factory(1500, 500, ladder_games=9)]) pro = Search([player_factory(1500, 10, ladder_games=100)]) pro.register_failed_matching_attempt() searches = [newbie1, pro, newbie2, newbie3] matches = algorithm.RandomlyMatchNewbies().find(searches) assert len(matches) == 4
def test_newbies_are_forcefully_matched_with_newbies(player_factory): newbie1 = Search([player_factory(0, 500, ladder_games=9)]) newbie2 = Search([player_factory(1500, 500, ladder_games=9)]) pro = Search([player_factory(1500, 10, ladder_games=100)]) pro.register_failed_matching_attempt() searches = [newbie1, pro, newbie2] matches = algorithm.RandomlyMatchNewbies().find(searches) assert matches[newbie1] == newbie2 assert matches[newbie2] == newbie1
def test_unmatched_newbies_forcefully_match_pros(player_factory): newbie = Search([player_factory(1500, 500, ladder_games=0)]) pro = Search([player_factory(1400, 10, ladder_games=100)]) searches = [newbie, pro] matches = algorithm.RandomlyMatchNewbies().find(searches) # No match if the pro is on their first attempt assert len(matches) == 0 pro.register_failed_matching_attempt() matches = algorithm.RandomlyMatchNewbies().find(searches) assert len(matches) == 2
def unmatched_newbie_teams_do_not_forcefully_match_pros(player_factory): newbie_team = Search([ player_factory(1500, 500, ladder_games=0), player_factory(1500, 500, ladder_games=0) ]) pro = Search([player_factory(1800, 10, ladder_games=100)]) pro.register_failed_matching_attempt() searches = [newbie_team, pro] matches = algorithm.RandomlyMatchNewbies().find(searches) assert len(matches) == 0
async def test_queue_cancel(matchmaker_queue, matchmaker_players): # Turn list of players into map from ids to players. s1, s2 = Search([matchmaker_players[1]]), Search([matchmaker_players[2]]) matchmaker_queue.push(s1) s1.cancel() try: await asyncio.wait_for(matchmaker_queue.search(s2), 0.01) except (TimeoutError, CancelledError): pass assert not s1.is_matched assert not s2.is_matched
def test_newbie_detection(matchmaker_players): pro, joe, _, _, _, newbie = matchmaker_players pro_search = Search([pro]) newbie_search = Search([newbie]) newb_team_search = Search([joe, newbie]) pro_team_search = Search([pro, joe]) assert pro_search.has_newbie() is False assert pro_search.is_newbie(pro) is False assert newbie_search.has_newbie() is True assert newbie_search.is_newbie(newbie) is True assert newb_team_search.has_newbie() is True assert pro_team_search.has_newbie() is False