Ranglistensortierung hat nen kleinen Fehler (ich hoffe "klein")

Marius Heil

Erfahrenes Mitglied
Hi,

ich hab für mein Onlinespiel, welches sich immer noch im Aufbau befindet eine kleine Funktion geschreiben die mir die Rangliste auf dem aktuellsten Stand halten soll.
In einer MySQL Tabelle habe ich eine Punkte sowie eine Rangspalte, meiner Funktion übergebe ich zum einen die UserID, zum anderen die Puntke die der Benutzer hinzubekommen soll.
Dies ist die Funktion:
PHP:
function order_rang($id, $punkte){
	$dbh = verbindung();
	//Zuerst die aktuellen Punkte und den aktuellen Rang auslesen
	$daten = einereihe($dbh, "SELECT rang, punkte FROM user WHERE ID=".$id." LIMIT 1");
	//Wenn der User bereits 1. ist
	if ($daten[0] == "1"){
		setanfrage($dbh, "UPDATE user SET Punkte=Punkte+".$punkte." WHERE ID=".$id." LIMIT 1");
		return;
	}
	
	$next = einereihe($dbh, "SELECT rang FROM user WHERE Punkte>=".($punkte+$daten[1])." ORDER BY Punkte ASC LIMIT 1");
	if ($next == ""){
		setanfrage($dbh, "UPDATE user SET Punkte=Punkte+".$punkte." WHERE ID=".$id." LIMIT 1");
		return;
	}
	setanfrage($dbh, "UPDATE user SET Rang=Rang+1 WHERE Rang>".$next[0]." AND Rang<".$daten[0]);
	setanfrage($dbh, "UPDATE user SET Punkte=Punkte+".$punkte.", Rang=".($next[0]+1)." WHERE ID=".$id." LIMIT 1");
}
Ich würde mich freuen wenn jemand den Fehler findet, sie sortiert nämlich von Zeit zu Zeit benutzer auf den gleichen Rang und überspringt Ränge.
Ich kann nur hoffen, dass ein Server PHP Abfragen nacheinander bearbeitet und nciht gleichzeitig, was dann passiert kann ich mir nciht vorstellen ;)


Marius
 
Hallo Marius,

sorry für die späte Antwort.
Ich habe mir die Sache mal genauer überlegt. Unklar ist, ob zwei Teilnehmer mit der gleichen Punktzahl den gleichen Rang in Deiner Ausgangstabelle haben. Ist das nicht so, dann scheint mir das Verfahren in den zitierten Zeilen problematisch zu sein:

PHP:
    $next = einereihe($dbh, "SELECT rang FROM user WHERE Punkte>=".($punkte+$daten[1])." ORDER BY Punkte ASC LIMIT 1");

Du ermittelst nur eine Reihe des besten, der mehr Punkte als der aktuelle Nutzer jetzt hat.

Im Nachgang wird aber genau hier eine Rangerhöhung all derer die einen Rang kleiner als der Maximalwert haben durchgeführt. Das könnte Nutzer betreffen, die die gleiche Punktzahl haben:
PHP:
    setanfrage($dbh, "UPDATE user SET Rang=Rang+1 WHERE Rang>".$next[0]." AND Rang<".$daten[0]);
Wie gesagt, nur wenn zwei Nutzer mit der gleichen Punktzahl nicht den gleichen Rang haben.

Des weiteren ist in der folgenden Zeile ein Fehler:
PHP:
    if ($next == ""){
        setanfrage($dbh, "UPDATE user SET Punkte=Punkte+".$punkte." WHERE ID=".$id." LIMIT 1");
        return;
    }

Du stellst fest, ob es überhaupt einen Nutzer gibt, der mehr Punkte hat als nach der Operation der aktuelle Nutzer. Ist das nicht der Fall, wird die Punktezahl verändert, aber nicht der Rang - es könnte aber sein, dass der aktuelle Nutzer erst durch die Punkteaddition Ranghöchster wird. Daher muss die setanfrage-Zeile lauten:
PHP:
    setanfrage($dbh, "UPDATE user SET Punkte=Punkte+".$punkte.", Rang=1 WHERE ID=".$id." LIMIT 1");

Ich hoffe das hilft Dir weiter. Das aufwändige Sortierverfahren sei Dir trotzdem nochmal ans Herz gelegt - spart Dir eine Menge Ärger mit Sonderfällen wie Punktgleichheit usw.

Gruß
Oliver
 
Hi,

