Nella lezione precedente abbiamo implementato il confronto tra i giocatori, arricchendo il gioco con funzionalità avanzate di statistiche e classifiche globali. In questa lezione, porteremo il nostro Solitario delle 5 Carte a un livello superiore introducendo la possibilità di organizzare e partecipare a tornei e di visualizzare classifiche temporanee. Queste funzionalità migliorano ulteriormente l'esperienza di gioco, creando contesti competitivi specifici e limitati nel tempo, ideali per eventi speciali.
Il nostro obbiettivo: organizzare tornei e creare classifiche temporanee
- Organizzazione di Tornei: creare una struttura di gioco che consenta ai giocatori di iscriversi e partecipare a tornei con durata limitata.
- Classifiche Temporanee: implementare una classifica che riflette i punteggi dei giocatori durante il torneo o per un periodo specifico.
- Premiazione dei Vincitori: implementare un sistema per la premiazione dei migliori giocatori al termine del torneo.
Modello di dati per i tornei
Per implementare i tornei, iniziamo con la definizione di un nuovo modello Tournament in MongoDB, che conterrà tutte le informazioni necessarie: nome del torneo, data di inizio e fine, e i partecipanti registrati.
// models/Tournament.js
const mongoose = require('mongoose');
const tournamentSchema = new mongoose.Schema({
name: { type: String, required: true },
startDate: { type: Date, required: true },
endDate: { type: Date, required: true },
participants: [
{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }
],
scores: [
{
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
points: Number
}
]
});
module.exports = mongoose.model('Tournament', tournamentSchema);
Creazione e gestione dei tornei
Implementiamo ora le rotte per la creazione e gestione dei tornei. Iniziamo con una rotta POST /api/tournaments per creare un nuovo torneo. Solo gli amministratori (o utenti con privilegi speciali) possono creare nuovi tornei.
Rotta per la creazione di tornei
Aggiungiamo la rotta per la creazione di un torneo nel nostro server:
// server.js
app.post('/api/tournaments', auth, async (req, res) => {
if (!req.user.isAdmin) {
return res.status(403).json({ error: 'Accesso non autorizzato' });
}
const { name, startDate, endDate } = req.body;
try {
const tournament = new Tournament({
name,
startDate,
endDate,
participants: [],
scores: []
});
await tournament.save();
res.status(201).json(tournament);
} catch (error) {
res.status(500).json({ error: 'Errore nella creazione del torneo' });
}
});
Iscrizione ai tornei
Aggiungiamo una rotta POST /api/tournaments/:id/join che consente ai giocatori di iscriversi a un torneo specifico, purché non sia già iniziato.
app.post('/api/tournaments/:id/join', auth, async (req, res) => {
try {
const tournament = await Tournament.findById(req.params.id);
if (new Date() > tournament.startDate) {
return res.status(400).json({ error: 'Il torneo è già iniziato' });
}
if (tournament.participants.includes(req.userId)) {
return res.status(400).json({ error: 'Già registrato al torneo' });
}
tournament.participants.push(req.userId);
await tournament.save();
res.status(200).json({ message: 'Iscrizione completata' });
} catch (error) {
res.status(500).json({ error: 'Errore durante l\'iscrizione' });
}
});
Classifiche temporanee per i tornei
Per visualizzare i punteggi dei giocatori durante i tornei, creiamo una rotta /api/tournaments/:id/leaderboard che restituisca la classifica dei partecipanti in base ai loro punteggi nel torneo specifico.
Rotta per la classifica del torneo
Implementiamo la logica per calcolare i punteggi dei partecipanti e restituire una classifica temporanea aggiornata:
app.get('/api/tournaments/:id/leaderboard', async (req, res) => {
try {
const tournament = await Tournament.findById(req.params.id)
.populate('scores.userId', 'username');
const leaderboard = tournament.scores
.sort((a, b) => b.points - a.points)
.map((entry) => ({
username: entry.userId.username,
points: entry.points
}));
res.status(200).json(leaderboard);
} catch (error) {
res.status(500).json({ error: 'Errore nel recupero della classifica del torneo' });
}
});
Premiazione dei vincitori al termine del torneo
Quando un torneo termina, possiamo premiare i vincitori. Implementiamo una funzione che identifica i migliori giocatori e li premia. Per semplicità, il nostro esempio mostrerà solo una notifica ai vincitori.
Identificazione e notifica dei vincitori
Aggiungiamo una funzione che verifica se il torneo è finito e notifica i vincitori:
const notifyWinners = async (tournamentId) => {
const tournament = await Tournament.findById(tournamentId)
.populate('scores.userId', 'email');
const maxScore = Math.max(
...tournament.scores.map(score => score.points)
);
const winners = tournament.scores
.filter(score => score.points === maxScore)
.map(winner => winner.userId.email);
winners.forEach((email) => {
console.log(`Email inviata a ${email} - Congratulazioni, sei tra i vincitori del torneo ${tournament.name}!`);
});
};
Questa funzione individua i giocatori con il punteggio più alto e invia una notifica (in questo caso, stampa un messaggio). In un'applicazione reale, l'invio di email potrebbe essere gestito con un servizio email come Nodemailer.
Integrazione frontend per tornei e classifiche temporanee
Ora aggiorniamo il frontend per visualizzare i tornei disponibili, classifiche temporanee e permettere la partecipazione ai tornei.
Visualizzazione dei tornei attivi
Creiamo un componente TournamentList per mostrare i tornei attivi e consentire agli utenti di iscriversi.
// components/TournamentList.js
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const TournamentList = () => {
const [tournaments, setTournaments] = useState([]);
useEffect(() => {
const fetchTournaments = async () => {
const response = await axios.get('http://localhost:5000/api/tournaments');
setTournaments(response.data);
};
fetchTournaments();
}, []);
const joinTournament = async (id) => {
const token = localStorage.getItem('token');
await axios.post(
`http://localhost:5000/api/tournaments/${id}/join`,
{},
{ headers: { Authorization: `Bearer ${token}` } }
);
alert('Iscrizione al torneo completata!');
};
return (
<div>
<h2>Tornei Attivi</h2>
<ul>
{tournaments.map((tournament) => (
<li key={tournament._id}>
<h3>{tournament.name}</h3>
<button onClick={() => joinTournament(tournament._id)}>
Iscriviti
</button>
</li>
))}
</ul>
</div>
);
};
export default TournamentList;
Visualizzazione della classifica temporanea del torneo
Creiamo il componente TournamentLeaderboard per visualizzare la classifica temporanea di un torneo:
// components/TournamentLeaderboard.js
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const TournamentLeaderboard = ({ tournamentId }) => {
const [leaderboard, setLeaderboard] = useState([]);
useEffect(() => {
const fetchLeaderboard = async () => {
const response = await axios.get(
`http://localhost:5000/api/tournaments/${tournamentId}/leaderboard`
);
setLeaderboard(response.data);
};
fetchLeaderboard();
}, [tournamentId]);
return (
<div>
<h2>Classifica del Torneo</h2>
<ul>
{leaderboard.map((entry, index) => (
<li key={index}>
{index + 1}. {entry.username} - {entry.points} punti
</li>
))}
</ul>
</div>
);
};
export default TournamentLeaderboard;
Conclusione
In questa lezione abbiamo implementato una struttura per la gestione dei tornei, incluse funzionalità di iscrizione, classifiche temporanee e notifiche per i vincitori. Queste nuove caratteristiche permettono di creare un'esperienza di gioco più avvincente e competitiva, in grado di attrarre un numero sempre maggiore di utenti.
Nella prossima lezione ci concentreremo sull'implementazione di notifiche in tempo reale per aggiornare immediatamente i punteggi nelle classifiche.
Se vuoi aggiornamenti su Organizzare tornei e classifiche temporanee inserisci la tua email nel box qui sotto: