JeuWeb - Crée ton jeu par navigateur
Classements des joueurs - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : Classements des joueurs (/showthread.php?tid=5087)

Pages : 1 2 3 4 5 6 7


RE: classements des joueurs - zeppelin - 19-08-2010

Hola ombre!

Bon, je pense que tu arriveras tout seul à créer un table qui contient les points des joueurs (que tu mets à jour à chaque action par exemple).

Dès que ceci est fait, jette un coup d'oeuil la dessus:

Code PHP :
<?php 
// uid = userid, points = points du joueur
function getRanking($uid, $points)
{
$request = 'SELECT uid FROM `ranglist` WHERE '. $points . ' >= ( SELECT '. $points .' FROM `ranglist` WHERE uid ='. $uid .' ) order by uid desc';
$query = mysql_query($request);
$count = mysql_num_rows($query);

return
$count;
}

Cette fonction te renvoie le classement d'un joueur précis en fournissant ses points comme argument.

Il y a à part contre un petit dédoublement d'information, car du moment que tu passe le uid comme argument tu peut chercher ses points directement dans la même requête. Mais bon, je te laisse optimiser dès que c'est fonctionel ;-)

Donc en gros: Tu récupère le classement du joueur en question, une fois que tu as cette information tu fais un select tout con genre:

Code PHP :
<?php 
$classement
['size'] = 20;
$classement['start'] = getRanking($uid, $points);
$request = 'select * from ranglist order by points desc limit '. $classement['start'] .', '. $classement['size'];

Et hop le tour est joué!

PS. Je n'ai pas testé le code, peut-être erreur de syntax méfie mais le principe devrait jouer!


RE: classements des joueurs - christouphe - 19-08-2010

J'avais fait un truc dans le style en mettant deux attributs supplémentaires à un membre/joueur (place_actuelle et place_precedente)

Code :
table membres (
id,
pseudo,
...,
place_actuelle int(5) default 10000,
place_precedente int(5) default 10000,
...,
PK(id)
)

Code :
table config (
-- des data
`flag_classement` enum('0','1','2') NOT NULL COMMENT 'flag de mise a jour classement',
-- d'autres data
)

Puis 3 fonctions me permettait de mettre à jour mon classement:

Code PHP :
<?php 
/**
*
* @param Ressource $_con - connexion à la base
* @param integer $_limit - pour le classement des $_limit meilleurs
* @return Ressource de type resultSet
*/
function donneClassement($_con,$_limit=false) {
$req = "SELECT id,pseudo,points,place_precedente,place_actuelle
FROM membres
ORDER BY points DESC"
;
if (
$_limit) {
$req .= " LIMIT 0,10";
}
//Ici lanceRequete() est une fonction qui fait
//le lancement de la requête vers le moteur SQL
$result = lanceRequete($req,$_con);
return
$result;
}

Code PHP :
<?php 
/**
*
* @param Ressource $_con - connexion à la base
* @param integer $_flag - état du flag pour savoir si on mets à jour
* @return boolean - true => OK, false => ARFF problème ;-)
*/
function majClassement ($_con,$_flag) {
$result = lanceRequete("SET AUTOCOMMIT = 0",$_con);
$result = donneClassement($_con);
$place = 1;
while (
$data = mysql_fetch_assoc($result)) {
$req = "UPDATE membres SET place_precedente = ".$data['place_actuelle'].",
place_actuelle = "
.$place." WHERE id = ".$data['id'];
$result2 = lanceRequete($req,$_con);
if (!
$result) {
rollback($_con);
//On renvoie FAUX => Affichage non modification.
return false;
}
$place += 1;
}

//MAJ du flag
$req = "UPDATE config SET flag_classement = '".$_flag."'";
$result2 = lanceRequete($req,$_con);
if (!
$result2) {
rollback($_con);
//On renvoie FAUX => Affichage non modification.
return false;
}
lanceRequete("COMMIT",$_con);
$result = lanceRequete("SET AUTOCOMMIT = 1",$_con);
return
true;
}

Code PHP :
<?php 
/**
*
* @param Ressource $_con - connexion à la base
* @return l'état du flag (0,1,2) ou FALSE
*/
function donneFlagClassement ($_con) {
$req = "SELECT flag_classement FROM config";
$result = lanceRequete($req,$_con);
if (
$result) {
$data = mysql_fetch_assoc($result);
return
$data['flag_classement'];
} else {
return
false;
}
}


Utilisation dans l'index

Code PHP :
<?php 
//...du code
$con = connecteBDD();

//...du code
$flagClassement = donneFlagClassement($con);
if ((
$flagClassement == '0') && ((date("H") >= "00") && (date("H") < "08"))) {
majClassement($con,'1');
} else if ((
$flagClassement == '1') && (date("H") == "08")) {
majClassement($con,'2');
} else if ((
$flagClassement == '2') && (date("H") == "16")) {
majClassement($con,'0');
}

