Einführung in die Programmierung mit HTML und PHP
Formulare und CSS: Spielzugeingabe
Zentrale - Lehrgänge - Einführung HTML+PHP - TicTacToe-Bedienung

Lehrziel

tictactoe - sechster Schritt Unser Lehrziel für heute: Das Tic-Tac-Toe - Spiel richtig bedienbar!

Dazu brauchen wir:
Wir werden heute erstmals ein wenig intensiver auf CSS eingehen, weil verschiedene notwendige Maßnahmen nicht mehr intuitiv selbsterklärend sind. Nachdem wir in den letzten beiden Lektionen funktionelle Elemente in HTML und PHP kennengelernt haben, kommen nämlich jetzt auch funktionelle Elemente in CSS dazu: Das Wechseln des Aussehens von Elementen in Abhängigkeit von Aktionen des Benutzers. Damit das ganze hübsch wird.

Wir werden es aus Zeitgründen noch nicht schaffen, die Auswertung von Sieg/Remis/Niederlage einzubauen. Das kommt aber gleich in der nächsten Lektion...

Ein kleiner Ansatz von Spielablauf-Logik

Nachdem wir jetzt die Möglichkeit haben, ein Spielfeld zum Server zu übertragen und von diesem die Belegung angezeigt zu bekommen, können wir ja immerhin schon durch Manipulation der URL im Browser ein Spiel gegen uns selbst spielen. Freilich ist sowas mit "bedienbar" nicht zu bezeichnen. Der Server mit seinem PHP-Script wirkt momentan als Anzeigemaschine für ein Spielfeld, dessen Belegung wir abstrakt als Zeichenkette übergeben.

Da ist mehr drin: Wir werden jetzt dazu übergehen, den Server auch als Schiedsrichter beim Ausführen von Spielzügen einzusetzen. Damit bewegen wir uns allmählich in die Richtung eines Multiplayer-Games. Die Bedienbarkeit sollten wir bei der Gelegenheit ebenfalls verbessern: Es doch schön, einen Stein durch einen einfachem Mausklick setzen zu können! Und nach dem Setzen eines Steines sollte der nächste Stein gesetzt werden können, der die jeweils andere Farbe trägt.

Das erfordert mehrere Maßnahmen...

Spielfeld im Browser zwischenspeichern
Wir wollen das Spielfeld noch nicht auf dem Server speichern (das lassen wir uns für die übernächste Lektion). Es soll sich aber natürlich im Laufe der Spielzüge mit Steinen füllen. Also muß es seinen Zustand zwischen den Spielzügen ändern können. Es reicht also nicht mehr, es einfach im PHP-Script fest auf einen konstanten Wert einzustellen. Statt dessen muß es im jeweils aktuellen Zustand (mitsamt visueller Darstellung der Belegung) vom Server an den Browser gemeldet werden, und dieser muß es mit jedem zu setzenden Spielzug wieder als Parameter an den Server übermitteln.

Indem das Spielfeld für jedes Spiel vom Server jeweils an den beteiligten Browser gesendet und von diesem bei jedem Spielzug als Parameter wieder beim Server eingereicht wird, haben wir außerdem bewirkt, daß auf unserem Server mit unserem PHP-Spiel beliebig viele Spieler gleichzeitig spielen können, ohne sich gegenseitig ins Gehege zu kommen. Jeder hat sein eigenes Spielfeld in seinem eigenen Browser.
Plätze auf dem Spielfeld anklickbar machen
Das werden wir uns gleich im nächsten Abschnitt ansehen: Wir organisieren, daß auf jedem freien Platz auf dem Spielfeld ein Klick erfolgen kann, woraufhin der Spieler, der gerade dran ist, einen Spielstein seiner Farbe auf diesen Platz setzen kann.
Spielzug entgegennehmen
Nach anklicken eines Feldes muß dann der jeweilige Spielzug auf dem Server entgegengenommen, auf Gültigkeit geprüft und ins Spielfeld einsetzen werden.

Im Resultat davon wird eine neue HTML-Seite erzeugt, die den neuen Zustand der Steine auf dem Spielfeld (und die verbleibenden Links zum Anklicken) enthält, und zum Browser zurückgeschickt.

Wobei... EIGENTLICH könnten wir uns in unserem Fall eine Prüfung des Spielzuges klemmen - FALLS wir davon ausgehen wollen täten, daß wir uns immer auf die Ehrlichkeit und Nettigkeit sämtlicher Benutzer unseres Spielchens verlassen können würden. Aber zu letzterem hatten wir ja schon ausführlich beratschlagt. Und wenn wir später am Ende des Lehrgangs gar noch KI's - eventuell sogar von fremden Leuten - in unser Spiel einbauen wollen, ist eine Prüfung des Spielzuges mehr als angebracht!
Den dran seienden Spieler umschalten
Als letztes Detail bleibt noch, dafür zu sorgen, daß immer dann, wenn ein gültiger Spielzug übernommen wurde, der jeweils andere Spieler dran ist. Das ist geradezu trivial, weil wir ja dafür gesorgt haben, daß jeder Spieler auf unserem Spielfeld-String sein Zeichen ('1' oder '2' zum Beipiel) bekommt.


Mehr sollten wir uns für heute erstmal nicht vornehmen, denn zu den einzelnen Schritten gehören doch eine Reihe von Details...

Die Speicherung des Spielfeldes im Browser

Das Spielfeld speichern wir im Browser so, daß es bei einem Spielzug in der URL mitgeschickt wird...
Erinnerung: Sie hatten in der letzten Lektion zum Schluß schon mal eine Vorarbeit dazu geleistet: Wie hatten Sie es hingekriegt, mehrere verschiedene Links in Ihr Lehrgangs-Inhaltsverzeichnis zu setzen, die unterschiedliche Spielfeld-Belegungen erzeugten?

Die triviale Lösung dazu wäre also, in jeden Link, den wir in die resultierende HTML-Seite einbauen, den aktuellen Zustand des Spielfeldes einzusetzen. Da das Spielfeld eh eine variable Sache ist, die sich zudem zig- oder gar hunderte Male wiederholen müßte, wäre eine Variable zur Zwischenspeicherung gar nicht so verkehrt, oder?! Das könnte so aussehen:
$query = '?spielfeld='.$spielfeld;

// ...und später dann...

<a href="tictactoe.php<?=$query?>"></a>

Im Allgemeinen würde man übrigens Parameter eines Query's grundsätzlich immer mit einer speziellen Funktion umkodieren, um sie in der URL gegen Fehlinterpretation zu sichern (wie schon in der letzten Lektion bei den Tipps zur Sicherung gegen Hacking angeführt):
$query = '?spielfeld='.urlencode($spielfeld);
Wenn man diese Variable auf dem Server entgegennimmt, dekodiert man einfach wieder:
$spielfeld = urldecode($_REQUEST['spielfeld']);
In unserem speziellen Fall könnten wir darauf verzichten, weil bei unserem Spielfeld keine gefährlichen Sonderzeichen vorkommen.

Den praktischen Einbau verschieben wir auf die Übung zum nächsten Abschnitt...

Die Installation von Trittschaltern im Spielfeld

Wir gehen da ganz stupide simpel vor: Jeder Platz bekommt eine Nummer - am sinnvollsten natürlich einfach fortlaufend hochgezählt wie die Indizes in unserer Zeichenkette, die das Spielfeld beschreibt:

s1 s2 s3
z1 012 z1
z2 345 z2
z3 678 z3
s1 s2 s3

Und wir organisieren, daß man den Platz anklicken kann. Wie man sowas (anklicken können) organisiert, wissen Sie bereits... (Wenn nicht: klickem Sie mal im Browser irgendwas anklickbares an! WAS haben Sie da angeklickt? Na also!)

Wir könnten also für jeden Platz des Spielfelds einen Link einrichten. Das wäre eine Form, die mit den von uns bisher verwendeten HTML-Techniken auskommt. Zum Beispiel so:
<a href="?tictactoe.php<?=$query?>&zug=5"></a>
Wir setzen unser in einer Variablen zwischengespeichertes Spielfeld mit einem weiteren Parameter für einen Spielzug zusammen. Und diesen Link plazieren wir dann auf's Feld Nummer 5 des Spielfeldes.

Wir können die Gelegenheit aber auch nutzen, die Eingabe mit Hilfe eines HTML-Formulars zu erzeugen.

