Teil 2: Verwendung von automatisierten Spielzügen
Es gibt Spielelemente, die in den verschiedensten Patiencen immer wieder auftauchen. Beispielsweise werden die Reihen meistens nach demselben Schema gebildet: auf- oder absteigend oder in einer festen Schrittfolge, gleichfarbig oder im Wechsel Schwarz-Rot oder unabhängig von der Farbe, mit einer festen Anfangs- und/oder Endkarte (oder auch nicht), usw. Damit beschäftigt sich der erste Teil dieses Dokuments.
Viele Patiencen warten jedoch mit Besonderheiten auf, die sich mit den bestehenden Tags nicht umsetzen lassen:
diamantenmine.xml
) oder das Legen auf beliebige größere Kartenwerte gleicher Farbe (Beispiel: herbstblaetter.xml
, drivel.xml
)52x14.xml
, doppelgaenger.xml
) oder Beschränkung auf komplette Sequenzen von 13 Karten (Beispiel: spinne.xml
)kerze.xml
, audienz.xml
)haeufeln.xml
)perserteppich.xml>
, loeper.xml
) oder Legerichtung (Beispiel: napoleon.xml
, sonne.xml
) während des Spielsbilderbogen.xml
, grazer.xml
) oder ganzer Reihen (Beispiel: rangier.xml
)schlangenkette.xml
)rangier.xml
, vierteskoenigreich.xml
)grausam.xml
) oder Sperrepasha.xml
, zank.xml
)Einige einfachere Beispiele sind in der Anleitung zu den XML-Spieldateien enthalten.
Wenn Sie eine spezielle Idee umsetzen wollen, schauen Sie zuerst, was mit den vorgegebenen Tags bereits möglich ist – das ist bereits eine ganze Menge. Auch wenn ein großer Teil der mitgelieferten Patiencen automatisierte Spielzüge einsetzt (manchmal mehr, manchmal weniger), kommt dennoch etwa ein Drittel der Patiencen vollkommen ohne sie aus.
Wenn sich Ihre Idee nur mit automatisierten Spielzügen umsetzen lässt, empfehle ich, die oben genannten Spieldateien zurate zu ziehen und mit den Elementen zu spielen (legen Sie Ihre Änderungen aber in einer neuen Spieldatei ab). Einige der genannten Besonderheiten lassen sich wenigen Zeilen umsetzen, andere sind – teils deutlich – aufwändiger. Die meisten kommen allerdings ohne viel Programmierkenntnisse aus.
Automatisierte Spielzüge sind allerdings sehr mächtig &8211; es handelt sich letztlich um eine turing-vollständige Sprache (ich würde sie zur Simulation einer Turngmaschine aus praktischen Gründen dennoch nicht empfehlen; weiter unten erkennt man vielleicht, warum). Um wirklich komplexe Elemente wie beispielsweise einen Computergegner umzusetzen, müssen wir etwas tiefer in die Möglichkeiten der automatisierten Spielzüge als Programmiersprache eintauchen. Bei den einzelnen Elementen der automatisierten Spielzüge werde ich im Folgenden der Einfachheit halber von Aktionen sprechen (und auch nicht zwischen beginn, aktion, abschluss und ende unterscheiden).
Aktionen werden, soweit nicht anders angegeben, der Reihe nach von oben nach unten abgearbeitet. Es kann aber auch, in Abhängigkeit von bestimmten Bedingungen, zu einer beliebigen anderen Aktion gesprungen werden (von der aus es dann in der Regel der Reihe nach weiter geht). Auch wenn wichtige Elemente einer Programmier-Hochsprache fehlen – wie Schleifen oder Unterprogramme – können diese mithilfe von bedingten Sprüngen dennoch umgesetzt werden.
Als „Variablen“ der Sprache dienen in erster Linie die Felder, die für die Patience definiert wurden. Nahezu alle entscheidenden Elemente eines Feldes können abgefragt oder neu gesetzt werden. Sehr flexibel einsetzbar ist das Element var, das eine beliebige Integer-Variable (32 Bit) speichern kann. In den folgenden Beispielen werde ich ausschließlich dieses Element verwenden. Je nach Situation können natürlich andere Elemente sinnvoller sein.
Einseitige einfache Bedingungen sind mithilfe des Tags bedingung leicht umzusetzen. Ebenfalls kein Problem sind einseitige zusammengesetzte Bedingungen, die mit and
verknüpft sind, da hierfür nur mehrere bedingung-Tags hintereinandergereiht werden müssen.
Da eine Aktion standardmäßig bei Erfolg den Durchlauf beendet und bei Misserfolg mit der Folgeaktion fortfährt, sind auch zweiseitige Bedingungen kein Problem. Wir betrachten daher den ein klein wenig anspruchsvolleren Fall, dass die Aktion bei Erfolg nicht enden soll. In Pseudocode:
WENN feld1.var = 1 UND feld2.var = 2 DANN Lege eine Karte vom Stock auf feld1 SONST Lege eine Karte vom Stock auf feld1 ENDE WENN Lege eine Karte vom Stock auf feld3Mit Aktionen lässt sich das folgendermaßen umsetzen:
<aktion typ="klick"> <-- WENN-Teil --> <bedingung feld="feld1" tag="var">1</bedingung> <bedingung feld="feld2" tag="var">2</bedingung> <lege feld="stock">feld1</lege> <erfolg>weiter</erfolg> </aktion> <aktion typ="klick"> <-- DANN-Teil --> <lege feld="stock">feld2</lege> <erfolg>weiter</erfolg> </aktion> <aktion id="weiter" typ="klick"> <-- Fortsetzung nach der Bedingung --> <lege feld="stock">feld3</lege> </aktion>
Mit or
zusammengesetzte Bedingungen sind etwas aufwendiger. Dazu muss jede einzelne Bedingung in eine eigene Aktion gepackt werden. Dann können Sie in jeder dieser Aktionen bei Erfolg zum DANN
-Teil springen und bei Misserfolg mit der folgenden Aktion fortfahren. Die letzte Bedingung springt dann bei Misserfolg in die Fortsetzung hinter dem DANN
-Teil.
Pseudocode:
WENN feld1.var = 1 OR feld2.var = 2 OR feld3.var = 3 DANN Lege eine Karte vom Stock auf feld1 ENDE WENN Lege eine Karte vom Stock auf feld3Mit Aktionen:
<aktion typ="klick"> <-- WENN-Teil 1 --> <bedingung feld="feld1" tag="var">1</bedingung> <erfolg>dann</erfolg> <misserfolg>naechster</misserfolg> </aktion> <aktion typ="klick"> <-- WENN-Teil 2 --> <bedingung feld="feld2" tag="var">2</bedingung> <erfolg>dann</erfolg> <misserfolg>naechster</misserfolg> </aktion> <aktion typ="klick"> <-- WENN-Teil 3 --> <bedingung feld="feld3" tag="var">3</bedingung> <erfolg>dann</erfolg> <misserfolg>weiter</misserfolg> </aktion> <aktion id="dann" typ="klick"> <-- DANN-Teil --> <lege feld="stock">feld3</lege> <erfolg>weiter</erfolg> </aktion> <aktion id="weiter" typ="klick"> <-- Fortsetzung nach der Bedingung --> <lege feld="stock">feld3</lege> </aktion>
Es handelt sich hier um eine einseitige Bedingung, aber sie lässt sich analog zum ersten Beispiel leicht zu einer zweiseitigen Bedingung erweitern.
Für eine Schleife muss nur an der passenden Stelle die Schleifenbedingung geprüft und entsprechend an den Schleifenanfang oder in die Aktion hinter der Schleife gesprungen werden. Kopfgesteuerte Schleifen setzen die Überprüfung an den Anfang, fußgesteuerte Schleifen an das Ende der Schleife. Das Prinzip soll an einer Schleife mit fester Wiederholungszahl demonstriert werden.
Pseudocode:
WIEDERHOLE 10 MAL Lege eine Karte vom Stock auf feld1 ENDE WIEDERHOLE Lege eine Karte vom Stock auf feld3
Heruntergebrochen auf elementarere Einzelschritte:
Setze feld1.var := 10 :schleifenstart WENN feld1.var = 0 DANN Springe zu schleifenende ENDE WENN Lege eine Karte vom Stock auf feld1 Setze feld1.var := feld1.var - 1 Springe zu schleifenstart :schleifenende Lege eine Karte vom Stock auf feld3Mit Aktionen:
<aktion typ="klick"> <!-- Laufvariable setzen --> <aenderung feld="feld1" tag="var">10</aenderung> <erfolg>naechste</erfolg> </aktion> <aktion id="schleifenstart" typ="klick"> <!-- Schleifenstart --> <bedingung feld="feld1" tag="var">0</bedingung> <erfolg>schleifenende</erfolg> <misserfolg>naechste</misserfolg> </aktion> <aktion id="schleifenstart" typ="klick"> <!-- Schleifenrumpf --> <lege feld="stock">feld1</lege> <aenderung feld="feld1" tag="var">-1</aenderung> <erfolg>schleifenstart</erfolg> </aktion> <aktion id="schleifenende" typ="klick"> <!-- Weiter nach der Schleife --> <lege feld="stock">feld3</lege> </aktion>Natürlich kann der Schleifenrumpf auch länger werden, seinerseits Bedingunen oder Schleifen enthalten und so weiter.
Mithilfe von Beziehungen zwischen den Feldern und der Änderung des Klick-Ziels während des Durchlaufs der Aktionen ist es auch möglich, eine Reihe von Feldern nacheinander zu durchlaufen.
... <feld id="g1" x="1" y="1" typ="hilf"> <beziehung id="weiter">g2</beziehung> </feld> <feld id="g2" x="2" y="1" typ="hilf"> <beziehung id="weiter">g3</beziehung> </feld> ... <feld id="g49" x="49" y="1" typ="hilf"> <beziehung id="weiter">g50</beziehung> </feld> <feld id="g50" x="50" y="1" typ="hilf"> <var>-1</var> </feld> <aktion typ="klick"> <!-- Startfeld setzen --> <setze id="ziel">g1</setze> <erfolg>naechste</erfolg> </aktion> <aktion typ="klick"> <!-- Durchlauf durch alle Felder --> <bedingung feld="ziel" tag="var" beziehung="ungleich">-1</bedingung> <setze id="ziel">ziel#weiter</setze> <erfolg>wiederholung</erfolg> </aktion>
Möglich, und vielleicht sogar eleganter, wäre auch, auf die Abbruchbedingung hin zu prüfen, dass ziel#weiter
leer ist.
Programmier-Enthusiasten haben mit den Felderbeziehungen alle Werkzeuge zur Hand, um mithilfe automatisierter Spielzüge eine Turingmaschine zu simulieren – solange man eine überschaubare Maximallänge des Bandes voraussetzen kann, denn ansonsten müsste man unendlich viele Felder definieren.
An den kurzen Programmierbeispielen wird wahrscheinlich schon recht gut deutlich, wo die Grenzen der Programmierung liegen. Einerseits handelt es sich nicht um eine Hochsprache, und komplexere Vorgänge müssen auf elementare Ebene heruntergebrochen werden. Andererseits ist bereits für diese elementaren Schritte einiges an Schreibarbeit erforderlich.
Zu erwähnen ist auch, dass alle benötigten „Variablen“ zuvor in der Spieldatei als Felder festgelegt werden müssen. Das dynamische Erzeugen weiterer Variablen ist also nicht möglich. Ebenfalls nicht zu vergessen: Sehr komplexe Berechnungen können die Spiel-Performance stark beeinträchtigen, insbesondere wenn die Aktionen bei jedem Legeversuch oder Mausklick durchgeführt werden.
Des Weiteren gibt es sicherlich noch eine Reihe an feldbezogenen Aktionen, bei denen es sinnvoll wäre, sie auf einfacherem Weg umzusetzen. Hierzu ist allerdings bisher noch kein Bedarf aufgetreten; die automatisierten Spielzüge bleiben aber weiter offen für Erweiterungen.
Nichtsdestotrotz bieten die automatisierten Spielzüge eine Basis für komplexe Berechnungen, mit denen beispielsweise auch anspruchsvolle Computergegner umgesetzt werden können. (Hierzu ein Hinweis: Der in zank.xml
realisierte Computergegner ist vergleichsweise einfach aufgebaut und ziemlich „dumm“.)