SWTPra-Team 4 - Heinz Nixdorf Institut
Transcription
SWTPra-Team 4 - Heinz Nixdorf Institut
Heinz Nixdorf Institut Fachgruppe Softwaretechnik Zukunftsmeile 1 33102 Paderborn Analyse- und Entwurfsdokument im Rahmen des Softwaretechnikpraktikums 2014 Team 04 Inhaltsverzeichnis 1 Analyse 1.1 1.2 1.3 1.4 1.5 2 Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.1 Modularer Aufbau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2 Server-Client-Architektur . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.3 Model-View-Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.4 Observer-Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.5 Strategy-Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Werkzeuge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1 Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.2 Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.3 Graphiti und Graphical Editing Framework ersetzt durch SWT und SVG 1.2.4 Eclipse Modeling Framework . . . . . . . . . . . . . . . . . . . . . . . 1.2.5 JavaScript Object Notation . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.6 Plug-in Development Environment . . . . . . . . . . . . . . . . . . . . . Gamekonfigurator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1 Konfiguration erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.2 Analyse-Klassendiagramm . . . . . . . . . . . . . . . . . . . . . . . . . GameEngine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.1 Spiel erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.2 Spiel kontrollieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.3 Zug überprüfen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4 Analyse-Klassendiagramm . . . . . . . . . . . . . . . . . . . . . . . . . Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.1 Mit dem Server Verbinden . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.2 Spiel abschließen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.3 Zug durchführen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.4 KI-Spieler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.5 Analyse-Klassendiagramm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Entwurf 2.1 2.2 2.3 II 1 Das Game Model . . . . . . . . . . . . . . . . 2.1.1 Game Model Mapping . . . . . . . . . Der Spielkonfigurator . . . . . . . . . . . . . . 2.2.1 de.scrabblenauts.configurator.view . . . 2.2.2 de.scrabblenauts.configurator.controller 2.2.3 de.scrabblenauts.gamemodel . . . . . . Die Game Engine . . . . . . . . . . . . . . . . 2.3.1 de.scrabblenauts.server.view . . . . . . 2.3.2 de.scrabblenauts.server.servercontroller 2.3.3 de.scrabblenauts.server.network . . . . 2.3.4 de.scrabblenauts.server.gamecontroller 1 1 2 3 3 4 5 5 5 5 6 6 6 7 7 7 7 7 9 9 12 13 13 14 14 17 17 20 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 21 21 22 23 23 23 23 23 25 25 2.4 2.5 Der Smartphone-Spieler . . . . . . . . . . . . . 2.4.1 de.scrabblenauts.player.controller . . . . 2.4.2 de.scrabblenauts.player.view . . . . . . . 2.4.3 de.scrabblenauts.player.network . . . . . 2.4.4 de.scrabblenauts.player.data . . . . . . . Der KI-Spieler . . . . . . . . . . . . . . . . . . 2.5.1 Klassendiagramm . . . . . . . . . . . . . 2.5.2 de.scrabblenauts.player.ai . . . . . . . . 2.5.3 de.scrabblenauts.player.ai.movegenerator 2.5.4 de.scrabblenauts.player.ai.strategy . . . . Abbildungsverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 26 27 28 28 28 28 28 29 30 32 III 1 Analyse In der Analyse werden die im Pflichtenheft abgesegneten Aufgabenbereiche genauer untersucht. Zunächst wird die Softwarearchitektur für die Programmteile festgelegt und deren Konsequenz erläutert. Bewährte Design-Pattern werden vorgestellt, die sich für die spätere Implementierung nützlich erweisen werden. UML-Diagramme helfen eine Übersicht über die zu entwickelnden Komponenten und deren Zusammenspiel zu bekommen. Ein weiterer Abschnitt verschafft einen Überblick über die benötigten Werkzeuge, die in diesem Projekt Anwendung finden. Zum Schluss werden die aufgelisteten Komponenten in ihre Objektstruktur dekompositioniert. Dies bedeutet insbesondere, dass ihre Struktur analysiert und ihr Verhalten dargestellt wird. 1.1 Architektur In diesem Kapitel wird die Softwarestruktur des Projekts skizziert. Eine Softwarearchitektur beschreibt das Grundgerüst, in dem sich die einzelnen Implementationsklassen eingliedern und zu einer funktionstüchtigen Komponente werden. 1.1.1 Modularer Aufbau Wie dem Pflichtenheft zu entnehmen ist, besteht das Projekt aus insgesamt fünf Bestandteilen. Diese setzen sich aus Game Engine, Spielkonfigurator, Smartphone-Beobachter, Smartphone-Spieler (bereitgestellt als eine Smartphone-App) und KI-Spieler zusammen. Da diese alle unterschiedliche Schnittstellen bereitstellen bzw. konsumieren, bietet sich ein modularer Aufbau als Grundstruktur an. Dies bedeutet insbesondere, dass alle fünf Komponenten für sich alleine lauffähige Software darstellen, die ohne die restlichen Komponenten aber nur teilweise funktionstüchtig sind. Die nur in den Schnittstellen abhängigen Komponenten bieten die Möglichkeit, bei veränderten Anforderungen nur änderungsrelevante, also spezifische Teile austauschen zu müssen. Dabei wird nicht die ganze Anwendung verändert, sodass eine Wartung deutlich vereinfacht wird. Abbildung 1.1 zeigt eine beispielhafte Verteilung der fertigen Software auf die benötigten Geräte. Dabei wird wie bereits im Pflichtenheft eine Unterteilung der Geräte in Server und Client vorgenommen. So befindet sich die Game Engine Komponente als zentrale Steuereinheit auf dem Server, während Smartphone-Beobachter und -Spieler, als zusammengefügtes Paket, nur auf ein Android Gerät aufgespielt werden können. Sie stellen die Seite des Clients dar. Smartphone-Beobachter und -Spieler werden bei der Bereitstellung zu einer Anwendung (App) gebündelt, um Codestrukturen, die für beide Komponenten benutzt werden können, optimal wiederzuverwenden. Der Spielkonfigurator wurde hier exemplarisch auf der Seite des Servers platziert, da die von ihm zu erstellende Konfiguration des Spiels zunächst einmal die Game Engine betrifft. Es wäre aber auch denkbar, diese Komponente auf einem anderen PC auszuführen und die Konfigurationsdatei per Dateitransfer für die Game Engine zugriffsbereit zu machen. Näheres dazu befindet sich in Abschnitt 1.1.2. Ähnlich zum Spielkonfigurator, hat auch der KI-Spieler kein festes Gerät. Er könnte sich auch direkt auf dem Server ausführen lassen und damit keinen eigenen PC beanspruchen wie in Abbildung 1.1 dargestellt. Insgesamt zeigt dies den groben Grundaufbau des Systems. Wir unterscheiden zu jeder Zeit die Seite des Servers von der Seite des Clients. Somit ist die Hauptarchitektur in unserem modularen System eine Server-Client-Architektur. 1 Abbildung 1.1: UML-Verteilungsdiagramm der Komponenten 1.1.2 Server-Client-Architektur Bei der Server-Client-Architektur, oder auch Zwei-Schichten-Architektur genannt, handelt es sich um eine Softwarestruktur für verteilte Anwendungen. Dabei nimmt der Teil der den Serverteil repräsentiert eine eher nicht interaktive Rolle als Dienstleister ein. Er steuert den Ablauf und die Geschäftsprozesse und validiert sie nach Gültigkeit. Ihm kommt im Netzwerk die Aufgabe zu, die einzelnen Clients zu steuern. Der Teil der Clients ist in der Regel hoch interaktiv mit dem Benutzer des Systems. Hier werden Eingaben des Benutzers entgegen genommen und zur Validierung an den Server gesendet. Gibt dieser sein OK, so hat der Benutzer eine erlaubte Handlung vollzogen. Abbildung 1.2: UML-Komponentendiagramm der Scrabble-Variante Abbildung 1.2 bezieht diese Grundstruktur nun auf unsere Scrabble-Variante. Dabei sind insgesamt drei Schnittstellen geplant. IServer Die Komponente, die diese Schnittstelle bereitstellt, platziert sich auf der Serverseite des Systems. Sie ist in der Lage die Steuerung des Systems zu übernehmen. Sie entscheidet über gültige Spielzüge und fordert den Datenverkehr über das Netzwerk an. Komponenten, die diese 2 Schnittstelle konsumieren, sind dagegen auf der Client-Seite anzusiedeln. Sie können mit der bereitstellenden Komponente kommunizieren und ihre Anforderungen erfüllen. IViewer Komponenten, die diese Schnittstelle bereitstellen, sind in der Lage ein laufendes Spiel zu be- obachten. Sie bekommen von der Serverkomponente die benötigten Informationen bereitgestellt und sind damit in der Lage das Spiel auf einem Ausgabegerät darzustellen. Die konsumierende Komponente nimmt den Wunsch ein Spiel zu beobachten auf und entscheidet, ob dies zugelassen wird. Sie stellt die benötigten Informationen für die Beobachtung bereit. IPlayer Ähnlich wie die IViewer-Schnittstelle, können bereitstellende Komponenten der IPlayer- Schnittstelle Spielen beiwohnen. Der Unterschied besteht im Wesentlichen darin, dass diese Komponenten nun interaktiv mit dem Server kommunizieren, um das eigentliche Spiel zu spielen. Die konsumierende Komponente kann, wie beim IViewer, den Wunsch des Mitspielens erlauben oder verweigern. Erlaubt sie den Zugriff muss sie die Spielzüge entgegennehmen und den aktuellen Status des Spiels mitteilen. Näheres zu den Schnittstellen befindet sich im Interface-Dokument. Abbildung 1.2 zeigt nun bereitstellende und konsumierende Komponenten des Systems. Dabei stellt die Game Engine die IServer-Schnittstelle bereit. Diese wird von allen Komponenten der ClientSeite konsumiert, welche wiederum entweder die Schnittstelle IViewer oder IPlayer bereitstellen. Diese Schnittstellen werden dann von der Game Engine konsumiert um die benötigten Informationen bereitzustellen. Als letztes zeigt Abbildung 1.2 die Komponente Spielkonfigurator. Sie zeichnet sich im Besonderen dadurch aus, dass sie keine Programmierschnittstelle anbietet. Ihre Ausgabe ist eine Spielkonfiguration im ECore-Format, welche als Datei exportiert und von der Game Engine importiert werden kann. 1.1.3 Model-View-Controller Mit dem Modell der Server-Client-Architektur ist nun der erste wichtige Baustein für das System gelegt. Trotzdem garantiert dieses Modell noch keine einfache Wartbarkeit und Testmöglichkeit. Darum erweitern wir die Grundarchitektur um das bewährte Modell der Model-View-Controller-Architektur. Diese Architektur soll in den einzelnen Komponenten Anwendung finden, die zunächst erst grob von der Server-Client-Architektur unterschieden worden waren. Das grundlegende Modell, welches in Abbildung 1.3 gezeigt wird, unterscheidet in drei Arten von Klassen: Model, View (später auch Boundary genannt) und Control. Dabei hat jeder Typ seine dedizierte Aufgabe. So hält eine Klasse vom Typ Model nur Daten, die von der View auf einem Ausgabegerät dargestellt werden können. Eine Klasse vom Typ Control kontrolliert den Zugriff auf die Daten und kann sie bei Aufforderung verändern. Dies führt, wie in Abbildung 1.3 dargestellt, zu einer Veränderung im Model, welches eine Klasse vom Typ View veranlasst, die Darstellung zu ändern. Um diese Architektur lauffähig und effizient zu halten, sollte jede Klasse auf ihren Typ reduziert werden und ihren Aufgabenteil erfüllen. Eine Vermischung von zwei, oder gar allen drei Typen, ist nicht vorgesehen. 1.1.4 Observer-Pattern Das Observer-Pattern, oder auch Beobachter genannt, ist ein bewährtes Design-Pattern aus der Softwaretechnik und gehört zu der Art der Verhaltensmuster. Zum Zweck des Observer-Patterns schrieben Gamma, Helm, Johnson & Vlissides (2004): „Definiere eine 1-zu-n-Abhängigkeit zwischen Objekten, so daß [sic] die Änderung des Zustands eines Objektes dazu führt, daß [sic] alle abhängigen Objekte benachrichtigt und automatisch aktualisiert werden.“ Es fügt sich nahtlos in die Architektur von Model-View-Controller ein, da auch hier dessen Typen exakt unterschieden werden müssen. 3 Abbildung 1.3: Modell des Model-View-Controller (von http://en.wikipedia.org/wiki/File:MVCProcess.svg [Zugriff am 13.05.2014]) Abbildung 1.4: Struktur des Observer-Pattern nach Gamma et. al. (2004) Abbildung 1.4 zeigt die Struktur des Observer-Patterns. Sie zeigt, dass sich jedes Model (Subject), welches sich beobachten lässt, sich seinen Zustand merkt und diesen bei Veränderungen all seinen Beobachtern mitteilt. Dabei unterscheidet man zwischen der abstrakten Definition eines Subjekts und dessen konkreter Implementierung. Dies ist auch auf der Seite des Beobachters der Fall. Erst dadurch ist die Beobachtung von 1-zu-n-Abhängigkeiten möglich. Das Observer-Pattern bietet eine gute Möglichkeit die Visualisierung für das Spielbrett zu verwalten. 1.1.5 Strategy-Pattern Das Strategy-Pattern ist ein bewährtes Design-Pattern aus der Softwaretechnik und gehört zu der Art der Verhaltensmuster. Zum Zweck des Strategy-Pattern schrieben Gamma, Helm, Johnson & Vlissides (2004): „Definiere eine Familie von Algorithmen, kapsele jeden einzelnen und mache sie austauschbar. Das Strategiemuster ermöglicht es, den Algorithmus unabhängig von ihn nutzenden Klienten zu variieren.“ Abbildung 1.5 zeigt die Struktur des Strategy-Patterns. Sie zeigt, dass ein Context-Objekt mithilfe eines Strategy-Objektes ein bestimmtes Problem löst. Dabei werden die jeweiligen Strategien zur Problemlösung innerhalb der ConcreteStrategy-Klassen implementiert. Dabei dient die Strategy-Klasse als Schnittstelle, die von allen konkreten Strategien verwendet wird. 4 Abbildung 1.5: Struktur des Strategy-Pattern nach Gamma et. al. (2004) 1.2 Werkzeuge Die folgenden externen Werkeuge werden in Übereinstimmung mit dem Lastenheft genutzt, um das Projekt „Implementierung eines verteilten Scrabble-Spiels“ umzusetzen. 1.2.1 Eclipse Eclipse ist eine Open-Source Entwicklungsumgebung, die ursprünglich für Java entwickelt wurde. Allerdings lässt sich Eclipse mit Hilfe von Plugins um eine Fülle an Funktionalität erweitern. Die im Softwaretechnik Praktikum verwendete Version 4.3.2 (Eclipse Kepler) beinhaltet keine eigene Funktionalität, sondern ist ausschließlich pluginbasiert. Dies bietet uns die Möglichkeit, die SwtPraKomponente Spielkonfigurator einfacher den Vorgaben entsprechend als Eclipse-Plugin umzusetzen, da wir auf eine vorhandene und bewährte Plugin-Struktur namens „Plug-in Development Environment“ (PDE) zurückgreifen können. Zur Java-Entwicklung werden wir das Plugin „Java Devolpment Tools“ (JDT) verwenden, welches eine Vielzahl an nützlicher Funktionalität zur Java-Entwicklung bietet. Um die Entwicklung einfacher zu gestalten werden wir das Eclipse Software Development Kit (SDK) benutzen, welches bereits die Java Development Tools (JDT) und Plug-in Development Environment (PDE) enthält. Zusätzlich dazu werden wir wie vorgegeben das Java Development Kit (JDK) Version 1.7 und die Java Runtime Environment (JRE) Version 1.7 als Laufzeit-Umgebung nutzen. 1.2.2 Android Android ist ein Smartphone-Betriebssystem, welches auf dem Linux Kernel basiert. Für das Softwaretechnik Praktikum werden wir auf der Android-Version 4.0 (API-level 15) einen Smarphone-Spieler umsetzen. 1.2.3 Graphiti und Graphical Editing Framework ersetzt durch SWT und SVG Graphiti und das Graphical Editing Framework (GEF) sind Eclipse-Frameworks, welche für die Erstellung von graphischen Oberflächen und Editoren entwickelt wurden. Dabei bietet sich die Nutzung 5 von Graphiti und GEF deswegen an, weil diese Frameworks die Model-View-Controller (MVC) Architektur umsetzen. Der Empfehlung des Lastenheftes widersprechend werden wir nicht GEF und Graphiti benutzen. Diese Entscheidung haben wir nach reichlicher Überlegung getroffen, bei der folgende Gründe sich ergeben haben: Die Eclipse Frameworks Graphiti und GEF sind sehr mächtige, und deswegen auch sehr umfangreiche Werkzeuge. Um unsere graphischen Oberflächen zu entwickeln reichen uns die Möglichkeiten eines kompakteren Werkzeuges vollkommen aus. Außerdem entsteht durch die große Komplexität von Graphiti und GEF für unser Team, dass über höchstens oberflächliche Kenntnisse der Funktionsweise von Graphiti und GEF verfügt, ein nicht zu vernachlässigender Einarbeitungsaufwand, den wir so umgehen können. Alternativ werden wir das Standard Widget Tool (SWT) verwenden, um alle unsere Oberflächen zu entwickeln. Die Möglichkeiten des SWT sind für unsere Zwecke hervorragend geeignet, weil sich mit Hilfe des SWT auf einfache Weise produktive Oberflächen erstellen lassen. Zusätzlich dazu bietet die SWT-eigene Struktur, welche auf Event-Klassen und Listener-Methoden aufgebaut ist, bereits die Möglichkeit, die MVC Architektur umzusetzen. Ein weiterer Grund für diese Entscheidung war, dass bereits einige Mitglieder unserer SWTPraGruppe einige Erfahrung mit SWT gesammelt haben und so die anderen Gruppenmitglieder bei der Nutzung von SWT unterstützen können. Zusätzlich dazu haben wir beschlossen, sämtliche Graphiken für die graphischen Oberflächen entsprechend der Scalable Vector Graphics (SVG) - Spezifikation zu hinterlegen, um bei jeder Auflösung optimale Symbole zu erzeugen. 1.2.4 Eclipse Modeling Framework Das Eclipse Modeling Framework (EMF) ist ein Open Source Framework, welches die Möglichkeit bietet Quelltext und dynamische Instanzen aus Modellen zu generieren. Dieses Framework ist für das diesjährige Softwaretechnik Praktikum in doppelter Hinsicht wichtig, da auf der einen Seite die Netzwerk-Kommunikation und auf der anderen Seite die Syntax einer Spielkonfiguration über ein EMF-Modell geregelt ist. So wird eine Spielkonfiguration als dynamische Instanz der Wurzelklasse „Configuration“ aus dem Spielkonfigurations-EMF-Modell entwickelt. Die fertige Spielkonfiguration liegt abschließend als xmi-Datei vor. 1.2.5 JavaScript Object Notation JavaScript Object Notation (JSON) ist ein offener Standard zur Übermittlung von Informationen in Form von Key-Value Paaren. Innerhalb des diesjähren Softwaretechnik Praktikums wird JSON für die gesamte Netzwerkkommunikation genutzt. Jeder Befehl bzw. jede Information wird also durch eine JSON-Datei übermittelt. Die genaue Syntax der JSON-Dateien wird dabei durch ein EMF-Modell bestimmt, welches vom Interface-Komitee entworfen wurde. Durch das Erstellen eines Objektes einer Klasse aus dem Netzwerk-EMF-Modell und dem anschließenden genormten Serialisierungsprozess können so einheitliche, von jeder Gruppe interpretierbare JSON-Dateien erstellt werden. 1.2.6 Plug-in Development Environment Der Zweck des Plug-in Development Environment (PDE) ist die Entwicklung und Verwaltung neuer bzw. selbst-erstellter Eclipse-Plugins. Dabei ist PDE selbst als Plugin in Eclipse integriert. Mit Hilfe 6 der drei Hauptkomponenten „Build“, „UI“ und „API Tools“ von PDE ist es möglich, die Entwicklung, die Wartung, das Testen, die Erstellung und Nutzung von Plugins in einer einzigen Umgebung (PDE) durchzuführen. 1.3 Gamekonfigurator 1.3.1 Konfiguration erstellen Das Diagramm (Abbildung 1.6) beschreibt ein Szenario, indem der User eine Konfiguration für ein Spiel erstellt. Er muss durch die Haupt-GUI die Größe des Spielfelds, die Verteilung der Premiumfelder und eine Gewinn-Kondition festlegen. Zusätzlich muss er sich zwischen einer natürlichen Sprache und einer erstellten Kategorie entscheiden. Die Erstellung der Kategorien erfolgt über der Kategorie-GUI, in der man die Wörter der Kategorie, die Verteilung der Punkte der Buchstaben und ihre Anzahl im Beutel festlegt. Schließlich hat der User die Möglichkeit eine Konfigurations-Datei mit den angegebenen Einstellungen zu erstellen. 1.3.2 Analyse-Klassendiagramm Im folgenden werden die Aufgaben der in Abbildung 1.7 dargestellten Klassen erläutert. Boundary MainGui Die MainGUI (Haupt-Gui genannt) visualisiert die verschiedene Einstellungsoptionen zur Konfiguration. CategoryGui Die CategoryGui visualisiert die verschiedene Einstellungsoptionen zur einer bestimmte Kategorie. Control ConfigurationControl Die ConfigurationControl verwaltet die Anfragen zur Änderung der Einstel- lungen, überprüft sie und gibt sie dem ConfigurationModel weiter. Entity Die Klassen aus der Entity-Schicht haben nur die Aufgabe die Daten der Spiele zu speichern und sind somit Selbsterklärend. 1.4 GameEngine 1.4.1 Spiel erstellen Szenariobeschreibung Das Diagramm (Abbildung 1.8) beschreibt ein Szenario, indem der User sich entscheidet, ein Spiel zu erstellen. Er wird zunächst die GUI verwenden, um Spieler zu dem Spiel hinzuzufügen oder Spieler zu entfernen. Hat er sich für eine Aufstellung entschieden, kann er den Startspieler bestimmen. Dies kann entweder durch einen Zufallsgenerator der Spielsteuerung geschehen oder indem der User die 7 Abbildung 1.6: Der Host benutzt die GUI, um eine Konfiguration zu erstellen 8 Abbildung 1.7: Analyse-Klassendiagramm der GameEngine Reihenfolge manuell verändert. Nun hat der User die Möglichkeit, einen 5 Sekunden Timeout zu aktivieren. Tut er dies, werden alle KI Spieler aus dem Spiel gekickt, wenn sie länger als 5 Sekunden für eine Antwort brauchen. Anschließend schickt die GUI die Konfigurationsdaten an die Spielsteuerung, welche eine Datei daraus erstellt. Diese Datei wird von der Spielsteuerung an alle Teilnehmer des Spieles gesandt. Die Spielsteuerung wartet jetzt 10 Sekunden, bevor sie das Spiel letztendlich startet und den ersten Spieler auffordert, seinen Zug zu tätigen. 1.4.2 Spiel kontrollieren Das Szenario „Spiel kontrollieren“ dient dazu, dass der Host das Spiel unterbrechen, fortsetzen und abbrechen kann. Hierzu wird nur eine Klasse, die den Befehl entgegen nimmt, benötigt. Diese prüft anschließend den Zustandes des Spieles, setzt es ggf. neu und informiert die Clients über den neuen Zustand bzw. informiert den Host darüber, dass das Spiel bereits im entsprechenden Zustand ist. Details über diesen Vorgang finden sich in Kapitel 3.1.3 des Pflichtenheftes oder der entsprechenden Abb. 3.4 eben dort. Da dieser Vorgang so einfach ist, wird hier bewusst auf ein Analysesequenzdiagramm verzichtet. 1.4.3 Zug überprüfen Das Diagramm (Abbildung 1.9) beschreibt ein Szenario, in dem ein Spielzug auf Gültigkeit überprüft wird. Zunächst übermittelt der Server der Spielsteuerung den nächsten Zug „m“ des aktuellen Spielers. Für die Gültigkeitsüberprüfung ist „BoardLogic“ zuständig. Abhängig von der Art des Spielzugs wird nun die Gültigkeit überprüft: pass Ein Spielzug, in dem der Spieler passt, ist immer gültig. exchange tiles Ein Wechseln von Spielsteinen mit dem Beutel ist genau dann gültig, wenn noch genügend Steine (mind. sieben) darin vorhanden sind. 9 Abbildung 1.8: Der Host benutzt die GUI, um ein Spiel zu erstellen play Damit das Legen eines Wortes gültig ist, muss das Wort einerseits im „Dictionary“ sein, anderer- seits muss es die Legeregeln einhalten: angrenzend von bestehenden Steinen liegen, alle Steine in derselben Spalte/Zeile, etc. Ist dies alles erfüllt, werden die Steine auf dem Brett platziert. Ist der Spielzug ungültig, wird dem Spieler (über den Server) ein Fehler gemeldet und er wird zu einem neuen Zug aufgefordert. Ist der Spielzug hingegen gültig, wird allen Spielteilnehmern mitgeteilt, was geschehen ist, insbesondere werden sie über den neuen Zustand des Bretts informiert, sofern Steine gelegt wurden. Danach wird der nächste Spieler zum Spielzug aufgefordert. 10 Abbildung 1.9: Ein vom Spieler gemachter Zug wird auf Gültigkeit geprüft 11 Abbildung 1.10: Analyse-Klassendiagramm der GameEngine 1.4.4 Analyse-Klassendiagramm Im folgenden werden die Aufgaben der in Abbildung 1.10 dargestellten Klassen erläutert. Boundary GameEngineGUI Die GameEngineGUI visualisiert die GameEngine für den Benutzer und wird bei Veränderungen der darunter liegenden Kontrollklassen aktualisiert. Server Es existiert nur ein Objekt der Server Klasse. Der Server stellt Funktionalitäten bereit die von Clients über das Netzwerk aufgerufen werden. Diese werden durch die Methoden beschrieben. ClientConnection Diese Klasse repräsentiert die Verbindung zu den Clients die sich mit dem Server verbunden haben. Es kann sich dabei sowohl um (KI-)Spieler als auch um Beobachter handeln. Die Methoden der Klasse haben die Aufgaben Informationen weiter an den Client zu senden. 12 Control GameLobby Ein Objekt der GameLobby Klasse verwaltet ein Spiel, das von dem Host geöffnet wurde und in dem sich Spieler anmelden können. Haben sich genügen Spieler registriert und der Host hat entschieden wer mitspielen darf, dann kann er das Spiel starten. In diesem Fall wird ein GameControl Objekt erstellt und die Lobby geschlossen. GameControl Die GameControl Klasse kümmert sich um den Ablauf der Spiele. Es werden Daten an die Mitspieler gesendet und Züge ausgewertet. GameEngine Die GameEngine verwaltet sowohl die Lobbys als auch die laufenden Spiele und leitet Anfragen die über das Server Objekt gehen weiter. BoardLogic Die BoardLogic verwaltet die Spiellogik eines Spiels. Sie aktualisiert das Spielfeld und überprüft Züge auf ihre Gültigkeit. Entity Die Klassen aus der Entity-Schicht haben nur die Aufgabe die Daten der Spiele zu speichern und sind somit Selbsterklärend. 1.5 Client 1.5.1 Mit dem Server Verbinden Abbildung 1.11: Ein menschlicher Nutzer der App verbindet sich mit dem Server 13 Szenariobeschreibung In der Abbildung 1.11 ist ein mögliches Szenario zu sehen, wie die Verbindung eines menschlichen Users am Smartphone mit dem Server ablaufen kann. Dabei sind auf dem Smartphone die Klassen ConnectionGUI, AppLogic, ServerConnection, User und nach der erfolgreichen Registrierung die LobbyGUI beteiligt. Der Benutzer sieht nach dem Starten der App einen „Connect to Server“ Button sowie ein Feld zur optionalen Eingabe eines Namens. Beim Klick des Buttons wird dem Server eine Anfrage übermittelt, dass ein Client sich am Server registrieren möchte. Diese Anfrage ist im Interface-Dokument festgelegt. Dabei wird dem Server mitgeteilt, welche Fähigkeiten dieser Client hat, also ob er zuschauen und ob er spielen kann. Nutzer der App können beides, daher wird hier für beide Werte true übermittelt, zusammen mit dem gewählten Namen. In unserem Beispiel gibt der User den Namen „Bob“ ein. Sollte der Server nicht innerhalb einer festgelegten Zeitspanne antworten, so ruft die ServerConnection auf der AppLogic eine Methode zur Fehlerbehandlung auf. Dem Benutzer wird eine entsprechende Fehlermeldung angezeigt und ein „Retry“-Button um erneut die selbe Verbindungsanfrage zu senden. Bei erfolgreicher Verbindung fügt der Server den Benutzer zur Kontaktliste hinzu. Der Server sendet das ConfirmLoginCommand und übergibt eine Id, mit der der Client von da an vom Server identifiziert werden kann. Erst bei Erhalt dieses Wertes zeigt die App dem User eine entsprechende Erfolgsmeldung an. Die LobbyGUI wird gestart und die Oberfläche um die Verbindung zu starten wird nicht länger angezeigt. Eine KI schickt ebenfalls die im Interface-Dokument festgelegte Anfrage LoginCommand an den Server, um sich zu registrieren. Der Server sendet bei erfolgreicher Registrierung ConfirmLoginCommand, die die Id übergibt. 1.5.2 Spiel abschließen Szenariobeschreibung Das Diagramm (Abbildung 1.12) beschreibt ein Szenario, in dem der User sich entscheidet, ein Spiel zu verlassen. Der erste Abschnitt der Alternative bezeichnet dabei das Szenario, falls der User ein Observer ist. Er bekommt zum Abschluss die Ergebnisliste angezeigt. Der zweite Abschnitt der Alternative hingegen bezeichnet das Szenario, falls der User ein Spieler ist. Er meldet sich vorher beim Server ab, so dass dieser Prüfen kann, ob genug ( 2) Spieler verbleiben. Der Server kann ggf. das Spiel beenden, falls nur 1 Spieler verbleiben. Der User bekommt in jedem Fall zum Abschluss die Ergebnisliste angezeigt. 1.5.3 Zug durchführen Szenariobeschreibung Das Diagramm (Abbildung 1.13) beschreibt ein Szenario, in dem ein Spieler einen Zug durchführt. Der erste Abschnitt der Alternative bezeichnet dabei das Szenario, dass der Spieler seinen Zug aussetzen möchte. Dies wird vom Server in jedem Falle akzeptiert. Der zweite Abschnitt der Alternative bezeichnet das Szenario, dass der Spieler manche seiner Spielsteine austauschen möchte. Dabei wählt der Spieler aus, welche Spielsteine er tauschen möchte und wählt dann den Button zum Tauschen. Diese Spielsteine werden dem Server mitgeteilt, welcher in diesem Szenario neue Spielsteine zurückliefert. Diese Änderung wird auch im GameModel gespeichert. Der dritte Abschnitt der Alternative bezeichnet schließlich das Szenario, dass der Spieler manche seiner Spielsteine auf dem Spielbrett platziert. Nach dem Senden des Zuges an den Server, liefert 14 Abbildung 1.12: Szenario: Das Spiel abschließen 15 Abbildung 1.13: Szenario: Zug durchführen dieser hier eine Bestätigung, dass der Zug akzeptiert wurde, sowie die neuen Steine für den Spieler. 16 Auch hier wird die Änderung wieder im GameModel gespeichert. 1.5.4 KI-Spieler Der KI-Spieler funktioniert ähnlich wie ein normaler, menschlicher Spieler, denn er verwendet dieselben Schnittstellen. Anstelle einer GUI, über die ein Mensch seine Züge eingeben kann, existiert jedoch das AI-Modul, eine Komponente, welche das Verhalten der KI bestimmt und versucht, möglichst gute Züge zu finden. Dieses Modul wird mithilfe der Konsole angesteuert, wobei durch Kommandozeilenargumente folgende Optionen bestimmt werden: 1. Name des AI-Spielers 2. Spiele zu denen er sich registriert 3. Optional: Bestimmung des Verhaltens der KI Außerdem gibt der KI-Spieler Informationen zu den Zügen die er gemacht hat auf dieser Konsole aus. 1.5.5 Analyse-Klassendiagramm Das Klassendiagramm in Abbildung 1.14 ergibt sich aus den bereits genannten Szenarien. Im Folgenden werden die Aufgaben der dargestellten Klassen erläutert. 1.5.5.1 Boundary ConnectionGUI Die ConnectionGUI visualisiert den Verbindungsaufbau vom Client zum Server. Hier wird der Nutzer die Verbindung konfigurieren und verwalten können, wie beispielsweise neue Verbindungsversuche bei Fehlern während des Verbindungsaufbaus unternehmen. GameView Die GameView ist die GUI, die der Spieler während des Spieles sieht. Hier werden das Brett, die Punkte und die eigenen Steine angezeigt und die Möglichkeit gegeben, einen Zug durchzuführen oder das Spiel zu verlassen. LobbyGUI Die LobbyGUI visualisiert die Spiellobby, in der man sich für Spiele registrieren kann. Hier werden dem Nutzer die Spiele angezeigt, die der Server zur Verfügung stellt und man kann sich für sie anmelden oder die Verbindung beenden. ServerConnection Die ServerConnection ist die Boundary-Klasse, welche mit dem Server kommuni- ziert. Sämtliche Kommunikation, die der Client mit dem Server hat, läuft über diese Klasse. Ein entsprechendes Gegenstück wird es auf der Server-Seite geben. 1.5.5.2 Control AppLogic Die AppLogic verwaltet alle Aktionen der User und verwaltet die GUIs, sodass immer die richtige GUI angezeigt wird. Es gibt hier nur eine Klasse, da der Client eine sehr kleine Komponente ist, die letztlich nicht mehr kann, als mit dem Server zu kommunizieren und das Spiel, das der Server liefert anzuzeigen. Daher sind die Operationen in der Logik nicht viele und alle einfach zu realisieren. Eine Intelligenz ist hier kaum bis nicht vorhanden. 17 Abbildung 1.14: Analyse-Klassendiagramm des Client 1.5.5.3 Entity GameModel Das GameModel hält den aktuellen Zustand des Spieles bereit. Hier ist es durch eine Klasse repräsentiert, da es an vielen Stellen benötigt wird und somit nur einmal in Kapitel 2.1 beschrieben ist. User Diese Klasse hält die Verbindungsdaten bereit, die u.a. für die Kommunikation mit dem Server und die Unterscheidung zwischen Spiel-Instanz und Observer-Instanz benötigt wird. Hierzu 18 dient die Methode isplayer(), die wahr zurückgibt, wenn es sich um eine Spieler-Instanz und falsch, falls es sich um einen Oberserver handelt. Die beiden Unterklassen repräsentieren entsprechend den Spieler bzw. den KI-Spieler. ResultTable Die ResultTable wird in dem Szenario „Spiel abschließen“ (siehe Abschnitt 1.5.2) benötigt, um die Ergebnis-Tabelle, die als JSON ankommt, an die GUI weiterzugeben, ohne dass die GUI sich mit JSON beschäftigen muss. Sie bekommt den JSON-String übergeben und bietet an, per Iteration alle Zeilen einzeln abzufragen. nextRow() springt dafür eine Zeile weiter und gibt true zurück, falls noch eine Zeile vorhanden war (also jetzt noch Daten über getName() und getPoints() abgefragt werden können), andernfalls false. 19 2 Entwurf Der Entwurf verfeinert die in der Analyse beschriebenen Komponenten und zeigt wie diese in Code umgesetzt werden sollen. Dies führt unter anderem dazu, dass Packages definiert werden, die in unterschiedlichen Komponenten Verwendung finden. Hierunter fällt das Game Model und das Mapper Package, die im folgenden Erläutert werden. Um die Übersichtlichkeit der Klassendiagramme zu erhöhen und ihre Größe zu reduzieren werden nicht bei allen Klassen alle Getter- und Setter-Methoden angegeben. 2.1 Das Game Model Abbildung 2.1: Das Entwurfsklassendiagramm des Game Models 20 Das Game Model enthält Klassen um ein Scrabble Spiel abbilden zu können. Aus diesem Grund ist es ein Package, das in allen Komponenten des Spiels Verwendung findet. Alle Klassen des Models sind dazu da, Daten zu speichern und nicht um logische Abläufe umzusetzen. Die Abbildung 2.1 stellt die enthaltenen Klassen dieses Pakets und ihre Verknüpfungen da. Eine besondere Funktion hat die Klasse Letter, die angibt welche Buchstaben wie oft vorhanden sind. Auch die Blankosteine werden von dieser Klasse erfasst. Die Tiles dürfen nur über die Methode generateTile von Letter erstellt werden um zu vermeiden, dass einem normalen Tile ein Blankostein Letter zugewiesen wird und dafür zu sorgen, dass stattdessen die dafür vorgesehene Joker Klasse genutzt wird. Die Version 1.0a des Netzwerkprotokolls des Interface Komitees wird in sofern berücksichtigt, das dem Client bei einem Update des Spiels die Möglichkeit gegeben wird, alle Tiles zurückzusetzen und über die Letter-Map des GameModels neu zu erzeugen. 2.1.1 Game Model Mapping Abbildung 2.2: Das Entwurfsklassendiagramm des Game Model Mappings Das Mapping-Paket stellt eine bidirektionale Übersetzungsschicht zwischen den Interface-Klassen des Komitees und dem von uns entworfenen GameModel dar. Das Paket umfasst zwei Klassen, die in Abbildung 2.2 zu sehen sind. Beide Klassen sind sowohl zur Serialisierung als auch Deserialisierung der von ihnen verantworteten Klassen zuständig. Der ConfigurationMapper bildet das GameModel auf den eCore-Klassen zur Konfiguration ab, während der NetworkMapper für das Übersetzen zwischen Netzwerk-Paketen und dem GameModel zuständig ist. Die Daten werden in ihre interne GameModel-Repräsentation überführt bevor sie in dieser Form von anderen Komponenten intern weiterverarbeitet werden. Der NetworkMapper wird ausschließlich von den Connection-Klassen des Servers und der Clients verwendet, damit diese Netzwerk-Pakete (de)serialiseren können. Mit Hilfe des ConfigurationMapper liest die GameEngine eine durch den Konfigurator exportiere Konfiguration ein. 2.2 Der Spielkonfigurator Die Abbildung 2.3 stellt das Entwurfsklassendiagramm des Spielkonfigurators dar. Wegen dem Model-View-Controller-Pattern ist der Konfigurator in die Packages de.scrabblenauts.configurator.view, de.scrabblenauts.configurator.controller, und de.scrabblenauts.gamemodel unterteilt wobei zu jeder GUI ein eigener Controller gehört. 21 Abbildung 2.3: Das Entwurfsklassendiagramm des Spielkonfigurators 2.2.1 de.scrabblenauts.configurator.view Das View Package des Spielkonfigurators ist für die Graphische Oberfläche des Spielkonfigurators verantwortlich. Es gibt jeweils ein Fenster für das Bearbeiten von Kategorien als auch ein Fenster zum Konfigurieren des Spielbretts. Die ConfigurationView Klasse präsentiert dem Benutzer die verschiedenen Einstellungsmöglichkeiten für das Spielbrett, die Sprache und Siegbedingung. Durch die Klasse BoardView, die im selben Package vorhanden ist, zeigt ConfigurationView eine Vorschau auf das von der Konfiguration erstellte Spielbrett. BoardView benutzt diesbezüglich die Klasse BoardCanvas um eine graphische Darstellung des Spielbrettes zu erhalten. Es soll möglich sein durch das Klicken auf das Spielbrett Premiumfelder zu platzieren. Die vom Benutzer veränderten Einstellungen werden an die ConfigurationControl Klasse gesendet. Die CategoryView Klasse ermöglicht es den Benutzern die Kategorie zu konfigurieren. Dies umfasst das bearbeiten der Wörter als auch eine Verteilung und Bewertung der Buchstaben über die Spielsteine anzugeben. Hierbei gibt es die Möglichkeit aus den Wörtern der Kategorie eine Standardverteilung 22 generieren zu lassen, die die Häufigkeit der Buchstaben berücksichtigt. 2.2.2 de.scrabblenauts.configurator.controller Das Controller Package verwaltet die Einstellungsveränderungen und Anfragen der Views sowie der Erstellung der Spielkonfigurationsdatei. Die ConfigurationControl bzw. CategoryControl Klasse kontrolliert die Einstellungsveränderungen und Anfragen der ConfigurationView bzw. CategoryView Klasse im View package. Die ConfigurationIO Klasse ist für das Erstellen, Modifizieren und Laden der Spielkonfigurationsdatei verantwortlich. Hierbei verwendet sie die Funktionalitäten des Mappers um aus dem GameModel eine Konfigurationsdatei zu erzeugen oder zu laden. 2.2.3 de.scrabblenauts.gamemodel Siehe Abschnitt 2.1. 2.3 Die Game Engine Abbildung 2.4 zeigt, wie die Game Engine des Servers aufgebaut sein soll. Die GameEngine verwaltet alle Spiele und Verbindungen, bietet jedoch auch eine Oberfläche, die der Host User verwenden kann, um Spiele hinzuzufügen und diese zu verwalten. Sie wird in 4 Packages aufgeteilt, die untereinander kommunizieren und voneinander abhängig sind. Das View Package regelt die Oberfläche und gibt alle Änderungen an das ServerController Package weiter. Dieses Package verwaltet alle Spiele und Verbindungen, wobei ein Spiel genauer von dem GameController Package und die Verbindungen von dem Network Package verwaltet werden. 2.3.1 de.scrabblenauts.server.view Das View Package der Game Engine beinhaltet nur die GameEngineGUI Klasse. Diese Klasse verarbeitet die Eingaben des Host-Users und gibt diese an die GameLobby im Server Controller Package weiter. Sie kennt alle GameLobby Objekte und die ServerLobby, um die Spieler und die Spiele anzeigen zu können. Weiterhin observiert die GameEngineGUI die ServerLobby unter Verwendung des Observer-Patterns, um die verfügbaren Spieler anzeigen zu können. 2.3.2 de.scrabblenauts.server.servercontroller Das Server Controller Package verwaltet die Lobby des Servers und die der Spiele. Weiterhin wird jede neue Verbindung zum Server oder zu einem Spiel von dem Server Controller verarbeitet. Die ServerLobby Klasse verwaltet alle Spieler und Spiele, die angelegt wurden. Die notify() Methode der ServerLobby Klasse realisiert das Observer Pattern mit der GameEngineGUI. Weiterhin kann die ServerLobby ein neues Spiel mit der createGame() Methode erstellen, welche die Methode mit gleichem Namen in der GameEngine aufruft. Jedes GameLobby Objekt kann durch die Methoden addPlayer, removePlayer, randomStartingPlayer, chooseStartingPlayer, acivateTimeout und chooseConfiguration von dem Host über die GameEngineGUI manipuliert werden. Anschließend kann die Lobby mit der createGameModel() Methode ein GameModel Objekt erstellen, das alle Informationen über das Spiel beinhaltet. Dieses wird benutzt, um ein Spiel mit Hilfe der startGame() Methode zu starten. Die GameEngine Klasse besitzt eine Map, die von einer GameID auf die GameLobby weist. Diese wird verwendet, um einen Spieler, der einem Spiel beitreten will, der richtigen Lobby zuzuweisen. Dies kann durch die joinGame(LobbyConnection,GameID) Methode getan werden. Wenn sich ein 23 Abbildung 2.4: Das Entwurfsklassendiagramm der Game Engine Client neu mit dem Server verbindet, wird in der Server Klasse die handleNewConnection() Methode aufgerufen, die die Methode mit selbem Namen in der GameEngine aufruft. Hier wird ein neues 24 LobbyConnection Objekt mit den Informationen des Clients erstellt, das anschließend an die Server Lobby weitergegeben wird, damit diese die verwalten kann. Wenn die startGame() Methode einer GameLobby aufgerufen wird, so erstellt die GameEngine ein neues GameControl Objekt aus den Informationen der GameLobby. Die createGame() Methode wird durch die ServerLobby aufgerufen, wodurch eine GameLobby erstellt wird und ein weiterer Eintrag für die neue GameLobby in der Map erstellt wird. 2.3.3 de.scrabblenauts.server.network Das Network Package beinhaltet die Schnittstelle für alle Verbindungen mit Clients. Die ClientConnection Klasse ist eine Oberklasse zu allen Connections. Diese stellt sicher, dass jede Connection eine eindeutige ID besitzt. Wenn ein Client eine Verbindung mit dem Scrabble Server aufbaut, so ruft die Server Klasse handleNewConnection() auf. Diese erstellt mit Hilfe der GameEngine eine LobbyConnection zu dem jeweiligen Client. Eine neue LobbyConnection wird jedem neuen Client zugeordnet. Diese LobbyConnections bestätigen den Clients die Verbindung mit der sendConfirmLogin(ClientID:int), wobei die ClientID die eindeutige ID der Connection ist. Danach wird periodisch die sendGameList() Methode aufgerufen, um dem Client eine Liste aller zur Zeit verfügbaren Spiele zu schicken. Sobald ein Spiel gestartet wird, werden von der GameControl je eine PlayerConnection Klasse aus den LobbyConnections jedes Mitspielers erstellt. Diese sind auch ViewerConnections, welche die Verbindung mit einem Beobachter regeln. Sobald das Spiel gestartet wird, wird die sendStartGame() Methode aufgerufen, um die Clients von dem Start des Spieles zu informieren. Danach wird periodisch sendUpdateGame aufgerufen, was die Clients über den momentanen Spielstand informiert. Sollte ein Spieler während des Spieles gekickt werden, so wird die sendKickNotification(int, KickReason) für alle Clients aufgerufen, wobei der Integer die ClientID ist und die KickReason ein enum Wert ist, der im Interface definiert ist und den Grund des kick anzeigen soll. Bevor ein Spiel startet, wird die sendConfiguration() Methode aufgerufen, welche die Konfiguration des Spiels broadcastet. Die PlayerConnection verwaltet alle Spieler und besitzt nur eine weitere Methode, um einen Spieler zu informeieren, dass der versuchte Zug nicht korrekt ist. 2.3.4 de.scrabblenauts.server.gamecontroller 2.3.4.1 de.scrabblenauts.server.gamecontroller.GameControl Die Klasse GameControl ist, wie der Name vermuten lässt, die Haupt-Klasse innerhalb des Game Controller Packages. Sie stellt eine Brücke zwischen der GameModel-Klasse innerhalb des gamemodel Packages und den PlayerConnection-Klassen innerhalb des Network-Packages dar. Die Klassenvariable currentPlayer merkt sich, von welcher PlayerConnection der nächste Zug angefordert werden muss. Die Klassenvariable players ordnet jeder PlayerConnection einen eindeutigen Player innerhalb des GameModels zu. Der Konstruktor für eine gültige GameControl bekommt das GameModel von der jeweiligen GameLobby übergeben, die das GameModel erstellt. Außerdem wird eine Liste an LobbyConnections übergeben, die zu PlayerConnections umgewandelt werden sollen. Die startGame Methode ruft für alle PlayerConnections und eventuelle ViewerConnections die sendStartGame-Methode auf. Anschließend wird das erste mal ein sendUpdateGame an alle PlayerConnections und eventuelle ViewerConnections geschickt, wodurch jedem Spieler seine Starthand zugewiesen wird und eindeutig hervorgeht, welcher Spieler nun einen Zug an die GameEngine senden muss. Abschließend wird 10 Sekunden gewartet um den ClientConnections die Chance zu geben, alle Informationen zu verarbeiten. 25 Die Methode informAllGameParticipantsOfNewBoardState wird immer dann ausgeführt, wenn die GameControl-Klasse einen neuen Spielzug vom currentPlayer anfordern möchte und den vorherigen Spielzug vollständig in das GameModell übertragen hat. Sie schickt sendUpdateGame an alle PlayerConnections und eventuelle ViewerConnections. Derjenige Client, dessen ID mit der ID in activePlayerID (siehe Interface-Dokument Seite 2) übereinstimmt, weiß nun, dass ein Zug von ihm verlangt wird. Die Methode processMove dient der Verarbeitung des Spielzuges, der von dem momentan aktiven Spieler über seine PlayerConnection von der GameControl empfangen wurde. Sie benutzt die BoardLogic-Klasse, um den Move zu validieren und anschließend mit performMove auf das GameModel zu übertragen. Die Methode informPlayerOfError wird von processMove aufgerufen, falls die Validierung eines übertragenen Moves fehlschlägt. In diesem Fall wird sendInvalidMove an die momentan aktive PlayerConnection geschickt. Die Methode handleLeave wird genau dann aufgerufen, wenn die GameControl von einer ViewerConnection das LeaveGameCommand (siehe Interface-Dokument) empfängt oder die Verbindung zwischen einer ViewerConnection und der GameControl fehlschlägt. Es wird auf allen PlayerConnections und eventuellen ViewerConnections sendKickNotification mit der jeweiligen ClientID und der kickReason aufgerufen. RegisterNewViewer wird von der GameEngine aufgerufen, wenn eine bestehende LobbyConnection den Befehl joinGameCommand an ein laufendes Spiel sendet und das Attribut isObserver innerhalb des joinGameCommands auf true steht. Die jeweilige LobbyConnection wird als Parameter übergeben und zu einer ViewerConnection der GameControl umgewandelt. Abschließend wird sendUpdateGame für die neue ViewerConnection aufgerufen. 2.3.4.2 de.scrabblenauts.server.gamecontroller.BoardLogic Die Klasse BoardLogic dient der Überprüfung von Spielzügen und der Aktualisierung des Boards innerhalb des zugehörigen GameModels. Die Methoden isMoveValid und isValidPosition dienen der Validierung von Spielzügen. Sie werden von der Klasse GameControl verwendet. Die Methode performMove setzt einen Move, der von einer PlayerConnection übermittelt und validiert wurde, auf dem zugehörigen GameModel um. 2.4 Der Smartphone-Spieler Wie in Abbildung 2.5 zu sehen ist, ist der Spieler in vier Pakete aufgeteilt. Im folgenden Abschnitt werden die Aufgaben dieser Pakete näher erläutert. 2.4.1 de.scrabblenauts.player.controller Das controller Paket ist für alle Aktionen des Spielers verantwortlich. Das Paket beinhaltet lediglich die Klasse AppLogic. Diese erledigt über die ServerConnection aus dem network Paket die gesamte Kommunikation mit dem Server, unter anderem Verbindungsaufbau, Übermitteln von Daten über den Client und Beitreten zu Spielen. Des Weiteren ist sie verantwortlich dafür, alle Aktionen, die der User auf einer GUI ausführt, wie z.B. Spielsteine tauschen, zur Überprüfung über die ServerConnection an den Server zu schicken. Dabei sendet die Methode den Spielzug und wartet auf eine Antwort vom Server um diese dann entsprechend zu behandeln. Wenn der Zug für gültig befunden wurde, übertragt sie die Änderungen in das GameModel. Ansonsten wird dem User mitgeteilt, warum der Zug ungültig war, dann hat er die Möglichkeit einen neuen Zug zu machen. 26 Abbildung 2.5: Das Entwurfsklassendiagramm des Smartphone-Spielers Die AppLogic legt dabei fest, was gesendet werden muss, für die dem Interfacedokument entsprechende Versendung ist das network Paket verantwortlich. 2.4.2 de.scrabblenauts.player.view Das view Paket ist für die komplette Oberflächenvisualisierung zuständig. Wir unterscheiden dabei zwischen der ConnectionGUI, der GameView und der LobbyGUI. Die ConnectionGUI wird beim Verbindungsaufbau angezeigt und wird lediglich zur Eingabe der Server-IP und des Ports und eines optionalen Benutzernamens verwendet und bietet somit nur 3 Textfelder und einen Bestätigungsbutton an. Die ConnectionGUI wird im Konstruktor der AppLogic gestartet. Die GameView zeigt das Spielfeld, die eigenen Spielsteine und eine Übersichtstabelle mit Spielinformationen an und ermöglicht durch diverse Buttons Spielzüge durchzuführen. Zusätzlich können Spielsteine ausgewählt werden. Dies wird durch die Android Touchscreensteuerung ermöglicht. Sie wird gestartet, sobald die registerForGame() Methode eine Antwort vom Server erhält. Die LobbyGUI visualisiert die Lobby des Servers mit allen offenen Spielen und ermöglicht somit eine Registrierung für ein erstelltes Spiel. Sie wird in der connectToServer() Methode nach dem Verbindungsaufbau gestartet. Für die Visualisierung dieser GUIs wird die Android-eigene Oberflächenbibliothek verwendet. 27 2.4.3 de.scrabblenauts.player.network Das network Paket beinhaltet die ServerConnection Klasse, welche für die Kommunikation mit dem Server zuständig ist. Sie verwendet den NetworkMapper um die empfangenen Daten so zu serialisieren, dass sie dem GameModel entsprechen und um die Änderungen der Spieler an den Server zu senden. Die ServerConnection übermittelt die von der AppLogic gesendeten Aktionen in dem Interfacedokument entsprechenden Formaten dem Server. Hierzu wird ebenfalls das Mapping Paket verwendet um unter anderem die Objekte der Move Klasse in Objekte der Netzwerkschnitstelle umzuwandeln. 2.4.4 de.scrabblenauts.player.data Das data Paket dient zur Datenhaltung außerhalb des festgelegten GameModel Paketes. Dieses Paket enthält die User Klasse, die die Identität des jeweiligen Spielers darstellt und durch eine ID eine eindeutige Identifizierung ermöglicht. Hier wird auch der gewählte Name des Spielers gespeichert. An der PlayerGameModel Klasse können sich ModelListener anmelden, die dann über Änderungen des GameModels benachrichtigt werden. In der Klasse Notification werden dem User anzuzeigende Meldungen gespeichert, beispielsweise wenn ein Zug ungültig war oder das Spiel pausiert wurde. 2.5 Der KI-Spieler Der KI-Spieler unterteilt sich zum einen in die Pakete die zur Verbindung mit dem Server und dem Senden und Empfangen der Befehlen befassen und zum anderen in die Spielstrategie, welche den Zug für den KI-Spieler ermittelt. Bei dem Entwurf der Spielstrategie haben wir uns an dem Verhalten der bekannten Scrabble KIs Maven1 und Quackle2 orientiert. Diese berechnen für jede Runde alle möglichen Züge des KI-Spielers, um sie anschließend zu bewerten. Dies ermöglicht uns den Zug mit der besten Bewertung durchzuführen. Außerdem haben wir uns dafür entschieden, an vielen Stellen das Strategy-Pattern zu verwenden, um alternative Algorithmen einfach umsetzen zu können und somit die Möglichkeit zu haben, unterschiedliche KI-Spieler zu implementieren. 2.5.1 Klassendiagramm Die Logik des KI-Spielers wurde in zwei Sub-Packages unterteilt: Das Package „de.scrabblenauts.player.ai.movegenerator“ dient dazu gültige Züge zu finden, damit diese im Package „de.scrabblenauts.player.ai.strategy“ bewertet werden, um schließlich einen möglichst guten Zug zu ermitteln. In Abbildung 2.6 werden die Zusammenhänge zwischen den Klassen dieser Packages dargestellt. 2.5.2 de.scrabblenauts.player.ai Diese Klassen haben die Aufgabe die Schnittstelle des Smartphone-Spielers wiederzuverwenden. Der AiPlayer wird bei Veränderungen benachrichtigt und kann mithilfe der AppLogic Befehle an den Server senden. Die Konsole zeigt Nachrichten der KI und regelt die Eingabe des Benutzers. Hierbei handelt es sich unter anderem um das Registrieren zu Spielen. 1 http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.2.9747&rep=rep1&type= pdf 2 http://people.csail.mit.edu/jasonkb/quackle/ 28 Abbildung 2.6: Das Entwurfsklassendiagramm des KI-Spielers 2.5.3 de.scrabblenauts.player.ai.movegenerator In diesem Package geht es darum, gültige Züge zu finden. Die abstrakte Basisklasse „MoveGenerator“ lässt es offen, welche Züge betrachtet werden und in welcher Reihenfolge sie generiert werden. Das gibt uns die Möglichkeit, bei der Implementierung einen anderen Algorithmus oder eine andere Datenstruktur zu wählen, sollte sich herausstellen, dass wir Laufzeit- oder Speicherplatzprobleme haben. Außerdem können somit die Züge, bei denen Spielsteine eingetauscht werden, unabhängig von den Zügen, bei denen Spielsteine auf das Brett gelegt werden, berechnet werden. Datenstruktur Um schnell alle möglichen Züge finden zu können, müssen die Wörter aus dem Wörterbuch in eine geeignete Datenstruktur gebracht werden. Hierzu eignet sich ein sogenannter 29 „GADDAG“. Die Alternative wäre die Datenstruktur „DAWG“ („Directed Acyclic Word Graph“). Ein „DAWG“ verbraucht zwar weniger Speicherplatz als ein „GADDAG“, ordnet allerdings die Wörter ausschließlich nach dem Anfangsbuchstaben. Das ist für das Spiel Scrabble ziemlich unpraktisch, da ein bereits liegender Buchstabe meistens für die Mitte eines neuen Wortes verwendet wird. Wir haben uns daher für den „GADDAG“ entschieden, der berücksichtigt, dass Wörter zum Überprüfen nicht nur von links nach rechts gelesen werden. Das Prinzip wird dabei aus dem Artikel ’A Faster Scrabble Move Generation Algorithm’3 von Steven Gordon übernommen. 2.5.4 de.scrabblenauts.player.ai.strategy Dieses Package benutzt die verschiedenen MoveGenerators und ist dafür zuständig, sich letztendlich für einen möglichst guten Zug zu entscheiden. Verwendet werden dafür mehrere Bewertungsklassen, die sogenannten Evaluatoren. Es gibt einen MoveEvaluator der jedem möglichen Zug eine BewertungsPunktzahl zuordnet und den RackEvaluator, dem die verbleibenden Spielsteine eines Zuges übergeben werden und der diese auswertet. Der einfache MoveEvaluator weist jedem Zug den Punktwert hinzu, den der Spieler durch das Legen erhält. Der einfache RackEvaluator bestraft Racks, bei denen Spielsteine mit einem niedrigen Buchstabenwert mehrfach enthalten sind. Der SimpleMoveDecisionMaker bewertet den Zug nach den oben genannten Kriterien und addiert das Ergebnis der beiden Evaluatoren auf. Anschließend überprüft er, ob der Zug eine höhere Punktzahl als der bisher beste gefundene Zug besitzt. Ist dies der Fall, so wurde der neue beste Zug gefunden. Somit kann der SimpleMoveDecisionMaker jederzeit abgebrochen werden und liefert dennoch ein gutes Ergebnis. Hierbei ist es wichtig, auf die Synchronisation zu achten, denn die Züge können von mehreren Threads übergeben werden. Abbildung 2.7 zeigt, was passiert, wenn vom SimpleMoveDecisionMaker ein neuer Zug angefordert wird: Strategie • Solange es noch mögliche Spielzüge gibt, wird der nächste Spielzug generiert. • Für jeden möglichen Spielzug werden zwei Bewertungen durchgeführt. Der aktuell beste Spielzug wird sich dabei gemerkt. • Wenn alle möglichen Spielzüge betrachtet wurden, oder wenn vorher das gesetzte Zeitlimit überschritten wurde, wird der bisher beste Spielzug zurückgegeben. Um das Abbrechen des SimpleMoveDecisionMakers zu ermöglichen, muss dieser in einem eigenen Thread laufen, der von der AiStrategy erstellt wird. Außerdem kann ein PutMoveGenerator so umgesetzt werden, dass das Finden der Züge in mehrere Threads aufgeteilt werden kann, um die Geschwindigkeit des KI-Spielers bei mehreren Kernen zu erhöhen. Threading Zudem gibt es noch die Möglichkeit, verschiedene Spielstrategien für die verschiedenen Phasen des Spiels festzusetzen: Hierzu wird die Klasse PhaseDecider, welche die Phase entscheidet und standardmäßig den Wert 0 zurückgibt, durch eine eigene Klasse überschrieben. Diese hat die Möglichkeit, bei dem Aufruf der Methode determinePhase eine nächste Phase festzustellen und dementsprechend die Phase zu inkrementieren. Da jede AiStrategy eine Liste von MoveDecisionMakern hat wird dementsprechend der nächste zum Berechnen des Zuges verwendet. Durch das Interface ist es hierbei möglich, verschiedene DecisionMakers umzusetzen. Wichtig ist nur, dass sie in der Lage sein müssen, bei einem drohenden Zeitlimit unterbrochen zu werden und trotzdem bereits den besten bisher Phasen 3 http://www.ericsink.com/downloads/faster-scrabble-gordon.pdf 30 Abbildung 2.7: Das Zustandsdiagramm des KI-Spielers beim Anfordern eines neuen Zugs gefunden Zug zurückgeben können. Zum Ende des Spiels könnte es sinnvoll sein, die Spielsteine im Beutel zu erraten oder hohe Spielsteine einzutauschen bevor das Spiel zu Ende ist. Die Standard-KI, wie sie im Diagramm zu sehen ist, tut dies jedoch nicht, denn sie kennt nur eine Phase. 31 Abbildungsverzeichnis 1.1 1.2 1.3 32 1.4 1.5 1.6 1.7 1.8 1.9 1.10 1.11 1.12 1.13 1.14 UML-Verteilungsdiagramm der Komponenten . . . . . . . . . . . . . . . . . . . . . 2 UML-Komponentendiagramm der Scrabble-Variante . . . . . . . . . . . . . . . . . 2 Modell des Model-View-Controller (von http://en.wikipedia.org/wiki/File:MVC-Process.svg [Zugriff am 13.05.2014]) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Struktur des Observer-Pattern nach Gamma et. al. (2004) . . . . . . . . . . . . . . . 4 Struktur des Strategy-Pattern nach Gamma et. al. (2004) . . . . . . . . . . . . . . . 5 Der Host benutzt die GUI, um eine Konfiguration zu erstellen . . . . . . . . . . . . . 8 Analyse-Klassendiagramm der GameEngine . . . . . . . . . . . . . . . . . . . . . . 9 Der Host benutzt die GUI, um ein Spiel zu erstellen . . . . . . . . . . . . . . . . . . 10 Ein vom Spieler gemachter Zug wird auf Gültigkeit geprüft . . . . . . . . . . . . . . 11 Analyse-Klassendiagramm der GameEngine . . . . . . . . . . . . . . . . . . . . . . 12 Ein menschlicher Nutzer der App verbindet sich mit dem Server . . . . . . . . . . . 13 Szenario: Das Spiel abschließen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Szenario: Zug durchführen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Analyse-Klassendiagramm des Client . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.1 2.2 2.3 2.4 2.5 2.6 2.7 Das Entwurfsklassendiagramm des Game Models . . . . . . . . . . . . . Das Entwurfsklassendiagramm des Game Model Mappings . . . . . . . . Das Entwurfsklassendiagramm des Spielkonfigurators . . . . . . . . . . . Das Entwurfsklassendiagramm der Game Engine . . . . . . . . . . . . . Das Entwurfsklassendiagramm des Smartphone-Spielers . . . . . . . . . Das Entwurfsklassendiagramm des KI-Spielers . . . . . . . . . . . . . . Das Zustandsdiagramm des KI-Spielers beim Anfordern eines neuen Zugs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 21 22 24 27 29 31