Der Unterschied zwischen Formular und einfachen Links: In JEDEM Link müßten SÄMTLICHE Zustände des Spiels, die man im Browser zwischenspeichern lassen möchte (oder muß), aufgeführt werden. Das ist eine vielfache Redundanz, wenn man viele Links hat!
Wenn wir für jedes der im kleinen TicTacToe neun Felder einen Link einrichten wollten, mit dem wir jedesmal das Spielfeld (und eventuelle weitere Parameter - zum Beispiel wer gerade dran ist) mitschicken wollten, dann müßten wir diese Parameter ja neun mal redundant anlegen!

Erinnern Sie sich noch, wie ich schon zur Gelegenheit einer einfachen (....aalsoooo zwei-fachen...) Wiederholung des Seitentitels in der dritten Lektion einen halben Aufstand geprobt hatte? Da werden Sie jetzt sicherlich Verständnis aufbringen, daß die Sache mit neun Wiederholungen noch ne ganze Spur schärfer wäre, nicht wahr?! Wir könnten zwar inzwischen aus Programmierersicht mit Hilfe einer Schleife diese Wiederholung aus unserem PHP-Programm heraushalten, aber die erzeugte HTML-Seite würde sich aufblähen, denn HTML kennt keine Wiederholungs-Operatoren oder dergleichen. Weil HTML auf Einfachheit getrimmt ist.

Und stellen Sie sich die Sache nur mal vor, wenn Sie das Spielfeld vergrößern wollen würden - etwa zur "Gomoku"-Variante auf einem 15 x 15 - Feld! Oder gar auf das ganz große Go-Feld mit 19 x 19 Plätzen! Da müßten Sie knapp 400 Links erzeugen und in jedem einzelnen die 400 Feldbelegungen mitschicken! 160 Kilobytes für - völlig überflüssige Wiederholung von Daten!

DAS wollen wir nicht, oder?!

Ein Formular ermöglicht uns, eine geballte Ladung verschiedenster Daten im Paket zusammenzufassen und gemeinsam zu versenden. Sogar in recht frei wählbarer Kombination (wozu man sich natürlich an gewisse Ausdrucksweisen zu halten hat, dazu kommen wir gleich). Und dabei aber alle Daten nur ein einziges mal notieren zu müssen.

Wir machen dazu folgendes: Um unser Spielfeld legen wir ein <form> - Tag:
<form>
<div class="tictactoe">
    <table>
        <tr><th>    </th><th> s1     </th><th> s2     </th><th> s3     </th><th>    </th></tr>
        <tr><th> z1 </th><td><?=$f0?></td><td><?=$f1?></td><td><?=$f2?></td><th> z1 </th></tr>
        <tr><th> z2 </th><td><?=$f3?></td><td><?=$f4?></td><td><?=$f5?></td><th> z2 </th></tr>
        <tr><th> z3 </th><td><?=$f6?></td><td><?=$f7?></td><td><?=$f8?></td><th> z3 </th></tr>
        <tr><th>    </th><th> s1     </th><th> s2     </th><th> s3     </th><th>    </th></tr>
    </table>
</div>
</form>

In dieses Formular können wir jetzt Formular-Elemente packen, die Benutzereingaben aufnehmen können. Deren Inhalte werden beim Abschicken des Formulars alle genau wie bei unseren Query-Experimenten zu exakt denselben Name-Wert-Paaren zusammengesetzt und exakt genau so in die URL eingetragen, die dann vom Formular aufgerufen wird. Was hier natürlich wieder dieselbe Seite mit dem Spielfeld sein wird, weil wir ja nun nicht einfach so vom Spiel davonlaufen wollen.

Konkret könnten wir eine Schaltfläche gebrauchen, die, sobald man sie anklickt, das Formular abschickt. Und zwar auf jedem noch besetzbaren Spielfeld einen solchen Schaltknopf. Damit wir auf so einfache Weise wie möglich einen Spielstein setzen können:
<input type="submit" name="zug" value="5"/>
Sie dürfen jetzt dreimal raten, welchen Teil der Name-Wert-Paare des resultierenden Querys die Inhalte der Attribute "name" und "value" einnehmen! Der "type" definiert, daß es sich um einen Schalter handeln soll, der das Formular bei Klick sofort "abschickt" (englisch: "submit").

Jetzt muß aber noch das Spielfeld hinzukommen. Allerdings wollen wir diesen häßliche String nicht anzeigen lassen (dazu haben wir uns ja extra reichlich Mühe mit den hübchen Spielsteinen und dem Spielbrett gegeben), sondern nur irgendwie ins Formular einbauen, so daß der beim Abschicken halt mit in die query-Parameter eingehängt wird. Und das geht SO:
<input type="hidden" name="spielfeld" value="<?= $spielfeld ?>"/>
...wobei Ihnen natürlich auch niemand verbieten würde, das Spielfeld mit urlencode zu kodieren, was bei unserem konkreten Fall aber nicht notwendig ist. Das Ding brauchen wir im gesamten Formular nur ein einziges Mal. Weil eben das tolle an einem Formular ist, daß es sämtliche in ihm enthaltenen Elemente beim Abschicken "zusammenbaut".


Übung zu Trittbrettschaltern
So, jetzt bauen wir das Zeug zusammen...

Dazu erst nochmal zur Kontrolle, ob Sie auch verstanden haben, was Sie machen sollen:
  • Was haben wir bereits auf dem Spielfeld? Richtig: Steine.
  • Was brauchen wir auf dem Spielfeld? Richtig: Links!
  • Wie sollen die Links aussehen?
  • Wie werden die Plätze bisher belegt?
  • Wo könnten wir die Plätze außer mit Steinchen auch mit Links belegen?

Gut, die Fragen sind bei vielen Menschen, die das Programmieren erlernen möchten, notwendig. Denn die sind es aus dem Alltagstrott üblicherweise nicht gewöhnt, Dinge im Detail bis zu den letzten Konsequenzen und ohne Vernachlässigung von Einzelheiten bis zum bitteren Ende zu durchdenken und zu durchrechnen. Das ist aber etwas, woran man sich als Programmierer für ablauf-orientierte Programmierung zwingend gewöhnen muß. Solche Fragen müssen sich in Zukunft in Ihrem Gehirn von allein mit stoischer Beharrlichkeit immer und immmer wieder abspulen. Das ist das Schicksal der Programmier-Handwerker auf dem derzeitigen Niveau der Programmierwissenschaft.

Tipp: Um den letzten Punkt (Einbau in unserer ohnehin schon laufenden Schleife) so einfach wie nur irgend möglich zu machen: Wäre es nicht trivial, in unserem "$icons"-Array den Eintrag "0" mit eben unserem "input"-String zu belegen und dann ist gut?!

Es gibt da eine Schwierigkeit: Wir müssen eine Winzigkeit von Zahl variabel einbauen: Jedes Feld muß seine eigene Nummer in den "zug"-Value eingetragen kriegen! Wobei die Nummer ja durchaus in der Schleife als "$i" herumgeistert. Aber wenn wir einen Text greifen, der zunächst mal eine KONSTANTE (!) darstellt (wenn da ein "a" steht, dann steht da ein "a" und kein "b" und so weiter), wie kriegen wir da mitten in den Text eine Variable reingedrückt?

Für alle die, die momentan unterfordert sind, gibt es das als selbständige Übung: Ja, es geht, benötigt aber die Fähigkeit (und ein wenig Übung darin, gelle?!), um die Ecke zu denken und den Faden nicht zu verlieren. Deshalb machen wir es im Lehrgang anders. Aber wer Lust hat, darf einen der folgenden Lösungswege (oder auch noch was ganz anderes eigenes) ausprobieren: Wenn ich jetzt die dazu notwendigen Ideen als Anleitung ausbreite, sitzen wir nächsten Monat noch hier. Deshalb: Nur für Interessierte, die sich sonst eher langweilen würden.

Die anderen gehen den folgenden Mainstream-Weg!

OK, also so extrem einfach es im ersten Moment anmutet, wegen der Variablen machen wir's doch anders - mit einer Technik, die wir bereits kennen: Verzweigung...! Diesmal mit "if"! Erweitern Sie Ihre Schleife zu:
for($i = 0; $i < 9; $i++)
{
    $beleg = $icons[$spielfeld[$i]];

    if ($beleg == '') $beleg = '<input type="submit" name="zug" value="'.$i.'"/>';

    $GLOBALS['f'.$i] = $beleg;
}

