kontakt

+49 421 20696 - 0
info@team-neusta.de

Programmierspiele im Browser – HTML5 macht’s möglich

Ein Programmierspiel ist ein Computerspiel, bei dem die Steuerung der Spielfiguren Programme (Bots) übernehmen. Die Spieler spielen nicht direkt gegeneinander, sondern sie schreiben Programme, die gegeneinander spielen. Neben dem Spaß am Programmieren werden solche Spiele zum Weiterentwickeln und Testen von KI-Strategien und zum spielerischen Erlernen von Programmiersprachen eingesetzt.

Grundsätzlich kann man jedes Spiel auch als Programmierspiel realisieren. Bekanntes Beispiel sind Schachprogramme. Aber auch Fußball kann man in den RoboCup Simulation Leagues virtuell mit Programmen spielen. Den klassischen Computerspielen nachgebildet sind Robocode und TORCS. Hier bekämpfen sich Panzer oder fahren Formel-1-Rennwagen um die Wette.

Solche Programmierspiele sind meist in Java, C++ oder C# geschrieben. Um daran teilzunehmen, muss man eine Spielumgebung auf dem eigenen Rechner installieren. Dort kann man Bots programmieren und gegen schon bekannte andere Bots antreten lassen. Möchte man an Wettkämpfen teilnehmen, lädt mein seine Bots auf der Wettkampfseite hoch. Auf ausgewählten Rechnern spielt dann der eingereichte Bot gegen seine Konkurrenten. Die Ergebnisse werden anschließend im Internet veröffentlicht.

Programmierspiele im Browser auf Basis von JavaScript gibt es bisher nicht. Es stellt sich die Frage, ob das grundsätzlich nicht möglich ist oder bisher einfach nur nicht umgesetzt wurde. Um diese Frage zu beantworten, muss man sich die Anforderungen an Programmierspiele genauer anschauen. Folgende Voraussetzungen müssen erfüllt sein:

  • Bots müssen isoliert voneinander und der Spielumgebung laufen.

  • Spielumgebung und Bots sollten parallel ausgeführt werden.

  • Spielumgebung und Bots müssen in einer genau definierten Weise miteinander kommunizieren.

  • Bots sollten sowohl vom Server als auch lokal geladen werden können.

  • Das Spiel sollte grafisch dargestellt werden.

Mit klassischem JavaScript lassen sich diese Anforderungen kaum erfüllen. Doch modernes JavaScript rund um HTML5 stellt das notwendige Rüstzeug zur Verfügung. Zentrales Feature sind dabei Web Worker. Mit ihnen kann man Bots isoliert und parallel laufen lassen. Dabei lässt sich die Kommunikation zwischen Web Workern und Spielumgebung genau definieren. Mit der File-API können Bots lokal geladen werden, und mit Canvas wird die grafische Darstellung realisiert.

DER WEB WORKER CONTEST

Die genaue Umsetzung dieser Features möchte ich an einem konkreten Projekt, dem WEB WORKER CONTEST, darstellen. Mit dem CONTEST habe ich – unterstützt durch team neusta – im Mai 2014 das wahrscheinlich erste browserbasierte JavaScript-Programmierspiel gestartet. Das Spiel war eine einfache mathematische / algorithmische Herausforderung: Von einem zufälligen Startpunkt aus musste man mit einfachen Zügen (nach links, rechts, oben oder unten) eine Spielfläche von 100 x 100 Feldern besetzen. Felder, die der Gegner schon besetzt hatte, durfte man nicht mehr betreten. Felder, die man selber zuerst besetzt hatte, durfte man weiterhin betreten.

Die Spielumgebung meldete nur zurück, ob der gewünschte Zug möglich (das Feld ist frei oder wurde selber schon besetzt) oder nicht möglich (das Feld wurde schon vom Gegner besetzt oder es wurde versucht, die Spielfläche zu verlassen) war. Es wurde nicht abwechselnd gezogen, sondern so schnell, wie man konnte. Es kam also darauf an, schnelle und intelligente Züge zu machen. Der CONTEST entwickelte sich zu einem lebendigen Wettkampf. Über 200 Programmierer mit über 700 eingereichten Bots kämpften 4 Wochen lang um die beste Strategie. heise Developer bereichtete ausführlich.[1]

