Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
yusuffugurlu committed Apr 18, 2024
2 parents 7e239f3 + 4fc735c commit 660747e
Show file tree
Hide file tree
Showing 11 changed files with 341 additions and 34 deletions.
5 changes: 5 additions & 0 deletions API/Apps/Game/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ 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', [])

Expand Down
67 changes: 40 additions & 27 deletions API/Apps/Game/consumers.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,41 @@
import asyncio
import json
import threading
import time
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
import random
import threading
import time

from asgiref.sync import async_to_sync, sync_to_async
from channels.generic.websocket import AsyncWebsocketConsumer, WebsocketConsumer
from channels.db import database_sync_to_async
from Apps.Game.api.serializers import GameSerializer
from Apps.Game.cache import get_players_in_que, add_player_in_que, clear_players_in_que, add_player_in_game, \
get_players_in_game, clear_player_in_game
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
from Apps.Game.matchmaking import match


class MatchMakingConsumer(WebsocketConsumer):
def connect(self):
self.profile = ProfileGetSerializer(
Profile.objects.get(nickname=self.scope['url_route']['kwargs']['nickname'])).data
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()
self.accept()

def disconnect(self, close_code):
remove_player_in_que(self.profile)
self.channel_layer.group_discard(
'matchmaking',
self.channel_name,
)

def receive(self, text_data):
pass

def match_making_message(self, event):
self.send(text_data=json.dumps({
'message': event['message'],
Expand All @@ -47,21 +44,37 @@ 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:
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'])
while len(players) != 0:
ideal_mmr = 1
while True and len(players) > 1:
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': f'Match found! Ready for playing!',
'game': GameSerializer(game).data,
}))
async_to_sync(self.channel_layer.group_send)(
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": GameSerializer(game).data,
}
)
break
time.sleep(0.1)
ideal_mmr += 0.5 # up to value and time intervals can be added
players = sorted(get_players_in_que(), key=lambda x: x['mmr'])


class GameConsumer(AsyncWebsocketConsumer):
Expand Down
85 changes: 85 additions & 0 deletions API/Apps/Game/matchmaking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
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: # for testing
print(f"{player1['nickname']} ile {player2['nickname']} matched!")
remove_player_in_que(player1)
remove_player_in_que(player2)
return [player1, player2]
return []


18 changes: 18 additions & 0 deletions API/Apps/Profile/migrations/0020_profile_mmr.py
Original file line number Diff line number Diff line change
@@ -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),
),
]
16 changes: 14 additions & 2 deletions API/Apps/Profile/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,23 @@ class Profile(models.Model):
is_verified = models.BooleanField(default=False)
friends = models.ManyToManyField('Profile', blank=True, related_name='profile_friends')
bio = models.TextField(blank=True, null=True, default=None)
mmr = models.IntegerField(default=1000)
blocked_users = models.ManyToManyField('Profile', blank=True, related_name='users_blocked')


def __str__(self):
return f"{self.nickname if self.nickname else self.user.username}"
return self.nickname

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)
Expand Down
Empty file.
4 changes: 3 additions & 1 deletion Backend/static/scripts/requests.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function getSocket() {
if (socket === null || socket.readyState === WebSocket.CLOSED) {
const nickname = localStorage.getItem('activeUserNickname');
if (!nickname)
throw new Error('No active user nickname found');
return null;
socket = new WebSocket(`ws://localhost:8000/ws/requests/${nickname}`);
}
return socket;
Expand All @@ -62,6 +62,8 @@ function addSocketTestButton(){
}
async function App() {
let socket = getSocket();
if (!socket)
return;
socket.onopen = function (e) {
}
socket.onmessage = async function (e) {
Expand Down
26 changes: 24 additions & 2 deletions Backend/static/scripts/verification.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {request} from "./Request.js";

import {notify} from "../components/Notification.js";
let inputs = document.querySelectorAll('.row input[type="number"]');
document.getElementById('singleDigitInput1').addEventListener('paste', function() {
document.getElementById('singleDigitInput1').addEventListener('paste', function(event) {
let pastedData = (event.clipboardData || window.clipboardData).getData('text');
for (let i = 0; i < Math.min(pastedData.length, inputs.length); i++) {
inputs[i].value = pastedData[i];
Expand Down Expand Up @@ -50,7 +50,6 @@ async function postVerificationCode(value) {
}),
});

console.log(response.tokens)
setCookie('access_token', response.tokens.access, 1);
setCookie('refresh_token', response.tokens.refresh, 1);
loadPage('/home/')
Expand All @@ -60,3 +59,26 @@ async function postVerificationCode(value) {
console.log(e.json())
}
}

function startTimer()
{
let minutes = 14;
let seconds = 59;
let timer = setInterval(function() {
document.getElementById('timer').innerText = `${minutes}:${seconds}`;
if(minutes === 0 && seconds === 0)
{
clearInterval(timer);
document.getElementById('timer').innerText = '0:0';
}
if(seconds === 0 )
{
minutes--;
seconds = 59;
}
else
seconds--;

}, 1000)
}
startTimer()
74 changes: 74 additions & 0 deletions Backend/static/styles/leaderboard.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
.leaderboard-container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 1rem;
}
.leaderboard-data-container {
width: 80%;
height: 100%;
overflow: auto;
margin: auto;
}
.leaderboard-wrapper {
display: flex;
align-items: center;
flex-direction: column;
gap: 1rem;
}
.leaderboard-element {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: 0.5rem;
border-radius: 40px;
background-color: rgb(0, 0, 0, 0.7);
}
.leaderboard-wrapper .leaderboard-element:nth-child(1) {
background-color: #64535f67;
}
.leaderboard-wrapper .leaderboard-element:nth-child(2) {
background-color: #544d249a;
}
.leaderboard-wrapper .leaderboard-element:nth-child(3) {
background-color: #2d2c2aa2;
}
.leaderboard-img {
width: 50px;
height: 50px;
border-radius: 50%;
overflow: hidden;
}
.leaderboard-img img {
width: 100%;
height: 100%;
object-fit: cover;
}
.leaderboard-point {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
}
.leaderboard-order {
display: flex;
margin-right: 10px;
}

.leaderboard-profile {
width: 80%;
}
.leaderboard-header {
display: flex;
align-items: center;
justify-content: center;
}
.element-data {
display: flex;
align-items: center;
gap: 1rem;
}
4 changes: 2 additions & 2 deletions Backend/templates/auth/verification.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ <h2>E-mail Verification</h2>

<p>Please type verification code sent to your e-mail address</p>

<p>The verification code will expire in 15 minutes</p>

<p>The verification code will expire in</p>
<h1 id="timer">15:00</h1>
<div class="row">
<input type="number" id="singleDigitInput1">
<input type="number" id="singleDigitInput2">
Expand Down
Loading

0 comments on commit 660747e

Please sign in to comment.