vielen Dank, hab mich sehr über die Antwort gefreut, ich bin gerade dabei das ganze Mal Schritt für Schritt nachzuverfolgen.
Ranggleichheit soll keine bestehen, auch mit gleicher Punktezahl sollen die Benutzer aufeinanderfolgende Ränge bekommen. Sähe sonst seltsam aus, wenn alle mit 0 Punkten auf einem Rang sind und wäre auch technisch durch meien Rangliste nciht zu machen.
Ursprünglich war gedacht dies anhand eines weiteren Feldes zu ermitteln, das habe ich jedoch der Einfachheit halber erstmal weggelassen.
Ich werde den Beitrag Editieren wenn ich durch bin.

Marius
 
So,
es scheint nun zu funktionieren, hab den Fehler gefunden. Ich bin es gestern nochmal mit meinem Vater durch, der kann zwar nicht programmieren, wenn man es aber jemandem erklärt versteht man es selbst auch besser. Zuerst poste ich hier mal schnell, wie die Funktion derzeit aussieht:
PHP:
function order_rang($id, $punkte){
    $dbh = verbindung();
    //Zuerst die aktuellen Punkte und den aktuellen Rang auslesen
    $daten = einereihe($dbh, "SELECT rang, punkte FROM user WHERE ID=".$id." LIMIT 1");//Aktuellen Puntkestand auslesen und Rang auslesen
    //Wenn der User bereits 1. ist
    /*if ($daten[0] == "1"){
        setanfrage($dbh, "UPDATE user SET Punkte=Punkte+".$punkte." WHERE ID=".$id." LIMIT 1");
        return;
    }*/
    $next = einereihe($dbh, "SELECT rang FROM user WHERE Punkte>=".($punkte+$daten[1])." ORDER BY Punkte ASC LIMIT 1");//Benutzer auslesen, der gleichviel oder mehr Punkte als der neue Punktestand hat
    //Wenn der Benutzer von einem anderen Rang auf den 1. kommt
    if (!$next[0]){
        //setanfrage($dbh, "UPDATE user SET Punkte=Punkte+".$punkte." WHERE ID=".$id." LIMIT 1");
        //return;
        $next[0] = 0;
    }
    setanfrage($dbh, "UPDATE user SET Rang=Rang+1 WHERE Rang>".$next[0]." AND Rang<".$daten[0]);
    setanfrage($dbh, "UPDATE user SET Punkte=Punkte+".$punkte.", Rang=".($next[0]+1)." WHERE ID=".$id." LIMIT 1");
}
Wie man sieht hab ich die eine Passage ausgeklammert, da sie nicht lebensnotwendig ist, könnte man vielleicht einklammern, aber funktionieren tut es auch so.
Wo der Fehler schlussendlich gefunden werden konnte ist glaube ich diese Zeile hier:
PHP:
if (!$next[0]){
Wie man sieht habe ich das [0] vergessen gehabt, und da es sich bei $next um einen Array handelte, könnte es gut sein, dass das immer True ergeben hat. In wirklichkeit ist dies jedoch ein Sonderfall, wie man aus den Kommentaren sieht.
Gleichzeitig habe ich das was du auch entdeckt hattest ausgeklammert, da es unsinn war und einfach wenn kein Rang drüber gefunden wird den Rang auf 0 gesetzt, der benutzer landet dann auf Rang 0+1.
Ich geh nun mal davon aus, dass die Funktion einigermaßen recoursenschonend ist und bin in der Hoffnung, der Server führt die PHP Skripte nciht gleichzeitig parallel aus ;)
Bisher ist die Rangliste noch geordnet, wie lange noch wird sich zeigen.
Vielen Dank nochmal für deine Hilfe.

Marius
----
Man glaubt es nicht, sogar da war noch ein Fehelr drin^^
Wenn der Benutzer keinen Rang aufsteigt, gibts ein Problem, wenn er Punkte dazubekommt, dazu hab ich einfach noch ne PHP-Abfrage eingebaut und das geprüft, dann einfach die Punkte addiert.
Ich lass das Spiel nun noch bis morgen oder übermorgen laufen und schau, ob die Rangliste immer noch stimmt^^

Marius
----
Soo, was wäre schon programmieren, wenn es nicht immer wieder neue Fehler zu finden gäbe :D
Meine tolle funktion wird auch aufgerufen wenn ein User 0 Punkte dazubekommt, klingt bescheuert, ist aber so.
Aus diesem Grund erweitere man sie ganz oben noch mit :
PHP:
if ($punkte < 1) return;
fertig! (Zumindest hoffe ich das)
Ausfürhliche tests haben ergeben, dass die Rangliste zumindest nach 20fachem umsortieren unter allen möglichen Situationen noch instand ist, man darf gespannt sein.

Marius
 
Zuletzt bearbeitet:
Zurück