Implement rating leaderboard
This commit is contained in:
parent
3d720b5599
commit
65f174c95f
3 changed files with 75 additions and 118 deletions
|
@ -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():
|
||||
|
@ -63,12 +93,19 @@ def get_total_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
|
||||
# leaders=leaders,
|
||||
# leaderboards=leaderboards,
|
||||
# variants=variants,
|
||||
|
@ -76,9 +113,10 @@ def render_leaderboard():
|
|||
# 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()
|
||||
|
|
|
@ -1,45 +1,42 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block content %}
|
||||
<div class="tab-content" id="myTabContent">
|
||||
<div class="tab-pane fade show active" id="leaderboards">
|
||||
{% for rating_type, leaders in leaders.items() %}
|
||||
<div class="tab-pane fade{{' active show' if rating_type == 0 else ''}}" id="leaderboards-{{rating_type}}">
|
||||
<div class="container my-5">
|
||||
<!-- Header -->
|
||||
<!-- <h1 class="text-center mb-5">Hanabi Leaderboards</h1> -->
|
||||
<!-- Leader Cards -->
|
||||
<div class="card-deck mb-5">
|
||||
{% for category, data in leaders.items() %}
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-4">{{ data.title }}</h5>
|
||||
<p class="mb-0 player-name">{{ data.leader.player_name_og }}</p>
|
||||
<p class="mt-1 alt-name">{{ data.leader.player_name }}</p>
|
||||
<p class="score-large">{{ data.leader.score }}</p>
|
||||
<p class="mb-0 player-name">{{ data.player_name }}</p>
|
||||
<p class="mt-1 alt-name">{{ data.user_accounts }}</p>
|
||||
<p class="score-large">{{ data.score }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Leaderboards -->
|
||||
<div id="leaderboards" class="accordion">
|
||||
<div id="leaderboards-{{rating_type}}-data" class="accordion">
|
||||
{% for category, leaderboard in leaderboards.items() %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<button class="btn btn-link btn-block text-left" data-toggle="collapse" data-target="#{{ category|lower|replace(' ', '') }}">
|
||||
<button class="btn btn-link btn-block text-left" data-toggle="collapse" data-target="#{{ category|lower|replace(' ', '-') }}">
|
||||
{{ category }}
|
||||
</button>
|
||||
</div>
|
||||
<div id="{{ category|lower|replace(' ', '') }}" class="collapse" data-parent="#leaderboards">
|
||||
<div id="{{ category|lower|replace(' ', '-') }}" class="collapse" data-parent="#leaderboards-{{rating_type}}-data">
|
||||
<div class="card-body">
|
||||
<table class="table table-striped">
|
||||
{% for player in leaderboard %}
|
||||
{% for row in leaderboard[rating_type] %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="player-name">{{ player.player_name_og }}</div>
|
||||
<div class="alt-name">{{ player.player_name }}</div>
|
||||
<div class="player-name">{{ row['player_name'] }}</div>
|
||||
<div class="alt-name">{{ row['user_accounts'] }}</div>
|
||||
</td>
|
||||
<td class="score">{{ player.score }}</td>
|
||||
<td class="score">{{ row['current_rating'] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
@ -48,90 +45,9 @@
|
|||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="variants">
|
||||
<div class="container my-5">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="text-center">Variant</th>
|
||||
<th scope="col" class="text-center">Rating</th>
|
||||
<th scope="col" class="text-center">Games Played</th>
|
||||
<th scope="col" class="text-center">Max Scores</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for variant in variants %}
|
||||
<tr>
|
||||
<td class="text-center"><strong>{{ variant.variant_name }}</strong></td>
|
||||
<td class="text-center variant-rating">{{ variant.variant_rating|int }}</td>
|
||||
<td class="text-center">{{ variant.number_of_games_variant }}</td>
|
||||
<td class="text-center">{{ variant.number_of_max_scores_variant }}</td>
|
||||
</tr>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="tab-pane fade" id="info">
|
||||
<div class="container my-5">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="alert alert-info" role="alert">
|
||||
<h4 class="alert-heading">Welcome to the league!</h4>
|
||||
<p>The Test Season will be live from <strong>July 15th - August 31st!</strong></p>
|
||||
<hr>
|
||||
<p class="mb-0">Sign ups are now open <a href="https://docs.google.com/forms/d/e/1FAIpQLSfDDW0Iz4pNHaQjqBpCuRsARJ1PE1kO85ndrvKte3irjCJhvw/viewform" class="alert-link">HERE</a>!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>About</h3>
|
||||
<p>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!</p>
|
||||
|
||||
<h3>How to Play</h3>
|
||||
<p>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:</p>
|
||||
<ul>
|
||||
<li>Everyone joins <strong>individually</strong>, and can play with any mix of other participants.</li>
|
||||
<li>League games must be played with a <strong>registered alt account</strong>.</li>
|
||||
<li>ALL games played from this new account will be tracked each Season, so don’t forget to switch back when playing non-League games!</li>
|
||||
</ul>
|
||||
<p>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.</p>
|
||||
|
||||
<h3>Game Rules</h3>
|
||||
<ul>
|
||||
<li><strong>3-5 player games</strong> only</li>
|
||||
<li><strong>5-6 suit games</strong> only, and only of the following variants (110 total!):
|
||||
<ul>
|
||||
<li>No Variant, the 9 non-dark ‘special suits’, and all combinations</li>
|
||||
<li>All Clue Starved combinations</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Time or Untimed, but no alternate game modes or special rules (Bottom-Deck Blindplays, Empty Clues, Detrimental Chars, etc.)</li>
|
||||
</ul>
|
||||
<p>Only games with which follow these game rules and which include exclusively registered players will be tracked!</p>
|
||||
|
||||
<h3>Introducing the Hanabi League Rating system!</h3>
|
||||
<p>We’re excited to announce the new, experimental <strong>Hanabi League Rating</strong> (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.</p>
|
||||
|
||||
<p>These estimates are based on the teammates’ ratings, versus the chosen variant’s estimated difficulty (variants have ratings too!).</p>
|
||||
<ul>
|
||||
<li>The system counts <strong>Max Scores</strong> as <em>Wins</em>; any other result is simply a <em>Loss</em>.</li>
|
||||
<li>For the veteran glory-seekers, there will also be <strong>Leaderboards</strong> tracking a few popular stats. For now, these include <em><strong>Rating</strong></em>, <em><strong>Longest Win Streak</strong></em>, and <em><strong>Scores Hunted</strong></em>.</li>
|
||||
<li>A few other fun stats will be tracked as well, just for curiosity’s sake.</li>
|
||||
<li>All the player ratings, leaderboards, and variant stats will be tracked & updated automatically on this website.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -17,7 +17,10 @@
|
|||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" id="leaderboards-tab" data-toggle="tab" href="#leaderboards">Leaderboards</a>
|
||||
<a class="nav-link active" id="leaderboards-0-tab" data-toggle="tab" href="#leaderboards-0">No Variant</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="leaderboards-1-tab" data-toggle="tab" href="#leaderboards-1">Clue Starved</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="variant-tab" data-toggle="tab" href="#variants">Variants</a>
|
||||
|
|
Loading…
Reference in a new issue