From 05d49bfac3f2d60eefdf72f77187e5ca3ed5cd45 Mon Sep 17 00:00:00 2001 From: yugurlu Date: Sun, 14 Apr 2024 12:28:14 +0300 Subject: [PATCH 1/7] test test test --- API/Apps/Game/match-making.py | 89 +++++++++++++++++++++++++++++++++++ API/Apps/Profile/models.py | 13 +++++ 2 files changed, 102 insertions(+) create mode 100644 API/Apps/Game/match-making.py diff --git a/API/Apps/Game/match-making.py b/API/Apps/Game/match-making.py new file mode 100644 index 00000000..2502be81 --- /dev/null +++ b/API/Apps/Game/match-making.py @@ -0,0 +1,89 @@ +import time +import timeit +from datetime import datetime + + +class Player: + def __init__(self): + self.mmr = 100 + + def win_games(self, opponent_mmr): + k_factor = 32 + expected_score = 1 / (1 + 10 ** ((opponent_mmr - self.mmr) / 400)) + self.mmr += k_factor * (1 - expected_score) + + def lose_games(self, opponent_mmr): + k_factor = 32 + expected_score = 1 / (1 + 10 ** ((opponent_mmr - self.mmr) / 400)) + self.mmr += k_factor * (0 - expected_score) + + +players = [ + {'nickname': 'yugurlu', 'win_rate': 60, 'total_game': 30, 'mmr': 876}, + {'nickname': 'ofirat', 'win_rate': 41, 'total_game': 23, 'mmr': 234}, + {'nickname': 'bert', 'win_rate': 18, 'total_game': 10, 'mmr': 1031}, + {'nickname': 'mkm_industries', 'win_rate': 31, 'total_game': 63, 'mmr': 453}, + {'nickname': 'kkanig', 'win_rate': 21, 'total_game': 13, 'mmr': 153}, +] + + +def win_games(self, opponent_mmr): + k_factor = 32 + expected_score = 1 / (1 + 10 ** ((opponent_mmr - self.mmr) / 400)) + self.mmr += k_factor * (1 - expected_score) + + +def lose_games(self, opponent_mmr): + k_factor = 32 + expected_score = 1 / (1 + 10 ** ((opponent_mmr - self.mmr) / 400)) + self.mmr += k_factor * (0 - expected_score) + + +player1 = Player() +player2 = Player() +player3 = Player() + +player1.win_games(player2.mmr) +player2.lose_games(player1.mmr) + +player3.win_games(2000) + +# print(player1.mmr) +# print(player2.mmr) +# print(player3.mmr) + + +players = sorted(players, key=lambda x: x['mmr']) + + +def find_small_diff(): + small_diff = players[0]['mmr'] + for i in range(len(players)): + if i + 1 < len(players) and (players[i + 1]['mmr'] - players[i]['mmr']) < small_diff: + small_diff = players[i + 1]['mmr'] - players[i]['mmr'] + matched_players = [players[i], players[i + 1]] + return matched_players + + +def match_making(ideal_mmr): + player1, player2 = find_small_diff() + if (player2['mmr'] - player1['mmr']) < ideal_mmr: + print(f"{player1['nickname']} ile {player2['nickname']} matched!") + players.remove(player1) + players.remove(player2) + return True + + + +start = timeit.default_timer() + +while len(players) != 0: + time.sleep(3) + ideal_mmr = 1 + while True and len(players) > 1: + if match_making(ideal_mmr): + break + ideal_mmr += 1 + +stop = timeit.default_timer() +print('Time: ', stop - start) \ No newline at end of file diff --git a/API/Apps/Profile/models.py b/API/Apps/Profile/models.py index b888a68e..55a50543 100644 --- a/API/Apps/Profile/models.py +++ b/API/Apps/Profile/models.py @@ -24,10 +24,23 @@ class Profile(models.Model): is_verified = models.BooleanField(default=False) friends = models.ManyToManyField('Profile', blank=True) bio = models.TextField(blank=True, null=True, default=None) + mmr = models.IntegerField(default=1000) def __str__(self): return f"{self.nickname if self.nickname else self.user.username}" + def win_games(self, opponent_mmr): + k_factor = 32 + expected_score = 1 / (1 + 10 ** ((opponent_mmr - self.mmr) / 400)) + self.mmr += k_factor * (1 - expected_score) + + def lose_games(self, opponent_mmr): + k_factor = 32 + expected_score = 1 / (1 + 10 ** ((opponent_mmr - self.mmr) / 400)) + self.mmr += k_factor * (0 - expected_score) + + + @receiver(m2m_changed, sender=Profile.friends.through) def update_friends(sender, instance, action, **kwargs): From ad344e71d8aafa560d9e33226636b54cdd5e0b1f Mon Sep 17 00:00:00 2001 From: yugurlu Date: Sun, 14 Apr 2024 23:08:44 +0300 Subject: [PATCH 2/7] if check_game close what happend? --- API/Apps/Game/cache.py | 5 ++ API/Apps/Game/consumers.py | 54 ++++++----- API/Apps/Game/match-making.py | 89 ------------------- API/Apps/Game/matchmaking.py | 89 +++++++++++++++++++ .../Profile/migrations/0020_profile_mmr.py | 18 ++++ 5 files changed, 145 insertions(+), 110 deletions(-) delete mode 100644 API/Apps/Game/match-making.py create mode 100644 API/Apps/Game/matchmaking.py create mode 100644 API/Apps/Profile/migrations/0020_profile_mmr.py diff --git a/API/Apps/Game/cache.py b/API/Apps/Game/cache.py index 5b3f4daf..45f5ab7b 100644 --- a/API/Apps/Game/cache.py +++ b/API/Apps/Game/cache.py @@ -15,5 +15,10 @@ def add_player_in_que(player): cache.set('players-in-que', json.dumps(all_keys)) +def remove_player_in_que(player): + all_keys = get_players_in_que() + all_keys.remove(player) + cache.set('players-in-que', json.dumps(all_keys)) + def clear_players_in_que(): cache.set('players-in-que', []) diff --git a/API/Apps/Game/consumers.py b/API/Apps/Game/consumers.py index f0c6eff1..0f7f1dff 100644 --- a/API/Apps/Game/consumers.py +++ b/API/Apps/Game/consumers.py @@ -1,15 +1,16 @@ +import asyncio import json import redis from asgiref.sync import async_to_sync from channels.generic.websocket import AsyncWebsocketConsumer, WebsocketConsumer from channels.layers import get_channel_layer - from Apps.Game.api.serializers import GameSerializer -from Apps.Game.cache import get_players_in_que, add_player_in_que, clear_players_in_que +from Apps.Game.cache import get_players_in_que, add_player_in_que, clear_players_in_que, remove_player_in_que from Apps.Game.models import Game from Apps.Profile.api.Serializers import ProfileGetSerializer from Apps.Profile.models import Profile +from Apps.Game.matchmaking import match class MatchMakingConsumer(WebsocketConsumer): @@ -17,16 +18,14 @@ def connect(self): self.accept() self.profile = ProfileGetSerializer(Profile.objects.get(nickname=self.scope['url_route']['kwargs']['nickname'])).data async_to_sync(self.channel_layer.group_add)( - 'matchmaking', + 'player-%s' %self.profile['nickname'], self.channel_name ) - self.send(text_data=json.dumps({ - 'message': 'Searching for a game...' - })) add_player_in_que(self.profile) self.check_game() def disconnect(self, close_code): + remove_player_in_que(self.profile) self.channel_layer.group_discard( 'matchmaking', self.channel_name, @@ -43,18 +42,31 @@ def match_making_message(self, event): def check_game(self): players_in_que = get_players_in_que() - if len(players_in_que) == 1: - self.send(text_data=json.dumps({ - 'message': 'Waiting for another player...' - })) - else: - player1, player2 = players_in_que - clear_players_in_que() - game = Game.objects.create(player1_id=player1['id'], player2_id=player2['id']) - async_to_sync(self.channel_layer.group_send)( - 'matchmaking', { - 'type': 'match_making_message', - 'message': 'Game found! You are now playing!', - 'game': GameSerializer(game).data, - } - ) + self.send(text_data=json.dumps({ + 'message': 'Searching for a game...' + })) + if len(players_in_que) == 2: + self.match_making() + + def match_making(self): + players = sorted(get_players_in_que(), key=lambda x: x['mmr']) + while len(players) != 0: + ideal_mmr = 1 + while True and len(players) > 1: + players = match(players, ideal_mmr) + if len(players) == 2: + Game.objects.create(player1=Profile.objects.get(id=players[0]['id']), player2=Profile.objects.get(id=players[1]['id'])) + self.send(text_data=json.dumps({ + 'message': 'Match found! Ready for playing!', + 'game': None, + })) + async_to_sync(self.channel_layer.group_send)( + f'player-{players[1]['nickname'] if self.profile['nickname'] == players[0]['nickname'] else players[0]['nickname']}', { + "type": "match_making_message", + "message": 'Match found! Ready for playing!', + "game": None, + } + ) + break + ideal_mmr += 0.00001 + players = sorted(get_players_in_que(), key=lambda x: x['mmr']) diff --git a/API/Apps/Game/match-making.py b/API/Apps/Game/match-making.py deleted file mode 100644 index 2502be81..00000000 --- a/API/Apps/Game/match-making.py +++ /dev/null @@ -1,89 +0,0 @@ -import time -import timeit -from datetime import datetime - - -class Player: - def __init__(self): - self.mmr = 100 - - def win_games(self, opponent_mmr): - k_factor = 32 - expected_score = 1 / (1 + 10 ** ((opponent_mmr - self.mmr) / 400)) - self.mmr += k_factor * (1 - expected_score) - - def lose_games(self, opponent_mmr): - k_factor = 32 - expected_score = 1 / (1 + 10 ** ((opponent_mmr - self.mmr) / 400)) - self.mmr += k_factor * (0 - expected_score) - - -players = [ - {'nickname': 'yugurlu', 'win_rate': 60, 'total_game': 30, 'mmr': 876}, - {'nickname': 'ofirat', 'win_rate': 41, 'total_game': 23, 'mmr': 234}, - {'nickname': 'bert', 'win_rate': 18, 'total_game': 10, 'mmr': 1031}, - {'nickname': 'mkm_industries', 'win_rate': 31, 'total_game': 63, 'mmr': 453}, - {'nickname': 'kkanig', 'win_rate': 21, 'total_game': 13, 'mmr': 153}, -] - - -def win_games(self, opponent_mmr): - k_factor = 32 - expected_score = 1 / (1 + 10 ** ((opponent_mmr - self.mmr) / 400)) - self.mmr += k_factor * (1 - expected_score) - - -def lose_games(self, opponent_mmr): - k_factor = 32 - expected_score = 1 / (1 + 10 ** ((opponent_mmr - self.mmr) / 400)) - self.mmr += k_factor * (0 - expected_score) - - -player1 = Player() -player2 = Player() -player3 = Player() - -player1.win_games(player2.mmr) -player2.lose_games(player1.mmr) - -player3.win_games(2000) - -# print(player1.mmr) -# print(player2.mmr) -# print(player3.mmr) - - -players = sorted(players, key=lambda x: x['mmr']) - - -def find_small_diff(): - small_diff = players[0]['mmr'] - for i in range(len(players)): - if i + 1 < len(players) and (players[i + 1]['mmr'] - players[i]['mmr']) < small_diff: - small_diff = players[i + 1]['mmr'] - players[i]['mmr'] - matched_players = [players[i], players[i + 1]] - return matched_players - - -def match_making(ideal_mmr): - player1, player2 = find_small_diff() - if (player2['mmr'] - player1['mmr']) < ideal_mmr: - print(f"{player1['nickname']} ile {player2['nickname']} matched!") - players.remove(player1) - players.remove(player2) - return True - - - -start = timeit.default_timer() - -while len(players) != 0: - time.sleep(3) - ideal_mmr = 1 - while True and len(players) > 1: - if match_making(ideal_mmr): - break - ideal_mmr += 1 - -stop = timeit.default_timer() -print('Time: ', stop - start) \ No newline at end of file diff --git a/API/Apps/Game/matchmaking.py b/API/Apps/Game/matchmaking.py new file mode 100644 index 00000000..c7e531ca --- /dev/null +++ b/API/Apps/Game/matchmaking.py @@ -0,0 +1,89 @@ +import time +import timeit +from datetime import datetime + +from Apps.Game.cache import get_players_in_que, remove_player_in_que + + +class Player: + def __init__(self): + self.mmr = 100 + + def win_games(self, opponent_mmr): + k_factor = 32 + expected_score = 1 / (1 + 10 ** ((opponent_mmr - self.mmr) / 400)) + self.mmr += k_factor * (1 - expected_score) + + def lose_games(self, opponent_mmr): + k_factor = 32 + expected_score = 1 / (1 + 10 ** ((opponent_mmr - self.mmr) / 400)) + self.mmr += k_factor * (0 - expected_score) + + +#players = [ +# {'nickname': 'yugurlu', 'win_rate': 60, 'total_game': 30, 'mmr': 876}, +# {'nickname': 'ofirat', 'win_rate': 41, 'total_game': 23, 'mmr': 234}, +# {'nickname': 'bert', 'win_rate': 18, 'total_game': 10, 'mmr': 1031}, +# {'nickname': 'mkm_industries', 'win_rate': 31, 'total_game': 63, 'mmr': 453}, +# {'nickname': 'kkanig', 'win_rate': 21, 'total_game': 13, 'mmr': 153}, +# {'nickname': 'jack', 'win_rate': 21, 'total_game': 13, 'mmr': 12}, +# {'nickname': 'egenc', 'win_rate': 21, 'total_game': 13, 'mmr': 353}, +# {'nickname': 'sylas', 'win_rate': 21, 'total_game': 13, 'mmr': 1189}, +# {'nickname': 'windows', 'win_rate': 21, 'total_game': 13, 'mmr': 53}, +# {'nickname': 'lol', 'win_rate': 21, 'total_game': 13, 'mmr': 465}, +# {'nickname': 'john', 'win_rate': 21, 'total_game': 13, 'mmr': 90}, +# {'nickname': 'wick', 'win_rate': 21, 'total_game': 13, 'mmr': 1}, +#] + + +def win_games(self, opponent_mmr): + k_factor = 32 + expected_score = 1 / (1 + 10 ** ((opponent_mmr - self.mmr) / 400)) + self.mmr += k_factor * (1 - expected_score) + + +def lose_games(self, opponent_mmr): + k_factor = 32 + expected_score = 1 / (1 + 10 ** ((opponent_mmr - self.mmr) / 400)) + self.mmr += k_factor * (0 - expected_score) + + +player1 = Player() +player2 = Player() +player3 = Player() + +player1.win_games(player2.mmr) +player2.lose_games(player1.mmr) + +player3.win_games(2000) + +# print(player1.mmr) +# print(player2.mmr) +# print(player3.mmr) + + + + +def find_small_diff(players): + small_diff = 0 + for i in range(len(players)): + if i + 1 < len(players) and ((players[i + 1]['mmr'] - players[i]['mmr']) < small_diff or i == 0): + small_diff = players[i + 1]['mmr'] - players[i]['mmr'] + matched_players = [players[i], players[i + 1]] + return matched_players + + +def match(players, ideal_mmr): + player1, player2 = find_small_diff(players) + if (player2['mmr'] - player1['mmr']) < ideal_mmr: + print(f"{player1['nickname']} ile {player2['nickname']} matched!") + remove_player_in_que(player1) + remove_player_in_que(player2) + return [player1, player2] + + + +start = time.time() +#match_making() +print('Time: ', time.time() - start) + diff --git a/API/Apps/Profile/migrations/0020_profile_mmr.py b/API/Apps/Profile/migrations/0020_profile_mmr.py new file mode 100644 index 00000000..17bed8b0 --- /dev/null +++ b/API/Apps/Profile/migrations/0020_profile_mmr.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.3 on 2024-04-14 16:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('Profile', '0019_stats_points'), + ] + + operations = [ + migrations.AddField( + model_name='profile', + name='mmr', + field=models.IntegerField(default=1000), + ), + ] From bd194c4f864350b63c34512de3e504676b49b611 Mon Sep 17 00:00:00 2001 From: yugurlu Date: Mon, 15 Apr 2024 08:12:32 +0300 Subject: [PATCH 3/7] adding threading fixed socket time out problem --- API/Apps/Game/consumers.py | 5 ++++- API/Apps/Game/matchmaking.py | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/API/Apps/Game/consumers.py b/API/Apps/Game/consumers.py index 0f7f1dff..b1c7415f 100644 --- a/API/Apps/Game/consumers.py +++ b/API/Apps/Game/consumers.py @@ -1,5 +1,7 @@ import asyncio import json +import threading +import time import redis from asgiref.sync import async_to_sync @@ -46,7 +48,7 @@ def check_game(self): 'message': 'Searching for a game...' })) if len(players_in_que) == 2: - self.match_making() + threading.Thread(target=self.match_making).start() def match_making(self): players = sorted(get_players_in_que(), key=lambda x: x['mmr']) @@ -70,3 +72,4 @@ def match_making(self): break ideal_mmr += 0.00001 players = sorted(get_players_in_que(), key=lambda x: x['mmr']) + print(len(players)) diff --git a/API/Apps/Game/matchmaking.py b/API/Apps/Game/matchmaking.py index c7e531ca..9f92ced1 100644 --- a/API/Apps/Game/matchmaking.py +++ b/API/Apps/Game/matchmaking.py @@ -75,11 +75,12 @@ def find_small_diff(players): def match(players, ideal_mmr): player1, player2 = find_small_diff(players) - if (player2['mmr'] - player1['mmr']) < ideal_mmr: + if (player2['mmr'] - player1['mmr']) < -1: print(f"{player1['nickname']} ile {player2['nickname']} matched!") remove_player_in_que(player1) remove_player_in_que(player2) return [player1, player2] + return [] From 392908e51d95dec76bd15c0997ca664e87dc6e2c Mon Sep 17 00:00:00 2001 From: yugurlu Date: Mon, 15 Apr 2024 08:33:49 +0300 Subject: [PATCH 4/7] refactor --- API/Apps/Game/consumers.py | 17 +++++------------ API/Apps/Game/matchmaking.py | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/API/Apps/Game/consumers.py b/API/Apps/Game/consumers.py index b1c7415f..dabbf0d2 100644 --- a/API/Apps/Game/consumers.py +++ b/API/Apps/Game/consumers.py @@ -2,13 +2,9 @@ import json import threading import time - -import redis from asgiref.sync import async_to_sync -from channels.generic.websocket import AsyncWebsocketConsumer, WebsocketConsumer -from channels.layers import get_channel_layer -from Apps.Game.api.serializers import GameSerializer -from Apps.Game.cache import get_players_in_que, add_player_in_que, clear_players_in_que, remove_player_in_que +from channels.generic.websocket import WebsocketConsumer +from Apps.Game.cache import get_players_in_que, add_player_in_que, remove_player_in_que from Apps.Game.models import Game from Apps.Profile.api.Serializers import ProfileGetSerializer from Apps.Profile.models import Profile @@ -33,9 +29,6 @@ def disconnect(self, close_code): self.channel_name, ) - def receive(self, text_data): - pass - def match_making_message(self, event): self.send(text_data=json.dumps({ 'message': event['message'], @@ -48,7 +41,7 @@ def check_game(self): 'message': 'Searching for a game...' })) if len(players_in_que) == 2: - threading.Thread(target=self.match_making).start() + threading.Thread(target=self.match_making).start() # opening thread because socket time out while true def match_making(self): players = sorted(get_players_in_que(), key=lambda x: x['mmr']) @@ -70,6 +63,6 @@ def match_making(self): } ) break - ideal_mmr += 0.00001 + ideal_mmr += 0.00001 # up to value and time intervals can be added players = sorted(get_players_in_que(), key=lambda x: x['mmr']) - print(len(players)) + print(len(players), players) diff --git a/API/Apps/Game/matchmaking.py b/API/Apps/Game/matchmaking.py index 9f92ced1..8f44eead 100644 --- a/API/Apps/Game/matchmaking.py +++ b/API/Apps/Game/matchmaking.py @@ -75,7 +75,7 @@ def find_small_diff(players): def match(players, ideal_mmr): player1, player2 = find_small_diff(players) - if (player2['mmr'] - player1['mmr']) < -1: + if (player2['mmr'] - player1['mmr']) < -1: # for testing print(f"{player1['nickname']} ile {player2['nickname']} matched!") remove_player_in_que(player1) remove_player_in_que(player2) From 54cf3bb62115b1e6a5b1d9e67c00f458ae21a728 Mon Sep 17 00:00:00 2001 From: yugurlu Date: Mon, 15 Apr 2024 23:09:01 +0300 Subject: [PATCH 5/7] mm error --- API/Apps/Game/consumers.py | 25 +++++++++++++++---------- API/Apps/Game/matchmaking.py | 7 +------ API/Apps/Game/templates/chat/index.html | 6 +++--- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/API/Apps/Game/consumers.py b/API/Apps/Game/consumers.py index dabbf0d2..b79b6c6b 100644 --- a/API/Apps/Game/consumers.py +++ b/API/Apps/Game/consumers.py @@ -4,6 +4,8 @@ import time from asgiref.sync import async_to_sync from channels.generic.websocket import WebsocketConsumer + +from Apps.Game.api.serializers import GameSerializer from Apps.Game.cache import get_players_in_que, add_player_in_que, remove_player_in_que from Apps.Game.models import Game from Apps.Profile.api.Serializers import ProfileGetSerializer @@ -46,23 +48,26 @@ def check_game(self): def match_making(self): players = sorted(get_players_in_que(), key=lambda x: x['mmr']) while len(players) != 0: + print(players) ideal_mmr = 1 while True and len(players) > 1: - players = match(players, ideal_mmr) - if len(players) == 2: - Game.objects.create(player1=Profile.objects.get(id=players[0]['id']), player2=Profile.objects.get(id=players[1]['id'])) + matched_players = match(players, ideal_mmr) + if len(matched_players) == 2: + game = Game.objects.create(player1=Profile.objects.get(id=matched_players[0]['id']), + player2=Profile.objects.get(id=matched_players[1]['id'])) self.send(text_data=json.dumps({ - 'message': 'Match found! Ready for playing!', - 'game': None, + 'message': f'Match found! Ready for playing!', + 'game': GameSerializer(game).data, })) async_to_sync(self.channel_layer.group_send)( - f'player-{players[1]['nickname'] if self.profile['nickname'] == players[0]['nickname'] else players[0]['nickname']}', { + f'player-{matched_players[1]['nickname'] if self.profile['nickname'] == matched_players[0]['nickname'] else matched_players[0]['nickname']}', + { "type": "match_making_message", "message": 'Match found! Ready for playing!', - "game": None, + "game": GameSerializer(game).data, } ) break - ideal_mmr += 0.00001 # up to value and time intervals can be added - players = sorted(get_players_in_que(), key=lambda x: x['mmr']) - print(len(players), players) + ideal_mmr += 0.0001 # up to value and time intervals can be added + players = sorted(get_players_in_que(), key=lambda x: x['mmr']) + print('finish') diff --git a/API/Apps/Game/matchmaking.py b/API/Apps/Game/matchmaking.py index 8f44eead..22400e68 100644 --- a/API/Apps/Game/matchmaking.py +++ b/API/Apps/Game/matchmaking.py @@ -75,7 +75,7 @@ def find_small_diff(players): def match(players, ideal_mmr): player1, player2 = find_small_diff(players) - if (player2['mmr'] - player1['mmr']) < -1: # for testing + if (player2['mmr'] - player1['mmr']) < ideal_mmr: # for testing print(f"{player1['nickname']} ile {player2['nickname']} matched!") remove_player_in_que(player1) remove_player_in_que(player2) @@ -83,8 +83,3 @@ def match(players, ideal_mmr): return [] - -start = time.time() -#match_making() -print('Time: ', time.time() - start) - diff --git a/API/Apps/Game/templates/chat/index.html b/API/Apps/Game/templates/chat/index.html index 5ab28f28..95b63424 100644 --- a/API/Apps/Game/templates/chat/index.html +++ b/API/Apps/Game/templates/chat/index.html @@ -11,18 +11,18 @@ {{ room_name|json_script:"room-name" }}