ZZZ bei Crash
Inhaltsverzeichnis
- 1 Vorwort
- 2 Erläuterung des Crash-Verhaltens
- 3 Bisschen OpenSource
- 4 Kommentare & Korrekturvorschläge
- 5 Kurz-Tips
Vorwort
Da ich nun schon mehrfach gebeten wurde das ZZZ-Verhalten bei Crash zu erklären, nehme ich mir mal die Freiheit an dieser Stelle den ZZZ-Bug zu erläutern. Ich hoffe ich kriege das dabei so hin, dass es auch für Nicht-Informatiker verständlich ist! Den Original-Code von Didi gibt es zum selbst Nachvollziehen natürlich weiterhin unten im Anschluss zusammen mit der Diskussion und den Korrekturvorschlägen... --Ultimate 17:09, 11. Jul. 2014 (CEST)
Erläuterung des Crash-Verhaltens
Einleitung
Um die Erklärung verständlicher zu machen, werde ich sie an einem Beispiel machen. Dazu habe ich ein paar Bilder aufbereitet und werde das Vorgehen Schritt für Schritt erläutern. Ich werde dabei zwei Szenarien durchspielen, die beide gleich anfangen und dann aber unterschiedlich fortgeführt werden...
Der 1. Crash
Schritt 0 - Beispielkarte
Ich nehme für meine Erklärung einfach mal "Die Erste" Karte. Aus Platzgründen habe ich sie aber mal ein bisschen zurechtgestutzt... Für das Beispiel nehme ich mal ZZZ=4, da sich das Verhalten besser bei etwas größerem ZZZ erläutern lässt.
Schritt 1 - Gegen die Wand fahren
Möge das Rennen beginnen... Wir fahren nun einfach mal mit Vollgas gegen die Wand:
Schritt 2 - Der Algorithmus zählt rückwärts
In dem Moment indem keine Züge mehr möglich sind und klar ist, dass man crasht, wird die bisherige Route zurückverfolgt um den Zurücksetzpunkt zu finden. Dazu beginnt der Zähler (im Code $i) beim ZZZ (im Code $aussetzen), also in unserem Fall bei 4. Der Zähler zählt bis 0 rückwärst und schaut dabei für jeden Zug aus der Datenbank, ob das ein Crash war.
Hä was wie? - Ja, Crashs werden genauso wie Züge in der Datenbank abgelegt und unterscheiden sich nur durch einen zusätzlichen Eintrag. Wenn Didi also die Liste der Züge aus der Datenbank holt, sind dort auch die Crashs mittenmang. Aktuell sind wir aber noch nicht gecrasht, daher ist noch alles gut.
Schritt 3 - Crashpunkt gefunden
Wenn wir bis auf 0 runtergezählt haben, nehmen wir von dem soeben betrachteten Zug das Zielfeld als den Punkt, wohin wir zurück gesetzt werden...
Bis hierhin sollte alles klar sein, oder?
Der 2. Crash - Szenario 1 - Crash nach weniger als ZZZ Zügen
Schritt 4 - Erneut crashen, nach 2 Zügen
Wir fahren nun ganz einfach so schnell wie möglich gegen die Wand. Das schaffen wir nach 2 Zügen, was in userem Beispiel weniger ist als der ZZZ von 4 - und darauf kommt es in diesem Szenario an...
Schritt 5 - Der Algorithmus zählt rückwärts
Hier wird es jetzt spannend!
Wie auch beim ersten Crash zählt der Algorithmus nun von ZZZ=4 runter bis 0. Allerdings findet er diesmal während des Rückwärtsgehenseinen Crash (die rote Linie). An dieser Stelle wird der Zähler ($i) um den ZZZ ($aussetzen) erhöht, so dass wir nun weiter zurückgehen müssen als ursprünglich angenommen, so lange bis wir wirklich bei 0 sind.
Schritt 6 - Crashpunkt gefunden
Wenn wir nun wieder bis auf 0 runtergezählt haben, dann nehmen wir von diesem Zug wieder das Zielfeld als den Punkt, wohin wir zurück gesetzt werden...
Das Ergebnis nehmen wir jetzt erstmal so hin, richtig merkwürdig wird es erst im folgenden Szenario... Ich hoffe aber ihr konntet mir bis hierher folgen!
Der 2. Crash - Szenario 2 - Crash nach genau als ZZZ Zügen
Schritt 4 - Erneut crashen, nach ZZZ Zügen
Diesmal fahren wir nicht auf dem kürzesten Weg gegen die Wand, sondern nach genau ZZZ Zügen - in unserem Fall also 4.
Schritt 5 - Der Algorithmus zählt rückwärts
Hier wird es jetzt erneut spannend!
Wie auch beim ersten Crash und im ersten Szenario zählt der Algorithmus nun wieder von ZZZ=4 runter bis 0. Allerdings findet er diesmal genau in dem Moment wo er bei 0 angekommen ist einen Crash (die rote Linie). Und anstatt hier nun abzubrechen wird der Zähler ($i) wie auch im ersten Szenario um den ZZZ ($aussetzen) erhöht. Dadurch geht es anschließend weiter in der Historie rückwärst (wir sind ja noch nicht bzw. nicht mehr bei 0), so lange bis wir nochmal bei 0 sind.
Schritt 6 - Crashpunkt gefunden
Wenn wir nun wieder bis auf 0 runtergezählt haben (und dieser zuletzt betrachtete Zug kein Crash war), dann nehmen wir von diesem Zug wieder das Zielfeld als den Punkt, wohin wir zurück gesetzt werden...
Hier macht sich der ZZZ-Bug am deutlichsten bemerkbar: Ziel des Algorithmus ist es - zumindest nach meiner Interpretation, dass man (insbesondere bei hohem ZZZ) auch vor den letzten Zurücksetzpunkt zurückgesetzt werden kann. Dabei sollte es eigentlich darauf hinauslaufen, dass man bei einem Zweit-Crash nach genau ZZZ Zügen (so wie in diesem Szenario beschrieben), das gar nicht merkt, weil man zwar "doppelt" zurückgesetzt wird, das sich aber irgendwie aufhebt. Da beim Zurückzählen jedoch Züge und Crashs gezählt werden, kommt es hier zu einer Abweichung (die auch noch mit jedem weiteren Crash größer wird). Richtig wäre gewesen den Zähler immer um ZZZ+1 zu erhöhen, aber dazu mehr unten...
Der 2. Crash - Szenario 3 - Crash nach mehr als ZZZ Zügen
Im Vergleich zu den vorherigen Szenarien ist das jetzt einfach. Da innerhalb der letzten ZZZ+1 (in unserem Fall 4+1=5) Züge kein Crash war, funktioniert das Zurücksetzen genau so, als wären wir noch gar nicht gecrasht. Daher gehe ich jetzt hier nicht weiter ins Detail.
Der 3. Crash
Das Verhalten beim 3. Crash lässt sich auf die gleiche Weise bestimmen. Dabei ist es im Wesentlichen unerheblich, wie viele Züge der letzte Crash wirklich zurück lag - es wird einfach nach dem gleichen Prinzip rückwärts gezählt. Ein Beispiel spare ich mir hier...
Doppelcrash
Sollte der seltene Fall eintreten, dass ihr auch nach einem Crash keine Zugmöglichkeit habt, so wird der Algorithmus im Anschluss einfach noch mal angewendet, beginnend an eurer neuen Position. Man wird dann dementsprechend weiter zurückgesetzt.
Fazit
Ich hoffe, dass war jetzt halbwegs verständlich erklärt und ihr seid jetzt in der Lage euren Zurücksetzpunkt vorherzusehen - und das ganz ohne Informatik-Kenntnisse. Ich jedenfalls für meinen Teil bin durch mit der Erklärung, bei Interesse am Code und an möglichen Lösungsvorschlägen, könnt ihr aber natürlich weiterlesen... --Ultimate 17:51, 12. Jul. 2014 (CEST)
Bisschen OpenSource
Original-Code von Didi
Da der (zugegebenermaßen evtl. etwas kompliziert programmierte) Algorithmus zur Bestimmung der Zurücksetzposition offenbar einen Bug hat, gibt's hier mal meinen "original"-Source code und danach eine korrektur bzw. Anmerkungen von Quabla.
Ich stell's hier jetzt mal zur Diskussion und möge sich die Community an diesem Stück Code beweisen :-D
if ($possibles==0) { $query="select M_ID,x_pos,y_pos,crash from $movestable [..]"; $res=do_query($query); $maxmoves=mysql_num_rows($res); if ($aussetzen>=$maxmoves) { $aussetzen=$maxmoves-1; } $walker=0; $i=$aussetzen; while ($i>=0) { $row=mysql_fetch_array($res); if ($row[crash]==1) { $sum=$aussetzen+$walker+$i; if (($sum)>=$maxmoves) $i=$maxmoves-$walker-1; else $i=$i+$aussetzen; } $i--; $walker++; } $sysmsg="-:KIch werde $aussetzen Züge zurückgesetztK:-"; echo "AUAAAA! Es quietscht und du knatterst ins Gras... Dafür wirst Du um $aussetzen Züge zurückgesetzt und startest wieder von 0."; }
Kommentare & Korrekturvorschläge
Vorwort von ultimate
Darin, dass der Algorithmus von Didi fehlerhaft ist, sind wir uns wohl alle einig. Über das, was "richtig" ist, kann man sich streiten... Die nachfolgenden Lösungsvorschläge beziehen sich auf zwei mögliche Korrekturen, daher habe ich sie mal danach gruppiert. Grundsätzlich gehe ich dabei davon aus, dass das "man soll auch weiter als bis zum letzten Zurücksetzpunkt zurück gesetzt werden können"-Verhalten von Didi grundsätzlich beabsichtigt, nur halt eben etwas kompliziert und fehlerhaft umgesetzt worden war... Lösungsvorschläge die sich auf die Beibehaltung dieses Prinzips beziehen habe ich daher zuerst aufgelistet. Vorschläge, die das Verhalten grundlegend verändern folgen im Anschluss...
Beibehalten des Weiter-Zurücksetz-Verhaltens - nur eben richtig
Unter der Annahme, dass wir Didis Intention "man soll auch weiter als bis zum letzten Zurücksetzpunkt zurück gesetzt werden können" beibehalten, ist die Korrektur des Algorithmus eigentlich recht simpel. Wie oben bereits erwähnt muss der Zähler ($i) im Prinzip nur immer um ZZZ+1 ($aussetzen+1), statt nur um ZZZ ($aussetzen) erhöht werden.
Dazu hat kili einen Vorschlag eingereicht...
von kili
Kili: vorhin schon im Forum verlinkt, aber sicherheitshalber noch mal hier: Bugfix (erzeugte Crashzuege mitzaehlen) und Aufraeumarbeiten (das $walker-Zeugs wird nicht benoetigt):
#... $maxmoves=mysql_num_rows($res); $i=$aussetzen; while ($maxmoves>0 && $i>=0) { $row=mysql_fetch_array($res); if ($row[crash]==1) { $i=$i+$aussetzen+1; } $i--; $maxmoves--; } #...
Nie weiter zurücksetzen als bis zum letzten Zurücksetzpunkt
Eine andere Möglichkeit den Algorithmus zu korrigieren ist es, das Crashverhalten grundsätzlich dahin zu ändern, dass man nie weiter als bis zum letzten Zurücksetzpunkt (also dem Feld, von dem man nach dem letzten Crash wieder von 0|0 beschleunigen musste) zurück gesetzt werden kann. In diesem Fall muss der Zähler ($i) gar nicht erhöht werden, sondern im Gegenteil das Rückwärtszählen abgebrochen werden (z.B. durch gezieltes herabsetzen des Zählers auf 0).
Dazu haben quabla und Jody eine Lösung ausgearbeitet...
von quabla
Hier die Kommentierung und Vorschläge von Quabla
if ($possibles==0) { $query="select M_ID,x_pos,y_pos,crash from $movestable WHERE [..]"; $res=do_query($query); $maxmoves=mysql_num_rows($res); if ($aussetzen>=$maxmoves) { $aussetzen=$maxmoves-1; }
$walker kann wech
#$walker=0; $i=$aussetzen; while ($i>=0) { $row=mysql_fetch_array($res); if ($row[crash]==1) {
und den ganzen Kladderadatsch in diesem if-Zweig kann man dann ersetzen durch
$i = 0; # $sum=$aussetzen+$walker+$i; # if (($sum)>=$maxmoves) # $i=$maxmoves-$walker-1; # else # $i=$i+$aussetzen; } $i--; #$walker++; }
von Jody
Jody fügt hinzu:
Damit die richtige Anzahl an Zügen ausgegeben wird, müsste man quabla's Vorschlag noch wie folgt ändern:
# ... if ($row[crash]==1) { $aussetzen -= $i; $i = 0; } # ...
Unspezifiziert
Unabhängig von dem gewünschten Ergebnis der Fehlerkorrektur (mögliche Optionen siehe oben), gibt es noch andere Möglichkeiten den Fehler zu beheben, z.B. über eine geschickte Datenbankabfrage anstelle des Rückwärtszählens.
Dazu hat zucker was ergänzt...
von zucker
Zucker: $query="select M_ID,x_pos,y_pos,crash from $movestable [..]"; Liese sich das ganze nicht über EIN sql ziehen und dann einfach einen normalen Zug machen ?
karopapier_singleplayer.zip Da drinn ist auch irgendwo sowas versteckt
Kurz-Tips
Für das Bestimmen des Zurücksetzpunkts (ZSP) möchte ich hier noch ein paar Kurzregeln hinterherwerfen...
- Crash nach ZZZ Zügen => "1 Zug nach vorne" (vom letzten ZSP)
- Immer wieder Crashen nach 1 oder 2 Zügen => funktioniert erst ab einem ZZZ > 3, lohnt sich aber oft erst ab deutlich höherem ZZZ
- Crash nach 1 Zug => "ZZZ-2 Züge zurück" (vom letzten ZSP)
- Crash nach 2 Zügen => "ZZZ-3 Züge zurück" (vom letzten ZSP)
- ZZZ = 0 => Spar dir die Zählerei ;-)