WEB WORKER

Klassisches JavaScript ist eine Single-Threaded-Umgebung. Der Code wird nacheinander abgearbeitet. Hier setzen die um 2009 eingeführten Web Worker an. Sie erlauben es, JavaScript parallel auszuführen. Umfangreiche Berechnungen können so im Hintergrund ausgeführt werden, ohne die eigentliche Webanwendung zu blockieren. Worker sind von Haus aus threadsicher. Der Worker-Code ist vom Hauptprogramm komplett isoliert. So gibt es keine (nicht statischen) Objekte, auf die sowohl das Hauptprogramm als auch ein Worker gemeinsam Zugriff haben. Insbesondere können Worker nicht auf das DOM oder das window-Objekt zugreifen. Hauptprogramm und Worker kommunizieren ausschließlich über Messages. Dabei können einfache Strings oder JSON-serialisierte Objekte übergeben werden.

Durch diese beiden Eigenschaften – die parallele und isolierte Verarbeitung – eignen sich Web Worker[2] ideal dazu, Programmierspiele zu realisieren. Man lädt Bots als Web Worker und lässt sie mit der Spielumgebung über die genannten Messages kommunizieren. Die Bots können so der Spielumgebung ihre Züge mitteilen, und die Spielumgebung kann den Bots Informationen über das Spiel (z.B. Züge des Gegners oder Positionen von Spielobjekten) liefern. Durch die parallele Verarbeitung sind die Bots immer in Aktion. Und durch die isolierte Verarbeitung ist sichergestellt, dass die Bots weder die Spielumgebung noch andere Bots manipulieren können.

Schauen wir uns das am Beispiel des WEB WORKER CONTESTS genauer an[3]. Zunächst muss man die Kommunikation zwischen Bot und Spielumgebung definieren. Ein Bot kann sich nach oben, unten, links oder rechts bewegen. Dementsprechend definieren wir eine direction – Eigenschaft mit den Werten up, down, left oder right. Die Spielumgebung teilt dem Bot nur mit, ob der Spielzug möglich war oder nicht. Hier wird eine success – Eigenschaft mit den Werten true oder false definiert. Damit sieht der Code die Spielumgebung wie folgt aus:


 var bot1 = new Worker('bot1.js');
 bot1.onmessage = function(event) {
    var direction = event.data.direction;
    var success = makeMoveBot1(direction);
    bot1.postMessage({
       success: success
    });
 };

Dabei ist makeMoveBot1() eine Funktion, die den eigentlichen Zug ausgeführt. Der Bot (in der Datei bot1.js) muss nun einen äquivalenten Code implementieren:


 onmessage = function (event) {
    var success = event.data.success;
    var direction = makeMove(success);
    postMessage({
       direction: direction
    });
 };

Hier ist makeMove() eine Funktion, die den nächsten Zug berechnet. Implementiert man den Code auch noch für einem zweiten Worker und stößt das Spiel initial mit


 // analog für bot2 
 bot1.postMessage({ 
    success: true
 }); 

an, ist das Spiel schon in vollem Gange. Der Bot macht einen Zug und teilt diesen per postMessage der Spieleumgebung mit. Diese empfängt den Zug mit onmessage, verarbeitet ihn und teilt dem Bot per postMessage das Ergebnis mit. Der Bot empfängt das Ergebnis mit onmessage, und ist wieder am Zug.

Lokale Worker

Programmierspiele im Browser sollten Bots sowohl vom Server als auch lokal laden können. So kann man Bots, die am CONTEST teilnehmen, gegeneinander antreten lassen und eigene Bots bequem lokal entwickeln.

Im obigen Beispiel wird der Bot klassisch mit new Worker('bot1.js') vom Server geladen.