Bon après mes test, ça fonctionnait bien chez moi, mais bon niveau optimisation, je pense pas que ce soit le top. Maintenant, c'est une bout de code fait sur un coin de table hein Wink


RE: classements des joueurs - Ter Rowan - 19-08-2010

attendez dans tous vos systèmes, vous lancez autant d'update qu'il y a d'enregistrement ?

N'y a t il pas moyen de faire tout cela directement par SQL avec une unique requete ?

Je vais me renseigner au bureau, y a forcément un truc plus optimisé (enfin j espère ^^)


RE: classements des joueurs - Jabberwock - 19-08-2010

J'ai peut être une solution plus courte mais je ne l'ai jamais testé avec plus de 1 page (la flemme de créer 100 comptes et +) :

Code PHP :
<?php 
if(empty($_POST['lim'])){
$mini = 0;
$maxi = $mini+99;
}
else{
$mini = $_POST['lim'];
$maxi = $mini+99;
}

Tout d'abord par le moyen d'un POST ou d'un GET j'obtiens la tranche de classement recherché, s'il n'y a rien j'affiche le classement de 1 à 100.

Code PHP :
<?php 
$qustion_profil
= mysql_query("SELECT id, pseudo, point, tag FROM general ORDER BY point DESC LIMIT ".$mini.", ".$maxi."");

Ensuite j'affiche le classement,

Code PHP :
<?php 
while($donnee = mysql_fetch_array($q_profil)){
$mini++;
// j'affiche la ligne du classement
}

Pour afficher le classement de ton joueur, sélectionne ces points et sélectionne les points du premier joueur (ou mais un chiffres qui sera toujours plus grands que le premier joueur) et fait une requête SQL COUNT(*) AS en utilisant BETWEEN (jamais testé cet idée... Normalement sa devrait fonctionner xD).

Si tu souhaite effectuer un classement qui peut varier tu met une variable dans la requête après le ORDER BY.

Et pour finir si tu souhaite afficher par défaut la page où le joueur se situe, tu t'arrange pour avoir le classement du joueur (ex : 456) tu arrondis à 500 tu enlève 1 pour le obtenir le maxi (499) et 100 pour le mini (400). Il faut que tu place ce code dans la balise :
Code PHP :
<?php 
if(empty($_POST['lim'])){
$mini = 0;
$maxi = $mini+99;
}

Voila ma solution est-ce que c'est rapide ? je ne sais pas ^^.
J'éspère que c'est propre au moins Confused.

EDIT : Voila en une page tu peux afficher différentes tranches de classement et différents type de classement, bien sûr il y a un formulaire à placer. Je ne montre pas le mien ayant mis cette page en pause je ne l'ais pas fini :mauvais:


RE: classements des joueurs - christouphe - 19-08-2010

@Ter: c'est pour ça que je le fait que 3 fois par jour Wink

Mais je n'ai pas trouvé d'autres solution, à part les Triggers


RE: classements des joueurs - zeppelin - 19-08-2010

Euh, un gros update != bonne idée.

Bon, je récapitule:

Tu as un table ranglist avec uid, points (& d'autres choses si tu veux ajouter... par exemple au lieu de points tu as offensivePoints, defensivePoints, generalPoints etc.).

Ensuite tu dis que tes joueurs peuvent faire des actions, ce qui leurs donnent des points. C'est donc tout simple: Chaque fois qu'un joueur effectue une action qui lui donne des points, ben tu lui ajoute ses points dans le classement (donc un seul update sur une seul ligne... à savoir le joueur en question).

Ensuite avec ma fonction écrite plus haut, tu récupère le classement du joueur d'après ses points. Dès que tu connais son classement, tu lance une requête toute simple order by points avec un limit sur son classement trouvé avec la fonction, et le tour est joué. Donc pas besoin de between et je ne sais trop quoi, mais juste une seule petite requête imbriqué (celle de ma fonction) Undecided

Bonne chance ;-)

ps. n'hésite pas à demander si tu as des problème à le mettre en place!


RE: classements des joueurs - Ter Rowan - 19-08-2010

je viens de comprendre, Zeppelin, ce que tu faisais ^^ :
en fait il n'y a pas de rang en bdd

et effectivement c'est pas mal du tout et devrait être assez rapide