Wir haben die direkte Zuweisung der Icon-Inhalte zu unseren konstruierten globalen Variablen einfach in der Mitte durchgesägt, dafür die Zwischenvariable "$beleg" eingeführt, und in der Mitte im Fall eines leeren Feldes unseren Link eingefügt. Und so wie es dort steht, ist das Dazwischensetzen des Feldzählers "$i" etwas triviales, was Sie schon lange (2 Lektionen immerhin!) kennen.

Aufgabe: Ab damit in Ihren Code!
Ergänzen Sie außerdem die <form>-Tags um Ihre tictactoe-Tabelle und legen Sie das das hidden-Element, das wir schon weiter oben erwähnt hatten, in das Formular (aber irgendwo neben die Tabelle)!

Wenn wir das jetzt allerdings auf die Menschheit loslassen, läuft uns wahrscheinlich ein kalter Schauer über den Rücken:


Mit DIESEN häßlichen und viel zu kleinen Schaltflächen können wir das unmöglich bestehen lassen!
Was wir jetzt zwingend brauchen, ist ein Abstecher nach CSS, um diese Schaltflächen unserem Design anzupassen!

CSS-Schönheitsoperation
Zuerst mal müssen diese Schalter ihr gesamtes jeweiliges Feld bedecken! Wir müssen sie in der Größe genauso wie die Feldflächen einfach größer festlegen! Schauen Sie mal in Ihr eigenes Progrämmchen, wie Sie für die Felder die Größe festgelegt hatten! Da steht was von:
.tictactoe td
Das hier sind die Kennworte, die angeben, auf welche Abschnitte
im HTML-Text sich die Definitionen in diesem
Block
alles zwischen {}
beziehen sollen. Diese Kennworte finden sich im HTML-Text wieder:
  • Wo ein Punkt (wie bei .tictactoe) davor steht, geht es um ALLE HTML-Elemente, die ein "class"-Attribut haben, in dessen Wert der Name hinter dem Punkt vorkommt (hier um die Tabelle deren "class" "tictactioe" enthält - also unser Spielfeld!).
  • Wo nichts davor steht, geht es um ALLE HTML-Elemente, die diesen Tag-Namen haben (hier um alle Tabellenzellen (td) in der Spielfeld-Tabelle).
Diese Dinger nennt man "Selektoren". Nicht ganz unerwartet, gelle?! Sie SELEKTIEREN aus dem HTML-Zeug alles raus, was auf sie paßt. Und alle diese selektierten HTML-Elemente bekommen dann all die Definitionen aufgedrückt, die in dem folgenden {Block} stehen. Irgendwie einleuchtend, oder?!

Wenn mehrere Selektoren wie bei uns hintereinander aufgereiht sind (OHNE weitere Sonderzeichen zwischen ihnen), dann ist eine Schachtelung gemeint. Hier also: Alle td-Elemente, die sich hierarchisch UNTERHALB (also umgangssprachlich: innerhalb) der Tabelle Namens "tictactoe" befinden. Falls weitergehendes Interesse besteht: Rechts oben hängt im Linkverzeichnis auch ein Verweis auf CSS 2.1 und die CSS-Gesamtübersicht sowie etliche Tutorials!
{ ... width: 100px; height: 104px; /* box-sizing wird in der Tabelle nur horizontal als "content-box" ausgeführt, */ /* vertical gilt trotzdem "border-box" (konsistent über Browser) */ ... }
Hier wird die Höhe und die Breite der einzelnen Felder des Spielfeldes festgelegt. Und zwar für alle "td's", die innerhalb der tictactoe-Tabelle vorkommen.

Wobei Höhe und Breite nicht ganz exakt übereinstimmen - falls Ihnen das auffallen sollte. In dem Kommentar darunter ist diese Sache begründet. Wir brauchen uns für solche Seltsamkeiten nicht verantwortlich zu fühlen. Zumindest NOCH lange nicht. Wir nehmen einfach mal hin, daß wir gegebenenfalls ein paar Pixel korrigieren müssen. Statt uns darüber paranoid den Kopf zu zermartern, learnen wir einfach mal wieder durch doing bzw. Versuch und Irrtum, das geht hier um Größenordnungen schneller als diese Krümelkackerei logisch sinnvoll begreifen und sich dann auch noch merken zu wollen...

TATSÄCHLICH jedenfalls sind die 100 Pixel x 100 Pixel groß.

Unsere Schalter sind von dieser Festlegung nicht betroffen, weil sie keine "td's" sind, sondern "input's".
Damit sollte es trivial sein, rauszukriegen, wie ein CSS-Block gekennzeichnet werden muß, um für "tictactoe-inputs" zu gelten! oder?!
.tictactoe input
{
    width:           100px;
    height:          100px;
}
Aufgabe: Ab damit in Ihren Code! Das Ergebnis sollte in etwa DAS hier sein:


Jetzt muß noch diese häßliche Standard-Schaltflächen-Kriegsbemalung verschwinden! Wir wollen:
  • KEINEN Rand!
  • KEINEN Hintergrund!
  • KEINE Schrift!
Ja, richtig gelesen: Die Schaltfläche soll - erstmal - komplett transparent (durchsichtig) werden! Weil unser Mahagony-Untergrund reichlich teuer war und den zu verdecken einfach eine Frechheit wäre! Also weg mit dem Zeug!
.tictactoe input
{
    border: none;           /* keine Border!  */
    background: transparent;/* keine Füllung! */
    font-size: 0;           /* keine Schrift! */
}
Aufgabe: Ab damit in Ihren Code! Das Ergebnis sollte in etwa Das hier sein:


Jetzt wäre es aber nett, wenn beim Überstreichen einer besetzbaren Fläche mit der Maus eine dezente optische Rückmeldung an den Benutzer gehen würde! Es ist nämlich nett, einem Benutzer (nicht zu aufdringlich allerdings) mitzuteilen, wo er klicken kann. Auch damit er merkt, daß das Spiel funktioniert und losgehen kann...
Dazu gibt es in CSS extra Selektor-Attribute, für unseren Zweck ":hover":
.tictactoe input:hover
{
    background: rgba(0,0,0,0.3);
}
Die eingestellte Farbe verdient etwas Beleuchtung... Wir wollen ja nun unsere schicke Mahagony-Fläche nicht ruinieren! Also verbietet sich das Ausschütten jedweder Farbeimer über dem Brett komplett! Was hier statt dessen gemacht wird, ist, das Bild insgesamt dezent etwas abzudunkeln. Und zwar indem eine schwarze Scheibe (die Nullwerte für rot, grün, blau in dem "rgba"-Wert) nur dezent mit einer Stärke ("alpha") von 30% alias 0.3 abdunkelnd "transparent" über den Hintergrund darunter gelegt wird.

Man könnte es natürlich auch andersrum machen (aufhellen). Oder ein leichtes Heben und Senken der Trittflächen mittels Randzeichnungen erzeugen. Solche Spielereien seien als Übung für alle Interessierten empfohlen. Es kann mächtig Spaß machen und zu visuellen Effekten führen, die einem das Wasser im Munde zusammenlaufen lassen!

Außerdem wäre es konsistent zum gesamten Rest der Welt, wenn sich der Cursor über einem anklickbaren Gebiet in das Symbol einer Hand mit ausgestrecktem Finger verwandeln würde. Dazu können wir einen Cursor aus dem Internet ziehen oder auch den vom System bereitgestellten verwenden. Ich habe vor, Ihnen nachher einen dynamisch vom dran seienden Spieler abhängigen unterzuschieben, deshalb begnügen wir uns zunächst mit was einfachem (Sie könnten das Ding natürlich auch ins hover reinpacken - das ist hier egal, weil der Cursor eine implizit eingebaute hover-Verwaltung mitbringt):
.tictactoe input
{
    cursor: pointer;
}

Aufgabe: Ab in Ihren Code damit! Und eventuell ein klein wenig damit herumbasteln! Das große Coden lassen Sie sich bitte noch für eine Weile später!
Das Ergebnis sollte in etwa Das hier sein:


Noch hübscher wäre es, wenn man DIREKT einen Stein (!) plazieren könnte! Das erfordert allerdings noch eine Kleinigkeit an Vorbereitung, die wir im übernächsten Abschnitt angehen...

Für den Moment erstmal Pause! Lassen Sie das ganze (hauptsächlich CSS diesmal gewesen) nochmal in Ruhe an Ihrem inneren Auge vorbeiziehen und entspannen Sie sich für den nächsten Schritt...! Da machen wir mit Volldampf in PHP weiter!