Das lokale Laden lässt sich mit der neuen File API elegant in vier Zeilen Code lösen. Zunächst muss man die lokale Datei auswählen. Das ist mit einem input-Tag vom Type file leicht möglich. Man erhält ein Eingabefeld mit dem man lokale Dateien auswählen kann. Über input.files[0] steht die Datei als File-Objekt zur Verfügung. Mit der statischen Methode URL.createObjectURL() erzeugt man aus dem File-Objekt eine sogenannte object URL. Eine solche URL zeigt auf eine lokale Datei. Sie kann überall dort verwendet werden, wo normale URLs eingesetzt werden. Nun muss man nur noch mit new Worker(objectURL) den lokalen Worker erzeugen und mit URL.revokeObjectURL(objectURL) die URL wieder freigeben. Zusammengefasst ergibt sich folgender Code für das Laden von lokalen Workern:


 <!-- HTML -->
 <input id='local-worker' type='file'>

 // - JavaScript -
 var localeWorkerFile = document.getElementById('local-worker').files[0];
 var objectURL = window.URL.createObjectURL(localeWorkerFile);
 var localeWorker = new Worker(objectURL);
 window.URL.revokeObjectURL(objectURL);

Canvas

HTML5 bietet mit dem Canvas-Element umfangreiche Möglichkeiten zum Zeichnen mit JavaScript. Verändert man die Zeichenfläche im zeitlichen Verlauf, entstehen bewegte Bilder.

Beim WEB WORKER CONTEST müssen quadratische Felder einer 100×100 Fläche nach und nach mit zwei Farben eingefärbt werden. Ein Feld soll dabei 5 x 5 Pixel groß sein.

Hat man im HTML ein Canvas hinterlegt, muss man sich aus diesem zunächst einen Context erzeugen. Dieser stellt die notwendigen Zeichenoperationen zur Verfügung. Wir müssen jeweils eines der 100 x 100 quadratischen Felder einfärben. Dies erledigt – siehe unten – die Methode draw(). Sie legt die Zeichenfarbe fest und färbt damit ein quadratisches Feld von 5×5 Pixeln an der gewünschten Stelle ein.

Die draw-Methode muss jetzt nur noch periodisch mit dem jeweils nächsten Zug aufgerufen werden. Dies erledigt die klassische Methode setInterval(). Sie führt die ihr übergebene Funktion periodisch aus. nextMove() ist dabei eine Methode, die den nächsten Zug mit den Koordinaten x, y und der Farbe color liefert.


 <!-- HTML -->
 <canvas id='canvas' width='500' height='500'>

 // - JavaScript -
 var canvas = document.getElementById('canvas');
 var context = canvas.getContext('2d');
 
 var draw = function (x, y, color) {
    context.fillStyle = color;
    context.fillRect(5 * x, 5 * y, 5, 5);
 };

 var interval = 1; // Zeitintervall in Millisekunden
 setInterval( function() {
    var move = nextMove(); // gibt den nächsten Zug zurück
    draw(move.x, move.y, move.color);
 }, interval);

YouTube Preview Image

Ausblick

Mit dem vorgestellten Code haben wir das Grundgerüst für Programmierspiele im Browser dargestellt. Kernstück sind dabei Web Worker. Sie ermöglichen es, Bots isoliert und parallel zur Spielumgebung auszuführen. Dabei können die Bots sowohl vom Sever als auch – mit der File API – lokal geladen werden. Die grafische Darstellung des Spiels gelingt problemlos mit Canvas. All diese Techniken lassen sich mit wenigen Zeilen Code umsetzen. Die Herausforderung besteht darin, das eigentliche Programmierspiel zu entwerfen und umzusetzen.


Quellennachweis:

2. Genauer gesagt handelt sich hier um Dedicated Web Worker. Daneben gibt es noch Shared Web Worker. Shared Web Worker können unter der Voraussetzung der same origin police mit mehreren Tabs gleichzeitig kommunizieren. So können zum Beispiel Warenkörbe in unterschiedlichen Tabs abgeglichen werden.

3. Der CONTEST-Code findet sich auf GitHub. Die hier dargestellten Codeausschnitte wurden auf das Wesentliche reduziert.

Share

Kommentare