first commit
This commit is contained in:
commit
c3223d211e
12 changed files with 570 additions and 0 deletions
16
.dockerignore
Normal file
16
.dockerignore
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
run.sh
|
||||
|
||||
venv/
|
||||
env/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
ranking.db
|
||||
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
|
||||
34
Dockerfile
Normal file
34
Dockerfile
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt .
|
||||
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
ENV FLASK_APP=app.py
|
||||
ENV FLASK_ENV=production
|
||||
ENV FLASK_RUN_HOST=0.0.0.0
|
||||
|
||||
# ---------------------------------------------
|
||||
# 9) Antes de arrancar, creamos la tabla si no existe
|
||||
# (llamamos a la función crear_tabla() definida en app.py).
|
||||
# Esto hará que, cuando se construya la imagen, se garantice
|
||||
# que la BD ya tiene la estructura mínima.
|
||||
# ---------------------------------------------
|
||||
RUN python - << 'EOF'
|
||||
from app import crear_tabla
|
||||
crear_tabla()
|
||||
EOF
|
||||
|
||||
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]
|
||||
|
||||
3
README.md
Normal file
3
README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# MULTI-GAME
|
||||
|
||||
Tienes 1 minuto para resolver todas las multiplicaciones de 2 cifras que seas capaz!
|
||||
149
app.py
Normal file
149
app.py
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
import sqlite3
|
||||
from flask import Flask, render_template, request, jsonify, g
|
||||
|
||||
DATABASE = "ranking.db"
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
def get_db():
|
||||
"""
|
||||
Abre una conexión a la base de datos SQLite y la asocia a 'g'
|
||||
para reutilizarla en cada petición.
|
||||
"""
|
||||
db = getattr(g, "_database", None)
|
||||
if db is None:
|
||||
db = g._database = sqlite3.connect(DATABASE)
|
||||
return db
|
||||
|
||||
|
||||
@app.teardown_appcontext
|
||||
def close_connection(exception):
|
||||
"""
|
||||
Al terminar el contexto de la petición, cierra la conexión si existe.
|
||||
"""
|
||||
db = getattr(g, "_database", None)
|
||||
if db is not None:
|
||||
db.close()
|
||||
|
||||
|
||||
def crear_tabla():
|
||||
"""
|
||||
Crea la tabla ranking si no existe.
|
||||
"""
|
||||
conn = sqlite3.connect(DATABASE)
|
||||
c = conn.cursor()
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS ranking (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
nombre TEXT NOT NULL,
|
||||
puntuacion INTEGER NOT NULL
|
||||
)
|
||||
""")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
def obtener_top10():
|
||||
"""
|
||||
Devuelve una lista de tuplas [(nombre, puntuacion), ...] con el top 10 ordenado descendente.
|
||||
"""
|
||||
conn = get_db()
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT nombre, puntuacion FROM ranking ORDER BY puntuacion DESC, id ASC LIMIT 10")
|
||||
resultados = c.fetchall()
|
||||
return resultados
|
||||
|
||||
|
||||
def puntuacion_entra_en_top(puntos):
|
||||
"""
|
||||
Comprueba si 'puntos' supera la última posición del top 10.
|
||||
Si hay menos de 10 filas, siempre devuelve True para guardar.
|
||||
"""
|
||||
top = obtener_top10()
|
||||
if len(top) < 10:
|
||||
return True
|
||||
# La décima posición es top[-1][1] (puntuacion más baja en top10)
|
||||
return puntos > top[-1][1]
|
||||
|
||||
|
||||
def guardar_puntuacion(nombre, puntos):
|
||||
"""
|
||||
Inserta la nueva puntuación en la tabla ranking.
|
||||
"""
|
||||
conn = get_db()
|
||||
c = conn.cursor()
|
||||
c.execute("INSERT INTO ranking (nombre, puntuacion) VALUES (?, ?)", (nombre, puntos))
|
||||
conn.commit()
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
"""
|
||||
Página principal con instrucciones y botón para iniciar el juego.
|
||||
"""
|
||||
return render_template("index.html")
|
||||
|
||||
|
||||
@app.route("/juego")
|
||||
def juego():
|
||||
"""
|
||||
Página donde se juega: se carga el HTML y el JS se encarga de todo lo demás.
|
||||
"""
|
||||
return render_template("game.html")
|
||||
|
||||
|
||||
@app.route("/ranking")
|
||||
def ranking():
|
||||
"""
|
||||
Página que muestra el top 10.
|
||||
"""
|
||||
top10 = obtener_top10()
|
||||
return render_template("ranking.html", top10=top10)
|
||||
|
||||
|
||||
@app.route("/api/score", methods=["POST"])
|
||||
def api_score():
|
||||
"""
|
||||
Recibe JSON: { "puntuacion": <int> }
|
||||
Comprueba si entra en top10 y devuelve JSON: { "entra_en_top": true/false, "top10": [...] }
|
||||
"""
|
||||
data = request.get_json()
|
||||
if not data or "puntuacion" not in data:
|
||||
return jsonify({"error": "No se recibió puntuación"}), 400
|
||||
|
||||
puntos = int(data["puntuacion"])
|
||||
entra = puntuacion_entra_en_top(puntos)
|
||||
top10 = obtener_top10()
|
||||
# Devolvemos el top10 actual (ANTES de guardar la nueva puntuación)
|
||||
return jsonify({
|
||||
"entra_en_top": entra,
|
||||
"top10": top10
|
||||
})
|
||||
|
||||
|
||||
@app.route("/api/score/save", methods=["POST"])
|
||||
def api_score_save():
|
||||
"""
|
||||
Recibe JSON: { "nombre": <string>, "puntuacion": <int> }
|
||||
Guarda en la BD y devuelve el ranking actualizado.
|
||||
"""
|
||||
data = request.get_json()
|
||||
if not data or "nombre" not in data or "puntuacion" not in data:
|
||||
return jsonify({"error": "Faltan parámetros"}), 400
|
||||
|
||||
nombre = data["nombre"].strip()
|
||||
puntos = int(data["puntuacion"])
|
||||
# Sólo guardamos si efectivamente entra en top10 (por seguridad)
|
||||
if puntuacion_entra_en_top(puntos):
|
||||
guardar_puntuacion(nombre, puntos)
|
||||
top10 = obtener_top10()
|
||||
return jsonify({
|
||||
"guardado": True,
|
||||
"top10": top10
|
||||
})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
crear_tabla()
|
||||
app.run(debug=True)
|
||||
|
||||
3
requirements.txt
Normal file
3
requirements.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Flask==2.3.2
|
||||
gunicorn==20.1.0
|
||||
|
||||
3
run.sh
Executable file
3
run.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
docker rm -f multiplicat-contenedor
|
||||
docker build -t multiplicat-app .
|
||||
docker run -d --name multiplicat-contenedor -p 5000:5000 multiplicat-app
|
||||
10
static/css/styles.css
Normal file
10
static/css/styles.css
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
body {
|
||||
background-color: #f8f9fa; /* gris claro */
|
||||
}
|
||||
.card {
|
||||
border-radius: 1rem;
|
||||
}
|
||||
#enunciado {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
186
static/js/game.js
Normal file
186
static/js/game.js
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
// static/js/game.js
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const TIEMPO_TOTAL = 60; // segundos
|
||||
let segundosRestantes = TIEMPO_TOTAL;
|
||||
let puntuacion = 0;
|
||||
|
||||
const barraTiempo = document.getElementById("barra-tiempo");
|
||||
const tiempoTexto = document.getElementById("tiempo-texto");
|
||||
const contadorAciertos = document.getElementById("contador-aciertos");
|
||||
const enunciado = document.getElementById("enunciado");
|
||||
const inputRespuesta = document.getElementById("input-respuesta");
|
||||
const feedback = document.getElementById("feedback");
|
||||
const botonFinal = document.getElementById("boton-final");
|
||||
const verRankingBtn = document.getElementById("ver-ranking");
|
||||
const jugarOtraBtn = document.getElementById("jugar-otra");
|
||||
|
||||
const formNombre = document.getElementById("form-nombre");
|
||||
const inputNombre = document.getElementById("input-nombre");
|
||||
const guardarNombreBtn = document.getElementById("guardar-nombre");
|
||||
const mensajeGuardar = document.getElementById("mensaje-guardar");
|
||||
|
||||
let numeroA = 0;
|
||||
let numeroB = 0;
|
||||
let temporizadorInterval = null;
|
||||
|
||||
// Genera un par aleatorio (dos números entre 10 y 99)
|
||||
function generarPregunta() {
|
||||
numeroA = Math.floor(Math.random() * 90) + 10; // 10…99
|
||||
numeroB = Math.floor(Math.random() * 90) + 10;
|
||||
enunciado.textContent = `¿Cuánto es ${numeroA} × ${numeroB}?`;
|
||||
inputRespuesta.value = "";
|
||||
inputRespuesta.focus();
|
||||
}
|
||||
|
||||
// Inicia el cronómetro y actualiza cada segundo
|
||||
function iniciarTemporizador() {
|
||||
barraTiempo.style.width = "100%";
|
||||
tiempoTexto.textContent = `${segundosRestantes} segundos restantes`;
|
||||
temporizadorInterval = setInterval(() => {
|
||||
segundosRestantes--;
|
||||
if (segundosRestantes < 0) {
|
||||
clearInterval(temporizadorInterval);
|
||||
terminarJuego();
|
||||
return;
|
||||
}
|
||||
// Actualiza texto y barra de progreso
|
||||
tiempoTexto.textContent = `${segundosRestantes} segundos restantes`;
|
||||
const porcentaje = (segundosRestantes / TIEMPO_TOTAL) * 100;
|
||||
barraTiempo.style.width = `${porcentaje}%`;
|
||||
|
||||
// Cambiar color de barra si queda poco tiempo
|
||||
if (segundosRestantes <= 10) {
|
||||
barraTiempo.classList.remove("bg-success");
|
||||
barraTiempo.classList.add("bg-danger");
|
||||
} else if (segundosRestantes <= 30) {
|
||||
barraTiempo.classList.remove("bg-success");
|
||||
barraTiempo.classList.add("bg-warning");
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Función que maneja el envío de la respuesta al presionar Enter
|
||||
inputRespuesta.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter" && segundosRestantes > 0) {
|
||||
e.preventDefault();
|
||||
validarRespuesta();
|
||||
}
|
||||
});
|
||||
|
||||
function validarRespuesta() {
|
||||
const valor = parseInt(inputRespuesta.value);
|
||||
const correcta = numeroA * numeroB;
|
||||
if (!isNaN(valor)) {
|
||||
if (valor === correcta) {
|
||||
puntuacion++;
|
||||
contadorAciertos.textContent = puntuacion;
|
||||
feedback.innerHTML = `<span class="text-success">Correcto!</span>`;
|
||||
} else {
|
||||
feedback.innerHTML = `<span class="text-danger">Incorrecto. Era ${correcta}.</span>`;
|
||||
}
|
||||
// Mostrar feedback por 800ms y luego limpiarlo
|
||||
setTimeout(() => {
|
||||
feedback.textContent = "";
|
||||
}, 800);
|
||||
} else {
|
||||
feedback.innerHTML = `<span class="text-warning">Ingresa un número válido.</span>`;
|
||||
setTimeout(() => {
|
||||
feedback.textContent = "";
|
||||
}, 800);
|
||||
}
|
||||
generarPregunta();
|
||||
}
|
||||
|
||||
function terminarJuego() {
|
||||
// Deshabilitar input
|
||||
inputRespuesta.disabled = true;
|
||||
feedback.innerHTML = `
|
||||
<div class="alert alert-info p-2">
|
||||
¡Tiempo terminado! Tu puntuación es: <strong>${puntuacion}</strong>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Enviar puntuación al servidor
|
||||
fetch('/api/score', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ puntuacion })
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.error) {
|
||||
feedback.innerHTML += `<div class="text-danger">Error: ${data.error}</div>`;
|
||||
botonFinal.classList.remove("d-none");
|
||||
return;
|
||||
}
|
||||
if (data.entra_en_top) {
|
||||
// Muestra formulario para nombre
|
||||
formNombre.classList.remove("d-none");
|
||||
} else {
|
||||
// No entra en top10: mostrar botón para ver ranking
|
||||
botonFinal.classList.remove("d-none");
|
||||
}
|
||||
// Guarda el top10 actual en data.top10 para mostrar más tarde si hace falta
|
||||
window.top10_actual = data.top10;
|
||||
})
|
||||
.catch(err => {
|
||||
feedback.innerHTML += `<div class="text-danger">Error conectando al servidor.</div>`;
|
||||
botonFinal.classList.remove("d-none");
|
||||
});
|
||||
}
|
||||
|
||||
// Botón “Ver Ranking” (si no entró en top 10)
|
||||
verRankingBtn.addEventListener("click", () => {
|
||||
// Redirigir a la página /ranking
|
||||
window.location.href = "/ranking";
|
||||
});
|
||||
|
||||
// Botón “Jugar otra vez”
|
||||
jugarOtraBtn.addEventListener("click", () => {
|
||||
window.location.href = "/juego";
|
||||
});
|
||||
|
||||
// Al hacer clic en “Guardar” nombre (si entró en top 10)
|
||||
guardarNombreBtn.addEventListener("click", () => {
|
||||
const nombre = inputNombre.value.trim();
|
||||
if (nombre.length === 0) {
|
||||
mensajeGuardar.innerHTML = `<small class="text-danger">El nombre no puede estar vacío.</small>`;
|
||||
return;
|
||||
}
|
||||
// Enviar nombre + puntuación al servidor
|
||||
fetch('/api/score/save', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ nombre, puntuacion })
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.error) {
|
||||
mensajeGuardar.innerHTML = `<small class="text-danger">Error: ${data.error}</small>`;
|
||||
return;
|
||||
}
|
||||
mensajeGuardar.innerHTML = `<div class="alert alert-success p-2">
|
||||
¡Puntuación guardada! Puedes ver el ranking actualizado.</div>`;
|
||||
// Opcional: mostrar tabla de top10 recibida en data.top10
|
||||
setTimeout(() => {
|
||||
window.location.href = "/ranking";
|
||||
}, 1500);
|
||||
})
|
||||
.catch(err => {
|
||||
mensajeGuardar.innerHTML = `<small class="text-danger">Error guardando el nombre.</small>`;
|
||||
});
|
||||
});
|
||||
|
||||
// Iniciar todo
|
||||
function iniciarJuego() {
|
||||
generarPregunta();
|
||||
iniciarTemporizador();
|
||||
inputRespuesta.disabled = false;
|
||||
inputRespuesta.focus();
|
||||
}
|
||||
|
||||
// Esperar 300ms para que el usuario vea la primera pregunta
|
||||
setTimeout(iniciarJuego, 300);
|
||||
});
|
||||
|
||||
52
templates/base.html
Normal file
52
templates/base.html
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
{% block head %}
|
||||
<title>{% block title %}Juego de Multiplicaciones{% endblock %}</title>
|
||||
<!-- Bootstrap 5 desde CDN -->
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
integrity="sha384-ENjdO4Dr2bkBIFxQpeo4Gaw416pGxYhbr2VdF+4E7RjzvRQZAZo+N91UZ7Jb8M+"
|
||||
crossorigin="anonymous">
|
||||
<!-- (Opcional) tu CSS personalizado -->
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<!-- Barra de navegación sencilla -->
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary mb-4">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="{{ url_for('index') }}">Multiplic-a-t</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('juego') }}">Jugar</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('ranking') }}">Top 10</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="container">
|
||||
{% block body %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<!-- Scripts de Bootstrap 5 (Popper + JS) -->
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-q2gy50CFdE7mU2MDoKvxo6oREjY4FKRDvKoiL+KD8l1zsR4OrFvCDZXGuXtn0pjg"
|
||||
crossorigin="anonymous"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
60
templates/game.html
Normal file
60
templates/game.html
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Jugar - Multiplic-a-t{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title mb-4 text-center">¡Pon a prueba tus multiplicaciones!</h3>
|
||||
|
||||
<!-- Temporizador: texto + barra de progreso -->
|
||||
<div class="mb-3">
|
||||
<span id="tiempo-texto" class="fs-5">60 segundos restantes</span>
|
||||
<div class="progress mt-1">
|
||||
<div id="barra-tiempo" class="progress-bar bg-success" role="progressbar"
|
||||
style="width: 100%" aria-valuenow="60" aria-valuemin="0" aria-valuemax="60">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contador de aciertos -->
|
||||
<div class="mb-3">
|
||||
<span class="fs-6">Aciertos: <span id="contador-aciertos">0</span></span>
|
||||
</div>
|
||||
|
||||
<!-- Enunciado de la multiplicación -->
|
||||
<div class="mb-3">
|
||||
<label for="input-respuesta" class="form-label fs-5" id="enunciado">¿Cuánto es 0 × 0?</label>
|
||||
<input type="number" id="input-respuesta" class="form-control" autocomplete="off" />
|
||||
</div>
|
||||
|
||||
<!-- Mensaje de feedback (correcto/incorrecto) -->
|
||||
<div id="feedback" class="mb-3" style="min-height: 1.5em;"></div>
|
||||
|
||||
<!-- Botón oculto inicialmente, para reiniciar o ver ranking -->
|
||||
<div id="boton-final" class="text-center mt-4 d-none">
|
||||
<button id="ver-ranking" class="btn btn-primary">Ver Ranking</button>
|
||||
<button id="jugar-otra" class="btn btn-secondary ms-2">Jugar otra vez</button>
|
||||
</div>
|
||||
|
||||
<!-- Formulario para que el usuario escriba nombre si entra en top 10 -->
|
||||
<div id="form-nombre" class="mt-4 d-none">
|
||||
<p class="mb-2">¡Felicidades! Entraste en el Top 10. Escribe tu nombre:</p>
|
||||
<div class="input-group">
|
||||
<input type="text" id="input-nombre" class="form-control" maxlength="20" />
|
||||
<button id="guardar-nombre" class="btn btn-success">Guardar</button>
|
||||
</div>
|
||||
<div id="mensaje-guardar" class="mt-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<!-- Importar el archivo JS que contiene la lógica del juego -->
|
||||
<script src="{{ url_for('static', filename='js/game.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
16
templates/index.html
Normal file
16
templates/index.html
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Inicio - Multiplic-a-t{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="text-center py-5">
|
||||
<h1 class="mb-3">Bienvenido a Multiplic-a-t</h1>
|
||||
<p class="lead">
|
||||
Tienes 1 minuto para resolver tantas multiplicaciones de dos cifras como puedas.
|
||||
Escribe la respuesta en el campo de texto y presiona Enter.
|
||||
Al terminar el minuto, podrás ver tu puntuación y, si entras en el top 10, registrar tu nombre.
|
||||
</p>
|
||||
<a href="{{ url_for('juego') }}" class="btn btn-lg btn-primary mt-4">Comenzar a jugar</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
38
templates/ranking.html
Normal file
38
templates/ranking.html
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Top 10 - Multiplic-a-t{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="text-center mb-4">
|
||||
<h2>Top 10 de Puntuaciones</h2>
|
||||
<a href="{{ url_for('juego') }}" class="btn btn-sm btn-primary mt-2">Jugar de nuevo</a>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
{% if top10 %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Nombre</th>
|
||||
<th>Puntuación</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{# Recorremos top10 sin enumerate; loop.index comienza en 1 #}
|
||||
{% for par in top10 %}
|
||||
<tr>
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>{{ par[0] }}</td> {# par[0] = nombre #}
|
||||
<td>{{ par[1] }}</td> {# par[1] = puntuación #}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="text-center">Aún no hay puntuaciones.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
Loading…
Reference in a new issue