Die Übernahme von Spielzügen auf dem Server

Können Sie sich noch erinnern, wie Sie in der letzten Lektion die "in ein Script hineinfließenden" Query-Parameter gegriffen haben? Wenn nicht, werfen Sie nochmal einen Blick zurück auf unsere Hacking-Experimente mit $_REQUEST!

Wir haben gerade eben erst Links in Form von Submit-Buttons eines Formulars auf Spielfeld gelegt. Die Dinger hießen nochmal WIE?! Ja: "zug". Gucken Sie nach!

Unter GENAU DIESEM Namen müßten wir ja nun, sobald wie uns die Frechheit herausnehmen, auf ein Feld zu klicken, Variablen im $_REQUEST auftauchen sehen... Probieren Sie das aus! Falls Sie Ihr Debugging zwischendurch mal abgeschaltet haben sollten: Schalten Sie's wieder ein! Gucken Sie gegebenenfalls nochmal in der Lektion 4 nach, wie das ging!

Es sollte in etwa folgende Debugmeldung erscheinen:
====================
$_REQUEST:
====================
array (
  'spielfeld' => '000010002',
  'zug' => '6',
)
====================
Ich hatte hier zum Beispiel aufs Feld links unten (Nummer 6) geklickt.

Wir könnten nun in unserem PHP-Code diesen Zug übernehmen. Dazu müssen wir diesen Verarbeitungsschritt in die verschiedenen anderen Verarbeitungsschritte einbetten, die wir ja schon eingerichtet haben. Als da wären:
  • Spielfeld-Übernahme aus dem Request
  • Vorbereitung der Spielsteine
  • Spielfeld-Anzeige
Damit wir jetzt keinen Fehler begehen, sollten wir diese beiden Abschnitte erstmal DEUTLICH (!) in unserem PHP-Code kennzeichnen. Und das geht SO: TicTacToe mit kräftigeren Kommentaren (Code)

Wo gehört jetzt das Übernehmen eines Spielzuges hin?!
  • Vor die Spielfeldübernahme?
  • NACH dem Vorbereiten der Spielsteine?
  • Noch später verbietet sich selbstredend, gelle?!
Was bleibt also übrig?

Das festgestellt, ist die Übernahme recht trivial: Wir schieben noch eine kleine Überprüfung des Wertebereiches dazwischen, um vor unschönen Hacks gesichert zu sein, sowie eine Prüfung der Gültigkeit des Spielzuges im Sinne der Spielregeln (nur auf freies Feld)...
if (isset($_REQUEST['zug']))
{
    $zug = (int) $_REQUEST['zug'];    // die Feldnummer, auf Ganzzahlenwert festgenagelt
    
    if (0 <= $zug && $zug <= 8)       // Schutz vor Hacking; Feld-Indizes sind Null-basiert
    {
        if ($spielfeld[$zug] == '0')   // Spielzug erlaubt?
        {
            $spielfeld[$zug] = '1';    // erstmal der schwarze Spieler
        }
    }
}
Übung zur Spielzugübernahme
Aufgabe: Ab damit in Ihren eigenen Code!
Es müßte ab jetzt möglich sein, schwarze Spielsteine auf dem Spielfeld abzusetzen! Das ganze sollte in etwa SO aussehen... TicTacToe mit Setzen schwarzer Steine (Code)

Tipp: Undo/Redo gratis: Netter Nebeneffekt des Prinzips, daß wir den Spielstand jedesmal per URL übermitteln: Der Browser hält Wache über die verschiedenen (!) URL's, die wir ansteuern. Und ermöglicht uns, schrittweise in diesen URL's zurück und auch wieder vorwärts zu wandern. Damit haben wir gratis einen Spielzugspeicher und ein Undo-/Redo-System spendiert bekommen! Probieren Sie's aus!

Tipp: Speichern/Laden gratis: Weiterer netter Nebeneffekt desselben Umstands:
  • Legen Sie sich mal zu einem Spielstand ein Lesezeichen an! (Sie wissen doch noch, was ein Lesezeichen ist?!)
  • Schließen Sie den Browser und starten Sie ihn neu!
  • Aktivieren Sie Ihr Lesezeichen!
Was sehen Sie?

Der Ausbau der Spielablauf-Logik für zwei Spieler

So, schwarz und weiß haben sich gefälligst abzuwechseln, nicht wahr?!

Nachdem Sie nun sogar ein ganzes Spielfeld speichern und laden können...
Wie würden Sie das "Dransein" der beiden Spieler speichern?
OK, einigen wir uns auf eine Variable namens "dran". Die wir auch - wie bei ALLEN unseren Variablen bisher gehandhabt - unter exakt demselben Namen im Formular ablegen. (Das letztere erfordert WAS genau nochmal?)

Tipp: Gewöhnen Sie sich an, die Namen der Variablen im PHP-Code immer exakt genau so zu halten wie die Namen der zugehörigen Elemente in Formularen oder Querys von Links! Sie vermeiden damit, sich selbst in den Wahnsinn zu treiben...

Wobei wir uns jetzt mal Gedanken über die Spielphasen machen sollten:
  • Das Spiel startet mit einem der beiden Spieler "dran".
    Der Gerechtigkeit halber sollte der zuerst "dranne" Spieler regelmäßig wechseln!
    Dazu könnten wir eine Vorschalt-Variable anlegen - zum Beispiel mit dem Namen "erster".
    Das Spiel geht dann so los, daß zuerst "dran" auf den Wert von "erster" gesetzt wird. (Umgangssprachlich: Dran ist zuerst der, der erster ist!)
  • Anschließend wechseln sich beide Spieler von Zug zu Zug ab. Das könnten wir realisieren, indem unmittelbar nach dem Eintrag des aktuellen Spielzuges ins Spielfeld der "dran"-Wert gewechselt wird (immer fein zwischen "1" und "2" hin und her schaltend).
Wir brauchen also gleich mal noch eine extra Variable UND wir können einen ganz anfänglichen Anfangszustand festlegen, indem zuerst in einem Satz von Spielen immer der Spieler Nummer 1 beginnt (schwarz) (was sich mit den üblichen Spielregeln bei TicTacToe und Gomoku deckt).

Das sind jetzt alles an sich triviale Nebensächlichkeiten, aber die läppern sich zu ein paar Codezeilen zusammen...

Übung zum Spielablauf für zwei Spieler
Ganz zu Anfang des Programms kommt zur Spielfeld-Ur-Initialisierung hinzu:
$erster = '1';    // wer beginnt, wechselt sich von Spiel zu Spiel ab
$dran = $erster;  // erstmal können wir bei Spielstart den ersten starten lassen

Zur Umschaltung der Spieler könnten wir mal wieder ein Array benutzen (weil das zum Darstellen der Spielsteine so schön geklappt hat):
$nächster = array
(
    '1' => '2',
    '2' => '1'
);

