New feature! Added Elo rating system!

This commit is contained in:
spla 2020-12-21 10:23:04 +01:00
parent ca9ec58dd3
commit 13ddb3b25b
7 changed files with 123 additions and 20 deletions

View file

@ -1,6 +1,7 @@
# Mastodon Chess
Play with other fediverse users a Chess game! Mastodon Chess control games, players and boards and even it post, graphically, every move to both players!
Mastodon Chess (mastochess) uses [python-chess](https://python-chess.readthedocs.io/en/latest/) library.
Mastodon Chess uses Elo rating system to calculate the relative skill levels of fediverse players!
### How to play:
@ -99,4 +100,5 @@ Within Python Virtual Environment:
04.12.2020 - New feature! Now players can claim a draw.
05.12.2020 - New feature! Add panel stats.
19.12.2020 - New feature! Now you can configure bot's language!
19.12.2020 - New feature! Added french language!
19.12.2020 - New feature! Added french language!
21.12.2020 - New feature! Added Elo rating system!

View file

@ -71,3 +71,4 @@ post_my_panel_str: panell (publica les estadístiques)
locale_change_successfully: llengua canviada amb èxit a
locale_not_changed: encara no és suportada :-(
change_lang_str: conf ca (per a configurar el bot en català)
panel_elo_rating_str: Elo

View file

@ -71,3 +71,4 @@ post_my_panel_str: panel (post player stats)
locale_change_successfully: language sucessfully changed to
locale_not_changed: is not supported yet :-(
change_lang_str: conf en (to configure the bot in english)
panel_elo_rating_str: Elo

View file

@ -71,3 +71,4 @@ post_my_panel_str: panel (publica el panel de datos)
locale_change_successfully: idioma cambiado con éxito a
locale_not_changed: no es soportado aún :-(
change_lang_str: conf es (para configurar el bot en castellano)
panel_elo_rating_str: Elo

View file

@ -71,3 +71,4 @@ post_my_panel_str: panneau (publie les statistiques)
locale_change_successfully: langue modifiée avec succès en
locale_not_changed: n'est pas encore pris en charge :-(
change_lang_str: conf fr (pour configurer le bot en français)
panel_elo_rating_str: Elo

View file

@ -169,7 +169,7 @@ if __name__ == '__main__':
create_table(db, db_user, table, sql)
table = "players"
sql = "create table "+table+" (player_id bigint PRIMARY KEY, player_name varchar(40), lang varchar(2))"
sql = "create table "+table+" (player_id bigint PRIMARY KEY, player_name varchar(40), lang varchar(2), elo_rating float)"
create_table(db, db_user, table, sql)
############################################################

View file

@ -20,7 +20,7 @@ import chess.svg
from cairosvg import svg2png
import chess.pgn
from PIL import Image, ImageFont, ImageDraw
import lichess.api
import math
def cleanhtml(raw_html):
cleanr = re.compile('<.*?>')
@ -31,7 +31,48 @@ def unescape(s):
s = s.replace("&apos;", "'")
return s
def create_panel(username, played_games, wins):
# Function to calculate the Probability
def Probability(rating1, rating2):
return 1.0 * 1.0 / (1 + 1.0 * math.pow(10, 1.0 * (rating1 - rating2) / 400))
# Function to calculate Elo rating
# K is a constant.
# d determines whether
# Player A wins or Player B.
def EloRating(Ra, Rb, K, d):
# To calculate the Winning
# Probability of Player B
Pb = Probability(Ra, Rb)
# To calculate the Winning
# Probability of Player A
Pa = Probability(Rb, Ra)
# Case -1 When Player A wins
# Updating the Elo Ratings
if (d == 1) :
Ra = Ra + K * (1 - Pa)
Rb = Rb + K * (0 - Pb)
# Case -2 When Player B wins
# Updating the Elo Ratings
else :
Ra = Ra + K * (0 - Pa)
Rb = Rb + K * (1 - Pb)
Ra = round(Ra, 6)
Rb = round(Rb, 6)
print("Updated Ratings:-")
print("Ra =", Ra," Rb =", Rb)
return(Ra, Rb)
# This code is contributed by
# Smitha Dinesh Semwal
def create_panel(username, rating, played_games, wins):
if played_games > 0 and wins > 0:
@ -77,10 +118,12 @@ def create_panel(username, played_games, wins):
panel_games_str = get_locale("panel_games_str", player_lang)
panel_wins_str = get_locale("panel_wins_str", player_lang)
panel_ratio_str = get_locale("panel_ratio_str", player_lang)
panel_elo_rating_str = get_locale("panel_elo_rating_str", player_lang)
draw.text((y+70,x+120), panel_games_str + ': ' + str(played_games), font=fnt, fill=(255,255,255,220)) #fill=(255,255,255,255)) ## full opacity
draw.text((y+70,x+170), panel_wins_str + ': ' + str(wins), font=fnt, fill=(255,255,255,220)) #fill=(255,255,255,255)) ## full opacity
draw.text((y+70,x+220), panel_ratio_str + ': ' + str(ratio) + '%', font=fnt, fill=(255,255,255,220))
draw.text((y+70,x+80), panel_games_str + ': ' + str(played_games), font=fnt, fill=(255,255,255,220)) #fill=(255,255,255,255)) ## full opacity
draw.text((y+70,x+130), panel_wins_str + ': ' + str(wins), font=fnt, fill=(255,255,255,220)) #fill=(255,255,255,255)) ## full opacity
draw.text((y+70,x+180), panel_ratio_str + ': ' + str(ratio) + '%', font=fnt, fill=(255,255,255,220))
draw.text((y+70,x+230), panel_elo_rating_str + ': ' + str(round(rating)), font=fnt, fill=(255,255,255,220))
fnt = ImageFont.truetype('app/fonts/DroidSans.ttf', 15, layout_engine=ImageFont.LAYOUT_BASIC)
@ -978,6 +1021,8 @@ def claim_draw(username):
def close_game(username, checkmate):
d = 0
try:
conn = None
@ -996,6 +1041,30 @@ def close_game(username, checkmate):
black_player = row[1]
cur.execute("select elo_rating from players where player_name = (%s)", (white_player,))
row = cur.fetchone()
if row[0] != None:
white_rating = row[0]
else:
white_rating = 1500
cur.execute("select elo_rating from players where player_name = (%s)", (black_player,))
row = cur.fetchone()
if row[0] != None:
black_rating = row[0]
else:
black_rating = 1500
cur.close()
except (Exception, psycopg2.DatabaseError) as error:
@ -1028,16 +1097,28 @@ def close_game(username, checkmate):
winner = username
if winner == white_player:
d = 1
else:
if query_word == search_end and username == white_user and stalemate == False:
winner = black_user
d = 2
elif query_word == search_end and username == black_user and stalemate == False:
winner = white_user
d = 1
K = 30
new_white_rating, new_black_rating = EloRating(white_rating, black_rating, K, d)
try:
conn = None
@ -1050,6 +1131,10 @@ def close_game(username, checkmate):
cur.execute("update stats set winner=(%s), finished=(%s), updated_at=(%s) where game_id=(%s)", (winner, finished, now, game_id))
cur.execute("update players set elo_rating=(%s) where player_name=(%s)", (new_white_rating, white_user))
cur.execute("update players set elo_rating=(%s) where player_name=(%s)", (new_black_rating, black_user))
conn.commit()
cur.close()
@ -1094,9 +1179,21 @@ def get_stats(player):
wins = row[0]
cur.execute("select elo_rating from players where player_name = (%s)", (player,))
row = cur.fetchone()
if row[0] != None:
rating = row[0]
else:
rating = 1500
cur.close()
return (played_games, wins)
return (rating, played_games, wins)
except (Exception, psycopg2.DatabaseError) as error:
@ -1745,9 +1842,9 @@ if __name__ == '__main__':
elif query_word == search_panel:
played_games, wins = get_stats(username)
rating, played_games, wins = get_stats(username)
create_panel(username, played_games, wins)
create_panel(username, rating, played_games, wins)
toot_text = '@'+username
@ -1969,11 +2066,11 @@ if __name__ == '__main__':
toot_text += '\n' + winned_games + "\n"
played_games, wins = get_stats(username)
rating, played_games, wins = get_stats(username)
wins_of_many = get_locale("wins_of_many", player_lang1)
toot_text += username + ': ' + str(wins) + ' ' + wins_of_many + ' ' + str(played_games) + "\n"
toot_text += username + ': ' + str(wins) + ' ' + wins_of_many + ' ' + str(played_games) + ' (Elo: ' + str(round(rating)) + ')' + '\n'
player_lang2 = get_lang(playing_user)
@ -1981,7 +2078,7 @@ if __name__ == '__main__':
toot_text += "\n@"+playing_user + ': ' + well_done + "\n"
played_games, wins = get_stats(playing_user)
rating, played_games, wins = get_stats(playing_user)
winned_games = get_locale("winned_games", player_lang2)
@ -1989,7 +2086,7 @@ if __name__ == '__main__':
toot_text += '\n\n' + winned_games + "\n"
toot_text += playing_user + ': ' + str(wins) + ' ' + wins_of_many + ' ' + str(played_games) + "\n"
toot_text += playing_user + ': ' + str(wins) + ' ' + wins_of_many + ' ' + str(played_games) + ' (Elo: ' + str(round(rating)) + ')' + '\n'
elif check == False and stalemate == True:
@ -1999,11 +2096,11 @@ if __name__ == '__main__':
toot_text += '\n' + winned_games + "\n"
played_games, wins = get_stats(username)
rating, played_games, wins = get_stats(username)
toot_text += username + ': ' + str(wins) + ' ' + wins_of_many + ' ' + str(played_games) + "\n"
played_games, wins = get_stats(playing_user)
rating, played_games, wins = get_stats(playing_user)
toot_text += playing_user + ': ' + str(wins) + ' ' + wins_of_many + ' ' + str(played_games) + "\n"
@ -2258,7 +2355,7 @@ if __name__ == '__main__':
toot_text += '\n' + winned_games + "\n"
played_games, wins = get_stats(white_player)
rating, played_games, wins = get_stats(white_player)
toot_text += white_player + ': ' + str(wins) + ' ' + wins_of_many + ' ' + str(played_games) + "\n"
@ -2274,7 +2371,7 @@ if __name__ == '__main__':
toot_text += '\n' + winned_games + "\n"
played_games, wins = get_stats(black_player)
rating, played_games, wins = get_stats(black_player)
toot_text += black_player + ': ' + str(wins) + ' ' + wins_of_many + ' ' + str(played_games) + "\n"
@ -2288,9 +2385,9 @@ if __name__ == '__main__':
elif query_word == search_panel:
played_games, wins = get_stats(username)
rating, played_games, wins = get_stats(username)
create_panel(username, played_games, wins)
create_panel(username, rating, played_games, wins)
toot_text = '@'+username