le seul bemol que je mettrais concerne le côté "colonne" que tu reprends là où j'estime qu'on devrait être en ligne (pour pouvoir construire autant de classement que souhaité sans avoir à altérer la table, mais c'estpeut être moins performant)

maintenant à php_addict de voir si ça lui convient, je ne vais pas "pourrir" son poste plus que ça :p


RE: classements des joueurs - Sephi-Chan - 19-08-2010

Tout comme Zeppelin, la solution la plus simple à mes yeux — et au regard des questions que j'ai posé au début — était d'avoir une colonne points incrémentée au fil des actions. C'est rapide et simple à trier.

Si tu optes pour une solution plus compliquée (en terme de charge serveur et/ou d'algorithme), peu importe.

La seule chose vraiment important, c'est la façon dont tu gères le cache.

Pour ça tu dois décider si tu le fais du côté du modèle (tout ce qui produit et trie le tableau que tu vas afficher) ou de la vue (l'affichage de ton tableau sous forme de HTML). Chaque côté a ses avantages.

Voici un exemple (qui n'utilise rien d'avancé, tout le monde peut comprendre) :


# Côté contrôleur.
# Notons qu'ActiveRecord n'effectue la requête que quand on commence à itérer sur la collection.
# La requête sera effectuée au maximum une fois par demi-heure et par page.
@players = Player.order('points DESC').paginate(:page => params[:page], :per_page => 30)


# Côté vue (avec Haml).
- cache([ :rankings, params[:page] ], :expires_in => 30.minutes) do
%table
%tr
%th Nom
%th Points

- @players.each do |player|
%tr
%td= player.name
%td= player.points

Prenons un scénario simple :

  1. - Personne n'a encore consulté le classement depuis l'expiration des caches ;
    - À 13h30, un visiteur consulte la page 1 du classement. La page ne sera pas recalculée avant 14h00 ;
    - À 13h40, le 31ème joueur du classement (AAA) dépasse le 30ème (BBB, qui se retrouve 31ème). Le cache de la page 1 affiche donc une erreur : BBB n'est plus 30ème ;
    - À 13h50, un visiteur consulte la page 2 du classement : le joueur BBB est la premier de la page (puisqu'il est 31ème), mais il est toujours marqué comme était le 30ème d'après la page 1 qui n'expirera que dans 10 minutes ;

Si une telle erreur te gêne, il est alors plus pertinent de cacher la collection obtenue par le classement. Cela peut se faire comme ceci :



# Côté contrôleur.
# Ici, @players vaut le contenu du cache s'il existe, sinon le contenu du bloc est évalué pour remplir le cache.
@players = cache.fetch([ :rankings ], :expires_in => 30.minutes) do
Player.order('points DESC')
end
@players = @players.paginate(:page => params[:page], :per_page => 30)


# Côté vue (avec Haml).
%table
%tr
%th Nom
%th Points

- @players.each do |player|
%tr
%td= player.name
%td= player.points

Et là on a le résultat escompté et toujours exact, mais on "perd" le temps de génération du tableau, ce qui peut-être gênant quand la structure est conséquente (par exemple si on inclut dans le HTML plein de données à afficher au survol).

Il faut donc bien réfléchir à la façon dont tu gères ton cache. C'est bien plus important que de trouver une façon efficace de calculer ton classement puisqu'aussi optimisé soit ton algorithme, à partir du moment où il est appelé plusieurs fois (et ce sera forcément le cas), tu perdras un temps monstrueux à le recalculer par rapport à sa lecture depuis un cache.

Bien sûr, plus longtemps dure le cache, plus tu y gagnes, mais tu ne peux pas y perdre : même s'il expire chaque minute, il suffit que 2 personnes le consultent en 1 minute pour que tu le rentabilises, ça arrivera forcément, alors imagine avec une durée de vie de 30 minutes (par exemple).


Sephi-Chan


RE: classements des joueurs - php_addict - 19-08-2010

salut

merci pour tout vos conseils et merci A sephi Chan pour le cache.

voici donc comment je m'y suis pris:

1 ) je créé un array nommé $joueur avec les 3 scores des 3 types de classement (points attaque - points defenses - points tralala)
2 ) je trie (en php, pas avec sql) l'array $joueur selon points attaque
3 ) je rajoute une entrée dans $joueur 'classement_points_attaque' que j'incremente
4 ) je fais la même chose que 2) et 3) pour points defenses
5 ) je fais la même chose que 2) et 3) pour points tralala
6 ) j'écris le tout dans une table classement qui ressemble donc à:

id_joueur | points_attaque | points_defenses | points_tralala |classement_points attaque | classement_points_defenses | classement_points_tralala |

c'est l'écriture de la table classement qui prend du temps (accès disque dur...)

vitesse d'execution pour 10.000 entrées: 4.2319 secondes
soit 10.000 joueurs (avec 1 seul village par joueur)
en localhost avec wamp sans accelerateur php et sur mon pc.

je vais faire des tests avec de plus gros volumes, mais cela vous parait il coherent ?


RE: classements des joueurs - Sephi-Chan - 19-08-2010

Pense aussi que ta page sera surement paginée : tu prendras rarement plus de 100 résultats par page.


Sephi-Chan