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