From 65f174c95f6fd801ad659f4730425bebc73b795c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Ke=C3=9Fler?= Date: Mon, 4 Dec 2023 00:40:29 +0100 Subject: [PATCH] Implement rating leaderboard --- src/render_site.py | 76 +++++++++++++++++++++------- templates/content.html | 112 ++++++----------------------------------- templates/layout.html | 5 +- 3 files changed, 75 insertions(+), 118 deletions(-) diff --git a/src/render_site.py b/src/render_site.py index db3030c..b7ae60a 100644 --- a/src/render_site.py +++ b/src/render_site.py @@ -1,14 +1,33 @@ +from pathlib import Path +from typing import Dict + import jinja2 import psycopg2.extras import constants import utils import ratings +from dataclasses import dataclass from database import conn_manager -def get_rating_leaderboards(): +@dataclass +class Leader: + title: str + player_name: str + user_accounts: str + score: int + + +@dataclass +class PlayerEntry: + player_name: str + user_accounts: str + score: int + + +def get_rating_lists(): cur = conn_manager.get_connection().cursor(cursor_factory=psycopg2.extras.DictCursor) cur.execute( "SELECT * FROM (" @@ -28,15 +47,26 @@ def get_rating_leaderboards(): "ORDER BY type ASC, current_rating DESC", (", ",) ) - novar_leaderboard = [] - clue_starved_leaderboard = [] + leaderboard = { + utils.get_rating_type(x): [] + for x in [True, False] + } for row in cur.fetchall(): - if row['type'] == utils.get_rating_type(False): - novar_leaderboard.append(row) - else: - clue_starved_leaderboard.append(row) + rating_type = row['type'] + leaderboard[rating_type].append(row) - return novar_leaderboard, clue_starved_leaderboard + return leaderboard + + +def get_leaders(rating_lists: Dict): + leaders = {} + for rating_type, rating_list in rating_lists.items(): + if len(rating_list) != 0: + leader = rating_list[0] + leaders[rating_type] = { + 'Player Rating': Leader('Highest Rating', leader['player_name'], leader['user_accounts'], round(leader['current_rating'])) + } + return leaders def get_streak_leaderboards(): @@ -58,27 +88,35 @@ def get_streak_leaderboards(): def get_total_games(): cur = conn_manager.get_new_cursor() cur.execute("SELECT MAX(league_id) FROM games") - (num_games, ) = cur.fetchone() + (num_games,) = cur.fetchone() return num_games def render_leaderboard(): - novar_leaderboard, clue_starved_leaderboard = get_rating_leaderboards() + rating_lists = get_rating_lists() + leaders = get_leaders(rating_lists) + + leaderboards = { + 'Player Rating': rating_lists + } env = jinja2.Environment(loader=jinja2.FileSystemLoader('templates')) template = env.get_template('content.html') # rendered_html = template.render(leaders=leaders, leaderboards=leaderboards, variants=variants) rendered_html = template.render( -# leaders=leaders, -# leaderboards=leaderboards, -# variants=variants, -# total_games_played=constants['total_games_played'], -# latest_run=latest_run_utc_formatted, -# total_players=total_players + leaders=leaders, + leaderboards=leaderboards + # leaders=leaders, + # leaderboards=leaderboards, + # variants=variants, + # total_games_played=constants['total_games_played'], + # latest_run=latest_run_utc_formatted, + # total_players=total_players ) - with open(constants.WEBSITE_OUTPUT_DIRECTORY / 'index.html') as f: + output_file = Path(constants.WEBSITE_OUTPUT_DIRECTORY) / 'index.html' + output_file.parent.mkdir(exist_ok=True, parents=True) + with open(output_file, 'w') as f: f.write(rendered_html) -get_leaderboards() -#render_leaderboard() +render_leaderboard() diff --git a/templates/content.html b/templates/content.html index 742a234..746eb0f 100644 --- a/templates/content.html +++ b/templates/content.html @@ -1,45 +1,42 @@ {% extends "layout.html" %} {% block content %}
-
+ {% for rating_type, leaders in leaders.items() %} +
- -
{% for category, data in leaders.items() %}
{{ data.title }}
-

{{ data.leader.player_name_og }}

-

{{ data.leader.player_name }}

-

{{ data.leader.score }}

+

{{ data.player_name }}

+

{{ data.user_accounts }}

+

{{ data.score }}

{% endfor %}
- - -
+
{% for category, leaderboard in leaderboards.items() %}
-
-
+
- {% for player in leaderboard %} + {% for row in leaderboard[rating_type] %} - + {% endfor %}
-
{{ player.player_name_og }}
-
{{ player.player_name }}
+
{{ row['player_name'] }}
+
{{ row['user_accounts'] }}
{{ player.score }}{{ row['current_rating'] }}
@@ -48,90 +45,9 @@
{% endfor %}
+
- -
-
- - - - - - - - - - - {% for variant in variants %} - - - - - - - {% endfor %} - -
VariantRatingGames PlayedMax Scores
{{ variant.variant_name }}{{ variant.variant_rating|int }}{{ variant.number_of_games_variant }}{{ variant.number_of_max_scores_variant }}
-
-
- - - - - - -
-
-
-
-
- -
-
-
- -

About

-

The Hanabi Pro Hunting League creates a new way for folks to play more "serious" Hanabi, but without the stress, formality, and scheduling headaches of normal tournaments. For folks of all skill levels, the most important goal is simply to compete against yourself and to constantly learn and improve!

- -

How to Play

-

Everyone is welcome to participate! To sign up, just click the link above to fill out the short Google Form. Just remember the following important info:

-
    -
  • Everyone joins individually, and can play with any mix of other participants.
  • -
  • League games must be played with a registered alt account.
  • -
  • ALL games played from this new account will be tracked each Season, so don’t forget to switch back when playing non-League games!
  • -
-

All game data will be automatically tracked & recorded, so all you need to do is play! Make sure to check out this page for important game & League info.

- -

Game Rules

-
    -
  • 3-5 player games only
  • -
  • 5-6 suit games only, and only of the following variants (110 total!): -
      -
    • No Variant, the 9 non-dark ‘special suits’, and all combinations
    • -
    • All Clue Starved combinations
    • -
    -
  • -
  • Time or Untimed, but no alternate game modes or special rules (Bottom-Deck Blindplays, Empty Clues, Detrimental Chars, etc.)
  • -
-

Only games with which follow these game rules and which include exclusively registered players will be tracked!

- -

Introducing the Hanabi League Rating system!

-

We’re excited to announce the new, experimental Hanabi League Rating (HLR) system! Adapted from Elo systems, the HLR basically works by estimating the expected outcome of each game. Each player will then gain or lose rating points based on how (un)likely the result was calculated to be.

- -

These estimates are based on the teammates’ ratings, versus the chosen variant’s estimated difficulty (variants have ratings too!).

-
    -
  • The system counts Max Scores as Wins; any other result is simply a Loss.
  • -
  • For the veteran glory-seekers, there will also be Leaderboards tracking a few popular stats. For now, these include Rating, Longest Win Streak, and Scores Hunted.
  • -
  • A few other fun stats will be tracked as well, just for curiosity’s sake.
  • -
  • All the player ratings, leaderboards, and variant stats will be tracked & updated automatically on this website.
  • -
-
-
+ {% endfor %}
{% endblock %} diff --git a/templates/layout.html b/templates/layout.html index 9a6d527..c261608 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -17,7 +17,10 @@