Bei der Übernahme eines Spielzuges muß ergänzt bzw. geändert werden:
if (isset($_REQUEST['zug']) && isset($_REQUEST['dran']))
{
    $zug = (int) $_REQUEST['zug'];  // die Feldnummer, auf Ganzzahlenwert festgenagelt

    $dran = $_REQUEST['dran'];
    if (!isset($nächster[$dran])) $dran = '1';  // Schutz vor Hacking: nur '1' oder '2' erlaubt

    if (0 <= $zug && $zug <= 8)     // Schutz vor Hacking; Feld-Indizes sind Null-basiert
    {
        if ($spielfeld[$zug] == '0')    // Spielzug erlaubt?
        {
            $spielfeld[$zug]= $dran;
            $dran = $nächster[$dran];   // Spielerwechsel nur bei korrektem, angenommenem Zug
        }
    ...

Zur Speicherung des "dran" und "erster" im Formular kommt hinzu...
<input type="hidden" name="dran"      value="<?=$dran?>"/>
<input type="hidden" name="erster"    value="<?=$erster?>"/>

Aufgabe: Ab damit in Ihren Code! Ausprobieren!
Es sollte jetzt möglich sein, ein Spiel zu zweit zu spielen!
Das ganze sollte in etwa SO aussehen... TicTacToe mit einfachem vollständigem Spiel (Code)

Das erneute Starten nach Beenden eines Spieles (wenn einer gesiegt hat oder das Spielfeld voll ist) ist noch etwas mühselig durch Hacking in der URL zu erledigen. Aber das kommt gleich im letzten Abschnitt für heute dran...

Eine letzte Verhübschung fehlt noch, die wir uns vorhin vorgenommen hatten: Wenn wir schon in einem Spiel sind, wo man Spielsteine platzieren kann, dann sollte es sich doch gehören, daß man auch Spielsteine "in die Hand nehmen" kann, wenn man sie platziert, oder?!
Das können wir mit einem CSS-Cursor erreichen. Und da wir zwei Spieler mit zwei verschiedenen Farben haben, möchten wir doch bitteschön den passenden Cursor in die Hand gelegt bekommen! Wir lassen also statt dem Hand-Cursor ab sofort einen Spielstein als Cursor einblenden. Und weil der recht groß ist und als "aktiven Punkt" aber seinen Mittelpunkt haben soll (sonst wirkt das reichlich links oben verklemmt), definieren wir ihm gleich noch eine Verschiebung um die Hälfte eines unserer Felder...
$iconfiles = array
(
    '1' => 'Hut-schwarz.png',
    '2' => 'Hut-weiß.png',
);

.tictactoe input[type="submit"]
{
    ...
    cursor: pointer;     /* für alle Browser, die extra Cursors nicht unterstützen */
    <?php if ($dran)
    echo 'cursor: url("'.$iconfiles[$dran].'") 50 50, pointer;';
    ?>
}

Aufgabe >:-> Ab damit in Ihren Code! Ausprobieren!
Es sollte jetzt möglich sein, zu zweit Spielsteine zu legen! Das ganze sollte in etwa SO aussehen... TicTacToe mit einfachem vollständigem Spiel und verhübscht (Code)
Es machen aber noch nicht alle üblichen Browser bei diesem Spiel mit. Hier haben wir mal ein Feature gegriffen, welches noch in Entwicklung ist. Aber lohnenswert sieht es doch aus, oder?!

Einen Moment Pause und dann zum Endspurt!

Der Ausbau der Spielablauf-Logik zum Starten bzw. Neustarten

Der letzte Abschnitt für heute bringt uns nochmal heftig in die Regionen von CSS und läßt uns ein wenig PHP-Konstrukte üben... Für alle Kursteilnehmer, die mit dem bisherigen Stoff unterfordert gewesen sein sollten, tut sich ab hier ein ganzes Reich an Experimentiermöglichkeiten auf, wo man zwar schon mit den einfachsten Konstruktionen was überhaupt funktionsfähiges hinsetzen kann, aber mit entsprechender Energie auch Dinge erschaffen kann, die den Benutzer einfach die Kinnlade runterklappen lassen. Tun Sie sich also bei Lust und Laune keinen Zwang an, über die hier vermittelten Ideen hinauszugehen!

Ziele:
  • Wir brauchen noch mindestens eine Möglichkeit, das Spiel anständig (ohne URL-Hacking) zu starten (bzw. nach Ablauf NEU zu starten)!
  • Außerdem wäre es nett, wenn außer durch den Cursor auch noch anders angezeigt werden würde, wer gerade dran ist: Wir hatten gerade erst erwähnt, daß die benutzerdefinierten Cursors noch nicht von allen üblichen Browsern unterstützt werden (auch wenn sich das schnell ändern kann). Die betroffenen Spieler wären ein wenig in den Arsch gekniffen, wenn sie nicht erkennen könnten, wer gerade dran ist!

Zum Starten könnten wir unserem Formular einfach noch einen weiteren Druck-Schalter (also ne Taste) spendieren.
Zur Anzeige des dran seienden Spielers könnten wir SOVIEL verschiedene Anzeigevarianten entwickeln, daß wir ganze Romane drüber schreiben könnten. Wir ziehen das folgendermaßen durch: Ich mache Ihnen dazu einfach ein paar Vorschläge, und Sie entscheiden sich, wie Sie es umsetzen wollen, nach eigenem Geschmack.

Eine Start-Taste
Das Grundprinzip, wie wir das mit der Start-Taste machen, ist trivial und von Ihnen schon längst angewendet worden:
  • input type="submit" name="start" (oder so ähnlich) im Formular
  • if (isset($_REQUEST['start']))... auf dem Server

Das formuliere ich jetzt nicht mehr in Einzelschritten aus. Sie sind jetzt bereits erwachsen genug, die paar Details in HTML und PHP selbst hinzusetzen! Außerdem schreibe ich in den Übungs-Boxen nicht mehr extra an allen möglichen Stellen hin, daß Sie das selbst ausprobieren sollen. Das versteht sich von selbst!

Daß Sie mit dem Start das Spielfeld leeren und den startenden Spieler wechseln wollen, ist noch in Erinnerung? GEMACHT haben Sie das eh schon in Ihrem Programmcode, nur halt noch nicht explizit auf einen Button-Klick hin. Los geht's!

Die Frage ist eher eine vom Design her: Wo soll der Schalter auf der Spielseite hin?!
  • Auf dem Spielfeld in irgendeine Ecke? Sieht eventuell etwas hingequetscht aus!
  • Unsichtbar, durch Drücken eines Hotkeys ausgelöst? Braucht zumindest eine Erklärung auf einer Hilfe-Seite (Bedienungsanleitung)!
  • So wie man das von hübsch designten Automatik-Brettspielen kennt? Mit einem Bedienfeld neben dem Spielfeld?

Die ersten beiden Varianten sind sicherlich die einfacheren. Aber ich möchte Ihnen doch die dritte empfehlen, weil wir noch in Richtung eines Multiplayer-Games programmieren wollen und dazu noch eine ganze Reihe weiterer Bedienelemente notwendig sein werden, die wir eh irgendwie unterbringen müssen und die eh nicht alle aufs Spielfeld gequetscht passen würden.

Festlegung: Also gehe ich davon aus, daß wir ein kleines extra Bedien-Panel neben dem Spielfeld anlegen...
Wie geht das am einfachsten? Und wo wäre es am sinnvollsten?
  • Wir werden (demnächst, noch nicht heute) für den Multiplayer-Modus sowas wie Namensfelder benötigen. Die sollten vielleicht nicht allzu eng gefaßt sein.
  • Eine Spielstandsanzeige (Gewinn-Zähler) bei den Namen wäre auch nicht zu verachten.
  • Das Bedienfeld sollte vielleicht nicht sinnlos Platz verbraten.
  • Wir könnten auch im Auge behalten, das Spielfeld in einem späteren Ausbau auf Gomoku- oder Go-Größe (oder gar beliebig) auszudehnen.
  • Wir sollten eventuell auch einen Gedanken an die Bildschirme verschwenden, auf denen wir es laufen lassen wollen!

Die Kriterien führen durchaus zu konträren Ansichten:
  • Für hochformatige Bildschirme (viele Handy's und Nano-Computer) wäre es günstiger, das Bedienfeld unter das Spielfeld zu setzen.
    Für breitformatige (alle modernen Desktop-Systeme und Laptops) selbstredend das Gegenteil.
  • Solange genug Bildschirmplatz in Reserve vorhanden ist, dürfte ein druntergesetztes Bedienpanel geometrisch eine ausgeglichenere Figur abgeben, weil wir erstmal nur wenig Höhe, aber relativ viel Breite benötigen werden.
  • Falls man allerdings sowas wie eine Spielzug-Folgen-Anzeige bauen will - die sich der Höhe nach in die Länge zieht (ups, eigenartiges Umgangsdeutsch!) - wäre wieder ein Hochformat des Panels besser dran, welches an der Seite des Spielfeldes besser aufgehoben wäre.
Je nachdem, was einem wichtiger ist, muß (darf) man sich entscheiden. Ich werde jetzt eine Variante durchspielen, aber Sie dürfen natürlich ganz nach Beliebigen variieren!

Mit CSS übrigens können wir auch im Nachhinein noch - ohne funktionell ein einziges Bit zu verändern - das Aussehen komplett umkrempeln. Vielleicht ist das jetzt eine gute Gelegenheit, gezielt einen Eindruck davon zu gewinnen, indem Sie sich auf der Seite Zen-Garden mal ganz gezielt und bewußt - ohne einen Handschlag tun zu müssen, nur mit dem Gehirn eingeschaltet bei der Sache - die Fähigkeiten von CSS reinziehen! Es geht dort um das komplette Auswechseln des Designs durch pures Wechseln der drangepappten CSS-Datei... (Was die wechselnden URL-Parameter zu bedeuten haben, sollten Sie ja inzwischen ansatzweise erahnen können...) Probieren Sie mal zwei, drei Ansichten durch, damit Sie merken, wie drastisch die Möglichkeiten sind! Ich empfehle: Also: Wenn Bedarf besteht, können wir im Nachhinein das Design allemal noch komplett umkrempeln. Die momentane Entscheidung ist eher damit begründet, daß wir darin ja nun keine Übung haben und das CSS eigentlich bei uns noch nicht so unmittelbar im Vordergrund stehen soll.

Festlegung: Ich werde jetzt einfach ein Panel unter das Spielfeld hängen.
Mit welchen HTML-Mitteln machen wir das am besten?
  • Mit einem Universal-Container "<div>" wäre das am einfachsten aus HTML-Sicht und der günstigste Kompromiß, falls wir später nochmal verschiedene CSS-Layouts mit ganz anderen Positionierungen probieren wollten.
  • Andererseits könnte man es genauso gut mit dem Spielfeld in eine Tabelle oder Liste packen. Erfordert aber bei Design-Änderungen extra Aufwand. Würde als Tabelle für den Moment die Platzierung und Ausrichtung vereinfachen, weil eine Tabelle eben genau zur Unterstützung der Platzierung und Ausrichtung da ist.
  • Statt in einen "div"-Universalcontainer könnte man das Ding auch in IRGENDEIN anderes HTML-Element packen und es sich mit CSS zurecht hämmern. Aber wir müssen das jetzt auch nicht übertreiben, gelle!?
Festlegung: Ein "<div>" unter dem Spielfeld!
Damit wir es anschließend so einfach wie möglich haben, das Panel an unsere Design-Wünsche anzupassen, geben wir ihm gleich noch einen CSS-Namen (alias entweder ein "id"- oder ein "class"-Attribut) mit...

Wenn wir das mal eben so hinschreiben und mit dem "input"-Element von ganz vorn füttern...
<form>
...
<div class="tictactoe">
    <table>
        ...
    </table>
    <div class="panel">
        <input type="submit" name="start" value="Start!" title="Spiel (neu) starten!"/>
    </div>
</div>
</form>
...kommt folgendes heraus... (Code)
  • negativ: Unser Submit-Button im Panel ist von den Festlegungen betroffen, die eigentlich nur für die Buttons auf dem Spielfeld gedacht waren (unsichtbar und auf 100x100 Pixel aufgezogen). Da müssen wir eine Einschränkung treffen: Das soll natürlich nur für die Felder auf dem Spielfeld (also in der Tabelle) gelten!
  • positiv: Das Ding hängt erstmal richtig, das Panel ist sogar von ganz allein auf die richtige Breite aufgezogen. Das erspart uns etwas CSS-Bastelei...
  • Mit Mahagony sähe das Panel aber besser aus, gelle?!
    Die betreffende Definition für den Background kopieren (nicht stehlen!) wir aus der Spielfeld-Definition...
Also:
  • Selektor ".tictactoe input" ändern in ".tictactoe table input" (auf die Schaltflächen auf dem Spielfeld begrenzen)
  • Background vom ".tictactoe table" kopieren zu einem neu angelegten ".tictactoe panel"
.tictactoe table input
{
    ...
}

.tictactoe table input:hover
{
    ...
}

.tictactoe .panel
{
    background:     url('mahagony.png') #480915;
}
-> führt zu DEM Aussehen (Code). Na ja...

Das unterstützt die Intuition aber nun gar nicht! Das Panel, auch wenn wir noch nicht viel draufgelegt haben, könnte doch schon mal in einer realistischen Größe erscheinen! Damit unser Gehirn in die Lage versetzt wird, sein Vorstellungsvermögen zu aktivieren! Dazu ziehen wir es - zumindest vorübergehend - auf eine konstante Höhe auf:
.tictactoe .panel
{
    ...
    height: 100px;
}

Danach sitzt der Schalter verkehrt: Der wäre in der Mitte des Panels viel besser aufgehoben! Das geht horizontal trivial mit Zentrierung - gegenüber dem übergeordneten Container, also dem Panel. Außerdem sollte er auch noch vom oberen Rand her ein wenig nach unten gezogen werden, was leider nicht so einfach zu haben ist wie das horizontale Zentrieren, weil die vertikale Ausrichtung bei Fließtext-Einheiten (was der Schalter erstmal ist) nur UNTEREINANDER (also zwischen verschiedenen Buchstaben und Bildchen z.B.) gilt, nicht jedoch gegenüber dem beinhaltenden Container. Aber wir können ihn durch relative Verschiebung gegenüber seinem Stammplatz in die Bildmitte holen.

Außerdem könnte der seinen Hintergrund zugunsten unseres teuren Mahagonys aufgeben! Bei der Gelegenheit muß natürlich auch der Rahmen umgefärbt werden, ebenfalls auf Mahagony! Außerdem wirkt ein exakt eckiger Holz-Schalter albern: Der muß abgerundet sein, sonst würde er bei nächstbester Gelegenheit splittern!

Die schwarze Schrift wäre auf dem Mahagony-Hintergrund nicht mehr zu erkennen. Eine goldfarbene Schrift paßt dagegen perfekt. Und fett geschrieben sollte sie sein! Und nicht ganz so winzig!
.tictactoe .panel
{
    ...
    text-align: center;
}

.tictactoe .panel input
{
    position:       relative;
    top:            50px;

    background:     none;
    border:         2px outset #480915;
    border-radius:  5px;

    color:          #eb0;
    font-weight:    700;
    font-size:      20px;
}
OK, das entwickelt sich langsam... (Code)

Der Schalter sieht danach noch auf die Schrift zusammengequetscht aus. Wir sollten ihm ein wenig mehr Raum geben (padding)!
Außerdem werden fingergerechte Schalter (außer auf Tastaturen) abgerundet, was bei Beleuchtung zu einem leichten Farbgradienten führt... Damit DER uns nicht das Mahagony verkleistert, verwenden wir nur eine leichte, farbneutrale Abdunklung und Aufhellung: Schwarz bzw. Weiß mit wenig Deckungsvermögen auf einem transparent durchscheinenden Hintergrund... Dazu gibt es Farbdefinitionen, bei denen man die Transparenz einstellen kann, was "alpha"-Wert genannt wird.

Wer sich mit dem Thema "Farben" in CSS detaillierter auseinandersetzen möchte, findet in der CSS 3-Color-Referenz (auf englisch allerdings) eine vollständige Beschreibung samt reichhaltiger Beispiele und Querverweisen auf die Definition von "Farbräumen".

Bei der Gelegenheit könnten wir auch die Randzeichnung nochmal eine Note zurückpfeifen, die etwas übertrieben harte Lichteffekte zeigt. Auch hier hilft ein wenig Transparenz...

Es gehört sich wie immer, für aktivierbare Elemente eine optische Rückmeldung an den Benutzer zu geben - sowohl für die Benutzbarkeit ("hover") als auch für die tatsächliche Benutzung ("active"). Das machen wir natürlich ebenfalls mit Transparenz und Gradienten! Für das Berühren mittels einer Verstärkung der Aufhellung, für das Drücken mittels einer Invertierung derselben...
.tictactoe .panel input
{
    ...
    padding:        3px 25px;
    background:     none;
    background:     linear-gradient(top, rgba(255,255,255,0.2), rgba(0,0,0,0.1));
    background:     -o-linear-gradient(top, rgba(255,255,255,0.2), rgba(0,0,0,0.1));
    background:     -moz-linear-gradient(top, rgba(255,255,255,0.2), rgba(0,0,0,0.1));
    border:         2px outset rgba(72,9,21,0.6);
    ...
}
.tictactoe .panel input:hover
{
    background:     rgba(255,255,255,0.2);
    background:     linear-gradient(top, rgba(255,255,255,0.4), rgba(128,128,128,0.2));
    background:     -o-linear-gradient(top, rgba(255,255,255,0.4), rgba(128,128,128,0.2));
    background:     -moz-linear-gradient(top, rgba(255,255,255,0.4), rgba(128,128,128,0.2));
}
.tictactoe .panel input:active
{
    border:         1px inset #480915;
    background:     none;
    background:     linear-gradient(top, rgba(0,0,0,0.1), rgba(255,255,255,0.2));
    background:     -o-linear-gradient(top, rgba(0,0,0,0.1), rgba(255,255,255,0.2));
    background:     -moz-linear-gradient(top, rgba(0,0,0,0.1), rgba(255,255,255,0.2));
}
Das sollte in etwa SO aussehen... (Code)

An den redundant wiederholten (was sich leider nicht abschalten läßt) Definitionen mit browserspezifischen Präfixen für die Gradienten erkennen Sie, daß diese Eigenschaft eine noch nicht allgemein durchgesetzte CSS-Fähigkeit ist und noch dabei ist, bei den Browsern Einzug zu halten. In solchen Fällen ist es wichtig, das Design so zu gestalten, daß auch ohne diese Verhübschung der Sinn der Elemente Ihrer Bedienoberfläche eindeutig erkennbar bleibt und die Sache immer noch hinreichend ansprechend aussieht.

Tipp: Browserkompatibilität mit relativ jungen CSS-Effekten: Das setzen wir in unserem Fall dadurch um, daß wir
  1. EINFACHE Effekte zuerst definieren und danach die Verhübschungen.
  2. Standard-treue Definitionen zuerst definieren und danach die browserspezifischen, der Entwicklung vorweg laufenden Gimmicks
Für CSS-Geräte ist nach Standard zwingend vorgeschrieben, daß sie jene Definitionen, die sie NICHT verarbeiten können, zu übergehen haben. Wenn also an späterer Stelle eine hübschere Definition kommt, die ein Browser noch nicht beherrscht, setzt das eine früher erfolgte einfachere Definition nicht außer Kraft. Damit haben wir einen sogenannten "Fallback"-Mechanismus. Man kann ihn auch als Mechanismus für "Vorwärts-Kompatibilität" bezeichnen. Der ist definiert seit den allerersten zarten Anfängen von CSS: CSS 1: Forward-compatible parsing, in CSS 2 nur noch um die herstellerspezifischen Präfixe ergänzt...

So, das war die Übung zur Start-Taste. Wenn Sie Lust haben, können Sie jetzt noch ein bißchen Verhübschung oder Anpassung an eigene Schönheitsideale betreiben und sich untereinander dazu austauschen!

Was Sie nicht vergessen sollten, ist die inzwischen schon als Kinkerlitz anmutende Winzigkeit der PHP-Funktionalität auf Serverseite: Der Schalterklick soll natürlich auch zum Neustart führen. Und wenn die Spieler das Recht auf den ersten Zug regelmäßig tauschen sollen, muß dieses noch aus dem Request ausgelesen werden (gespeichert hatten wir es ja bereits vorsorglich in der Übung zum Spielablauf für zwei Spieler)!
Schauen Sie gegebenenfalls nochmal in den Beispielcode hinein!

Sooo... Die Starttaste hätten wir.
DAS war mal ein CSS-Marathon, oder?!

Ohne CSS wären wir aber nie zum schicken Aussehen gekommen. Und nachdem wir jetzt einmal die CSS-Definitionen entwickelt haben, gelten die natürlich automatisch für jeden weiteren Schalter, den wir mit einem simplen HTML-input-Element irgendwo ins Panel einsetzen! Deshalb wird sich die Investition lohnen.

Tipp: Wiederverwendbarkeit: Weil wir hier optische Effekte vorrangig mit Transparenz-Effekten (leichtes abdunkeln bzw. aufhellen) erzeugt haben, können Sie diese Effekte ohne jede Änderung auf vollkommen andere Projekte übertragen. Oder sich eine ganz globale CSS-Datei für Ihren Webserver anlegen, in der Sie diese Effekte für alles bereitstellen, was Sie jemals in Zukunft schreiben werden. Man wechselt dann bloß noch den Hintergrund aus und alle CSS-Effekte funktionieren auf Anhieb in jeder Design-Umgebung!

Eine letzte Kleinigkeit können wir uns heute noch zutrauen:
Wir könnten (und sollten) anzeigen lassen, wer gerade dran ist!
Weil zum Beispiel der Stein-Cursor (noch) nicht von allen Browsern unterstützt wird. Und deren Benutzer erstmal noch nichts weiter sehen als den Hand-Cursor...

Anzeige des Spielers am Zuge
Das ist jetzt wieder eine Sache, die einen extrem gestaffelten Schwierigkeitsgrad hat:
  • In PHP ist da nichts weiter als der Wert der Variablen "dran" dran: '1' oder '2'.
  • In HTML werden wir gleich zwei <div>'s mit <img> hinsetzen und gut ist.
  • In CSS werden wir nochmal eine Orgie erleben. Aber keine Angst: Das überleben Sie!

Falls Sie sich noch an das eingangs gezeigte Ziel erinnern: Da waren zwei Anzeigen im Panel unten zu sehen, in denen jeweils ein Stein der jeweiligen Farbe eingesetzt war und die von unten (ringförmig) beleuchtet wurden, wenn der Spieler mit dieser Farbe dran ist.

Dieses Design ist nun nicht zwingend. Wenn Sie ganz andere Ideen haben, sind die genauso gut. Ich zeige Ihnen an dem Beispiel nur nochmal ein paar CSS-Effekte und dann dürfen Sie nach eigenem Gutdünken das so anwenden oder auch nicht - ganz nach individueller Note!

In HTML fügen wir zwei trivial einfache Elemente ein: Unsere Steinchen als Bild und drumherum jeweils noch ein <div>, damit wir dem Zeug einen Rahmen (bzw. Leuchteffekt) geben können...
...Wobei wir den dran seienden Spieler durch eine extra Klasse namens "dran" kennzeichnen lassen, die wir in bereits bekannter Manier in den HTML-Code einsetzen. Damit das nicht zu kompliziert zu lesen ist, habe ich hier das PHP-Konstrukt zur HTML-Erzeugung zum Teil aus dem HTML-Code herausgezogen...
<?php
$dran_1 = ($dran == '1' ? ' dran' : '');
$dran_2 = ($dran == '2' ? ' dran' : '');
?>
<div class="panel">
    <div class="lamp black<?=$dran_1?>"><img src="Hut-schwarz.png" /></div>
    <div class="lamp white<?=$dran_2?>"><img src="Hut-weiß.png"    /></div>
    <input type="submit" name="start" value="Start!" title="Spiel (neu) starten!"/>
</div>

...Dabei wird hier mal eine Alternative Form von Fallunterscheidung benutzt, die gegenüber einer Formulierung mit "if" deutlich kürzer und leserlicher ausfällt - wenn einer von zwei Werten ausgewählt werden soll (ein Auswahl-Operator):
(<bedingung> ? <true-Wert> : <false-Wert>)
Statt
$dran_1 = ($dran == '1' ? ' dran' : '');
hätte man sonst schreiben müssen:
if ($dran == '1')
    $dran_1 = ' dran';
else
    $dran_1 = '';
Mit dem Auswahl-Operator ist das doch einfacher formulierbar.

...und irgendwas hatte mich geritten, hier englische Bezeichner für die Klassen zu verwenden. Na, egal, Sie dürfen natürlich Namen wählen, wie immer Sie lustig sind. Sinnvoll allerdings ist es, sie "sprechend" zu machen (das sollte sich so langsam zur Selbstverständlichkeit entwickeln)!

Wobei...Vielleicht wäre hier nochmal ein kleiner Abstecher sinnvoll:
Tipp: Sprechende Namen:
Wenn Sie Ihre Variablen (und später Funktionen) "sprechend" benennen wollen, empfehle ich, insbesondere die IDEE dessen, wozu sie dienen, vordergründig zur Namensvergabe zu verwenden. Nicht so wichtig sind Details, die Sie eh trivial aus dem Programmkontext herauslesen können. Daß zum Beispiel eine Variable, die einen Namen für irgendwas speichert, einen String darstellt, muß man nicht unbedingt durch eine extra Vorsilbe betonen, wenn der restliche Name schon kaum noch Interpretationsspielraum läßt und die Anwendung ebenso wenig.

Etwas anders kann das sein, wenn Sie eine Programmiersprache vor sich haben, die Ihnen einen extremen Interpretations- und Behandlungsspielraum läßt wie z.B. C++: Wenn eine Operation auf zwei, drei oder mehr verschiedene Ebenen der Daten wirken kann, die eine Variable repräsentiert, dann bleibt gar nichts anderes übrig, als genauer zu kennzeichnen (und/oder mit Kommentaren zu beschreiben), was man mit einer Variablen in einem bestimmten Kontext gerade vorhat. Wobei: Auch da kommt es letztlich vor allem eben auf die im aktuellen Kontext vorherrschende IDEE der Verwendung an, die den Namen bestimmen sollte.

Alle Namensschemen, die aus purer Sturheit und Prinzipienreiterei mit Gewalt und Gedankenlosigkeit durchgezogen werden, bringen einem Programmierer nur den Effekt von Mehraufwand beim Schreiben und Lesen.

In diesem Fall wollen wir also eine Lampe bauen für den schwarzen und den weißen Spieler bzw. Stein. Wobei: Wenn wir das Zeug soweit anzeigen lassen, sieht es erstmal -> chaotisch aus... (Code):
Die Steine werden in der Mitte (zentriert, wie wir eingestellt hatten) und untereinander (raumgreifend - jeder in einer eigenen Zeile) angezeigt und unter ihnen weit nach unten rausgeschoben der Schalter.
Aufgabe: Ausprobieren! Ab in Ihren Code damit!

Das soll ja so nicht sein... Die Lampen sollen rechts und links an die Seite! Außerdem sind die viel zu groß für die Anzeige: Da reicht die Hälfte der Größe dicke aus! Und der Schalter soll wieder in der Mitte auf dem Panel auftauchen!
Zum Zurechtrücken haben wir mehrere Möglichkeiten:
  • Wir könnten die Lampen zur Seite "schweben" lassen (englisch: "float").
  • Wir könnten die Lampen außerdem in Bezug zum Panel auf frei definierte Positionen relativ zu den Rändern des Panels schieben (was man "absolut" positionieren nennt).
  • Wir könnten die Lampen und den Schalter in eine Tabelle packen, wo jeder seine eigene Spalte bekommt.
Wir haben mal wieder die Qual der Wahl... Aus Sicht des Kompromisses aus sofortigem Aufwand und leichter Änderbarkeit empfehle ich einfach mal die Version 2: absolut positionieren relativ zum Panel!
Und das geht SO...
.tictactoe .panel
{
    position:       relative;
    ...
}

...

.lamp
{
    position:       absolute;
    top:            20px;
}
.lamp.black {left:  20px;}
.lamp.white {right: 20px;}

.lamp img
{
    width:          50px;
    height:         50px;
}
Aussehen tut das dann in etwa SO... (Code)

Fehlt noch die Beleuchtung um die Steine herum... Wir wollen ja irgendwie den "aktiven" (dran seienden) Spieler sichtbar machen. Alternativ könnten wir auch den (oder die) nicht aktiven Steine(e) angegraut darstellen (graue Scheibe mit alpha 50% und Farbwerten um 128 drübergelegt), aber das wird nur komplizierter. Eine Beleuchtung außen herum ist wirklich die einfachste Methode...

Wir haben die Steinchenbilder ja schon vorsorglich in <div>'s verpackt. Die <div>'s brauchen bloß einen farbig leuchtenden Hintergrund, zum Beispiel gelb (mit ein ganz klein wenig Touch ins orangene hinein, weil das zum Mahagony paßt). Und zwar genau dann, wenn der Spieler dran ist:
.lamp.dran
{
    background:    #fc0;
}
Das sieht etwa SO aus... (Code)

...und ist natürlich nicht das, was wie haben wollen: Bei all den schönen Rundungen ein eckiges Hintergundlicht ist unpassend! Um es abzurunden, können wir den Rand der Leuchtfläche rund machen. und zwar auf den Radius des Steines, damit er nicht bloß ein bißchen abgerundet aussieht. Etwa SO... (Code)
.lamp
{
    ...
    border-radius: 25px;
}

Aber halt! Dann verschwindet das Licht ja (fast) vollständig! ein Streifen von einigen Pixeln Breite sollte für das Licht schon übrig bleiben. Das machen wir mit einem Ausdehnen des Randbereiches mittels "padding". Also nochmal präzisiert:
.lamp
{
    ...
    border-radius: 30px;
    padding:       5px;
}
...mit DIESEM Ergebnis... (Code)

Der Leuchtring könnte abschließend noch eine Vertiefung gebrauchen, damit der optische Eindruck, daß es sich bei ihm um eine Hintergrund-Beleuchtung (von unterhalb des Steines, aus dem Inneren des Panels heraus) handelt, glaubwürdiger rüberkommt:
.lamp
{
    ...
    border:        2px inset #480915;
}
Fertig! (Code)



Wer Lust hat, kann nochmal etwas individuellen Feinschliff mit Effekten ausprobieren. Mir war es zum Beispiel lieber, wenn die Kante vom Spielfeldrand konsequent am Spielfeld entlang läuft und nicht das Panel mit einschließt. Dazu ist allerdings ein logischer Rahmen nochmal extra um beides (Spielfeld und Panel) herum notwendig, damit das Panel nicht raumgreifend links und rechts am Seitenrand anklatscht. Und die goldene Schrift sieht mit ein wenig (1 Pixel) Vertiefung etwas hübscher aus. Und der halbtransparente Rahmen des Startschalters sieht natürlich bei den anderen Elementen (Tabelle und Anzeige-Lampen) genauso hübsch aus.

Das sind aber alles nur noch Spielereien, die mit der Funktionalität nichts mehr zu tun haben. Die sind komplett freiwillig und optional und individuell.

Das Endergebnis für den heutigen Tag kann dann zum Beispiel SO aussehen... (Code)
Aufgabe: Abschließend verlinken Sie noch diesen Zustand in Ihrem Lehrgangs-Inhaltsverzeichnis!

Endzustand heute
Wenn Sie jetzt mal so ein paar Spielchen spielen, fallen natürlich sofort die nächsten notwendigen Ergänzungen ins Auge:
  • Wir brauchen noch eine Sieg- und Remis-Auswertung!
  • Die Siege und Remis sollten über mehrere Runden hinweg zusammengezählt werden können!
  • Es wäre nett, wenn zwei Spieler ihre Name irgendwie eintragen könnten, damit nicht die Gefahr besteht, versehentlich schwarz und weiß zu verwechseln (OK: zu zweit vor einem Computer vielleicht weniger das Problem, aber in einer offenen Multiplayer-Umgebung doch recht zwingend notwendig).
  • Während der Vorbereitung (bis die Spieler sich eingetragen haben) sollte das Spielfeld noch nicht aktiv sein, damit man sich nicht versehentlich verklicken kann und ein neues Spiel immer konsistent auf die gleiche Art und Weise gestartet wird - durch Drücken des Start-Knopfes (was nach Abschluß eines Spieles zum Start der nächsten Runde auch notwendig ist, damit das Spielfeld wieder in einen Grundzustand versetzt wird)!
  • Beim Speichern von Spielständen (durch Anlegen von Lesezeichen) fehlt im Moment noch der Bedienkomfort: Man muß sich immer einen Namen für das Lesezeichen ausdenken. Sobald wir Spielernamen verfügbar haben, können wir das viel leichter machen, indem wir einen extra Link für ein Lesezeichen in die Seite einbetten, der den Titel bereits in sich trägt (ja: Sie ahnen es schon: mit dem "title"-Attribut).
Diese Dinge nehmen wir uns in der nächsten Lektion vor.

Gratis: Simultanspiel
Nebenbei: Können Sie eigentlich auch schon simultan spielen? Gleichzeitig gegen mehrere Gegner?
...Ja, OK, noch haben wir keine echte Multiplayer-Fähigkeit eingebaut, aber man kann ja ein bißchen mit Vorstellungsvermögen nachhelfen: Sich einzubilden, die Steine der einen Farbe seien die eigenen, die der anderen Farbe seien die eines Gegners, ist ja nun keine Hürde. Und darauf aufbauend sich vorzustellen, wenn man mehrere Fenster (Tabs) mit verschiedenen Spielen offen hat, daß man gegen mehrere Gegner simultan spielt, sollte dann auch nicht mehr schwer sein...

Probieren Sie das mal!
Funktioniert es?

Ja: Sie haben durch die vollständige Speicherung des Spielzustandes in der HTML-Seite (bei uns: im Formular) nicht nur vom Browser gratis ein Undo-/Redo-System spendiert bekommen, sondern auch noch ein Simultanspiel-System für das gleichzeitige Spielen gegen mehrere Gegner. Obwohl wir für Multiplayer wie auch für Undo selber noch keinen Finger krumm gemacht haben!



Impressum
Email
aktualisiert: 2013-02-11 22:06