high quality - The Beer Distribution Game

Werbung
Diplomarbeit
Demonstration der Dynamik
in Logistik- und Produktionsnetzwerken
anhand des Beer Distribution Game
in einer Online-Version
Christoph Duijts
Betreuer:
Dipl.-Wi.-Ing. Jörg Nienhaus
Zürichbergstrasse 18
CH-8028 Zürich
Institut für Informationssysteme:
Logistik und Informationsmanagement:
Prof. Dr. C.A. Zehnder
ETH Zentrum, IFW
CH-8092 Zürich
Prof. Dr. Paul Schönsleben
ETH-Zentrum für Unternehmenswissenschaft
Zürichbergstrasse 18
CH-8028 Zürich
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
1
Abstract / Management Summary
Das Beer Game wurde entwickelt als Einführung in die Konzepte der Systemdynamik. Gespielt
werden dürfte es hauptsächlich von Studenten oder an Seminaren für Firmen. Die Spieler machen
die Erfahrung, eine Rolle in einem komplexen System zu spielen und sehen anschliessend die
Auswirkungen ihres Handelns. Das Ziel des Spiels ist die Minimierung der Kosten für eine
gegebene Supply-Chain. Die Spieler erleben den sogenannten Bullwhip-Effekt – die
Bestellmengen vervielfachen sich über die Länge der Lieferkette, da die Spieler jeweils nur die
Bestellinformationen des jeweiligen Vorgängers kennen.
Die Aufgabe dieser Diplomarbeit war es, das bestehende klassische Brettspiel auf eine OnlineVariante abzubilden. Dazu wurde eine Client-Server Architektur aufgesetzt. Umgesetzt wurde
der Client als Java-Applet und der Server als Java-Programm. Die Kommunikation wird über
eine Socket Connection abgewickelt. Als Erweiterung zum klassischen Beergame kann auch der
Computer einzelne (oder alle) Rollen übernehmen und nach gewissen vorgegebenen Strategien
spielen. Die Auswertung erfolgt in zahlreichen Charts, welche entweder einzelne oder überlagerte
Kurven enthalten, welche den Spielverlauf sehr schön aufzeigen. In der Auswertung ist es auch
möglich, sich jede einzelne Runde (mit den Zahlen und Graphen aller Mitspieler) nochmals
anzusehen.
Die Spieler werden über ihre (weltweit eindeutige) E-Mailadresse identifiziert. Jeder Spieler hat
die Möglichkeit, sein eigenes neues Spiel mit verschiedenen Parametern zu kreieren und danach
andere Spieler einzuladen oder selbst einem anderen bestehenden Spiel beizutreten. Das Spiel
wurde so programmiert, dass ein einzelner Spieler ein laufendes Spiel nicht blockieren kann.
Dazu wird eine fixe Zeit pro Bestellrunde vorgegeben – läuft diese ab, übernimmt der Computer
die Bestellung. Fällt ein Client ganz aus, wird dessen Rolle ebenfalls an den Computer
übertragen.
Die beendeten Spiele werden auf dem Server gespeichert. Den Spielern steht jederzeit die
Möglichkeit offen, die Auswertung ihrer beendeten Spiele wieder anzusehen. Dies ist sinnvoll,
falls das Spiel zu einer späteren Zeit (ev. in einer Vorlesung) besprochen werden soll.
Der folgende Bericht liegt in vier Hauptteilen vor. Im ersten Teil folgt eine Auseinandersetzung
mit theoretischen Grundlagen des Spieles sowie eine Situationsanalyse zu bereits bestehenden
Varianten. Im zweiten Teil wird auf die Wahl der Entwicklungsumgebung eingegangen und im
dritten Teil dann das Objektmodell und die Programmstruktur erläutert. Dazu gehört eine
Kurzbeschreibung aller Klassen sowie deren wichtigsten Methoden. Abschliessend im vierten
Teil folgt dann die Spielbeschreibung selbst mit verschiedenen Screenshots.
Seite 2/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
2
Vorwort
Als ich auf der Suche nach einer möglichen Diplomarbeit war, bin ich auf der Homepage der
Professur für Logistik- und Informationsmanagement von Herrn Prof. Dr. Schönsleben auf diese
Aufgabe gestossen. Die Problemstellung hat mich vom ersten Moment an fasziniert und die
Faszination hat bis zum Schluss nicht mehr losgelassen. Mit freundlicher Unterstützung von
meinem Diplomprofessor - Herrn Prof. Dr. Zehnder (Institut für Informationssysteme) - war es
dann möglich, diese bereichsübergreifende Arbeit in Angriff zu nehmen.
Neben der eigentlichen Aufgabe bestand die Motivation vor allem in der Herausforderung, mich
einmal mit einer modernen Programmiersprache intensiv auseinandersetzen zu können (ich hatte
zuvor weder in Java noch in einer anderen objektorientierten Sprache programmiert). Es war eine
sehr interessante und spannende Erfahrung und das Projekt hat vom Anfang bis zum Ende Spass
gemacht. An dieser Stelle muss ich meinem Freund Simon Zweifel einen herzlichen Dank
aussprechen, da er mir als Informatiker während der ganzen Arbeit online als kompetenter
Ratgeber in Programmierfragen zur Seite gestanden ist. Ohne ihn wäre diese Arbeit bei weitem
nicht so weit fortgeschritten. Herzlich zu Dank verpflichtet bin ich auf meinen Eltern Karl und
Ruth Duijts, welche die Korrekturlesung übernommen haben sowie Nina Reiser für die spanische
Übersetzung.
Im Laufe des Projektes wurde aus der Arbeit eher ein Hobby - Probleme wurden in zahlreichen
Nachtschichten und Wochenendeinsätzen gelöst. Auch Schreckensmomente fehlten natürlich
nicht; zum Beispiel ist in der Abgabewoche mein Laptop ausgestiegen, auf dem sowohl das
gesamte Programm als auch dieser Bericht gespeichert war (selbstverständlich gab es
Sicherheitskopien – aber eben...). Glücklicherweise stellte sich heraus, dass „bloss“ das Netzteil
defekt war – ein paar Stunden später war mein sonst so zuverlässiges Mac-PowerBook wieder
einsatzfähig.
Mit dieser Arbeit schliesse ich nun mein Studium ab. Wenn ich zurück blicke, darf ich sagen,
dass es mir sehr gefallen hat und dass ich dasselbe Studium wieder wählen würde. Ich freue mich
nun aber auch auf eine neue Herausforderung im Berufsleben.
Zürich, 1.2.2001
Christoph Duijts
Seite 3/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
Inhaltsverzeichnis
1
Abstract / Management Summary __________________________________________ 2
2
Vorwort_______________________________________________________________ 3
3
Aufgabenstellung _______________________________________________________ 6
4
Das Beer Distribution Game_______________________________________________ 8
4.1
4.2
4.3
5
Situationsanalyse ______________________________________________________ 11
5.1
5.1.1
5.1.2
5.1.3
5.2
6
6.3.1
6.3.2
6.4
6.4.1
6.4.2
6.4.3
6.4.4
6.4.5
6.5
6.6
Brettspiel – Klassische Variante___________________________________________________11
Computerized Beer Game _______________________________________________________12
Simple ++ Variante ____________________________________________________________14
Beurteilung ____________________________________________________________ 15
Pflichtenheft / Leitlinien für die Umsetzung___________________________________ 16
Auswahl der Programmiersprache, Entwicklungsumgebung _____________________ 16
Java – offene Probleme __________________________________________________ 17
Java Support _________________________________________________________________17
Unterschiede in der Java Implementation ____________________________________________17
Lösungsansätze für die Implementierung_____________________________________ 18
Ausschliessliche Verwendung von JDK 1.1.x und früher ________________________________18
Installieren von nicht Kern Java Klassen auf den Clients_________________________________18
Unterstützung für nur einen Browsertyp _____________________________________________18
Verwenden des Java Plug-in _____________________________________________________18
Verwenden von Java Web Start ___________________________________________________19
Zusammenfassung der Lösungsansätze______________________________________ 19
Die Wahl ______________________________________________________________ 20
Diskussion und Auswahl der Architektur____________________________________ 21
7.1
7.1.1
7.1.2
7.2
7.2.1
7.2.2
7.2.3
7.2.4
8
Existierende Varianten des Beer Games______________________________________ 11
Umsetzungskonzept ____________________________________________________ 16
6.1
6.2
6.3
7
Die Entstehung __________________________________________________________ 8
Das traditionelle Beer Game ________________________________________________ 8
Theoretische Grundlagen – der Bullwhip Effekt ________________________________ 9
Umsetzungsentscheid Programmarchitektur __________________________________ 21
Der Client ___________________________________________________________________21
Der Server ___________________________________________________________________21
Kommunikationskonzept _________________________________________________ 22
http-requests (request/response) ___________________________________________________22
Socket connection _____________________________________________________________22
RMI – Remote Method Invocation _________________________________________________22
Umsetzungsentscheid ___________________________________________________________22
Die Umsetzung ________________________________________________________ 23
8.1
8.1.1
8.1.2
8.1.3
8.2
Kurzübersicht über die Packages und ihre Klassen_____________________________ 23
Das Package „beergame.server ____________________________________________________23
Das Package „beergame.client“ ___________________________________________________25
Das Package „beergame.client.images“ _____________________________________________27
Das Spielkonzept ________________________________________________________ 28
Seite 4/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
9
Klassen des Package beergame.server ______________________________________ 29
9.1
9.2
9.3
9.4
9.5
9.6
9.7
10
BeergameServer.java ____________________________________________________
HelloThread.java. _______________________________________________________
SendThread.java ________________________________________________________
ListenThread.java _______________________________________________________
PlayerId.java ___________________________________________________________
Game.java _____________________________________________________________
Strategy.java ___________________________________________________________
29
30
30
33
33
34
37
Klassen des Package beergame.client _____________________________________ 39
10.1
10.2
10.3
10.4
10.5
10.6
10.7
10.8
10.9
10.10
10.11
10.12
10.13
BeergameClient.java _____________________________________________________
ClientListener.java ______________________________________________________
ClientSender.java _______________________________________________________
GameLogic.java_________________________________________________________
OrderPerformedTransition _______________________________________________
NextRoundTransistion ___________________________________________________
ServerMessage.java______________________________________________________
Res.java _______________________________________________________________
Ref.java _______________________________________________________________
PnlStatistics.java ______________________________________________________
DlgNewGame.java_____________________________________________________
DlgJoinGame_________________________________________________________
PnlStock.java, PnlGoodsReceiving.java und PnlTranport.java _________________
39
42
43
43
48
48
50
51
52
52
55
56
57
11
mögliche Erweiterungen des Spieles______________________________________ 58
12
Spielbeschreibung____________________________________________________ 60
12.1
12.2
12.3
12.4
12.5
12.6
12.7
12.8
12.9
13
13.1
13.2
13.3
14
Die Anmeldung _________________________________________________________
Der Welcome-Screen _____________________________________________________
Ein neues Spiel erzeugen __________________________________________________
Beitreten zu einem Spiel __________________________________________________
Spielstart ______________________________________________________________
Das Spiel ______________________________________________________________
Die Auswertung _________________________________________________________
Auswertung einer Computer Simulation _____________________________________
Animationen ___________________________________________________________
60
61
62
64
65
66
67
68
69
Anhang ____________________________________________________________ 70
Übersicht über die Klassen und ihre Abhängigkeiten ___________________________ 72
Der Server Start Prozess / Start of a socket connection__________________________ 73
The Invitation Process – a three way handshake _______________________________ 74
Literaturverzeichnis __________________________________________________ 76
Seite 5/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
3
Aufgabenstellung
Herrn
Christoph Duijts
Herbstweg 110
8050 Zürich
Dipl.-Wi.-Ing. Jörg Nienhaus
Zürichbergstrasse 18
CH-8028 Zürich
Tel. +41 1 632 05 23, Fax +41 1 632 10 40
[email protected]
http://www.lim.ethz.ch
Zürich, 6. September 2001
Diplomarbeit
Arbeitsbeginn:
Abgabetermin:
22. Oktober 2001
21. Januar
2001 (60 Werktage)
Demonstration der Dynamik in Logistik- und Produktionsnetzwerken
anhand des Beer Distribution Game in einer Online-Version
Das zu Beginn der 60er Jahre in der System Dynamics Group am Massachusetts Institute of
Technology (MIT) entstandene Beer Distribution Game veranschaulicht den in Logistik- und
Produktionsnetzwerken auftretenden Bullwhip-Effekt: Ein Unternehmen macht die Anzahl der
beim Zulieferer zu bestellenden Komponenten von den Bestellungen des ihm im Netzwerk
vorgelagerten Unternehmens (und nicht von denen des Endkunden) abhängig. Deshalb
schwanken die Bestellmengen umso stärker, je mehr Stufen im Netzwerk zwischen einem
Unternehmen und dem Endkunden liegen. Die steigende Variabilität der Bestellmengen erfordert
bei den Zulieferern mit zunehmender Tiefe im Netzwerk immer höhere Sicherheitsbestände,
damit sie ihre Lieferfähigkeit gewährleisten können. Diesen Effekt vollziehen beim Beer
Distribution Game vier Teilnehmer nach, indem sie ein Logistik- und Produktionsnetzwerk
bestehend aus Lieferant, Produzent, Grosshändler und Einzelhändler simulieren.
Im Rahmen der Diplomarbeit sollen Sie ein Konzept für eine Online-Version der Simulation
erstellen und es anschliessend umsetzen. Eine solche Online-Version erlaubt Teilnehmern an
beliebigen Standorten auf der Welt, sich zu einer vorher verabredeten Zeit auf einer Webseite zu
treffen und am Beer Distribution Game teilzunehmen. Ferner kann die sonst manuell
vorzunehmende Auswertung der Ergebnisse – wie bei einer am ETH-Zentrum für Unternehmenswissenschaften (BWI) bereits vorhandenen Version in Simple++ – automatisch erfolgen.
Seite 6/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
In einem ersten Schritt machen Sie sich mit den Regeln des Beer Distribution Game und der
dieser Simulation zugrunde liegenden Theorie vertraut. Zeigen Sie anschliessend, welche
Anforderungen sich aus den Regeln für die Umsetzung in eine Online-Version ergeben.
Berücksichtigen Sie dabei, dass die Simulation beim klassischen Beer Distribution Game in
Runden getaktet erfolgt. Schlagen Sie Varianten und Erweiterungen der Regeln vor, die durch die
erweiterten Möglichkeiten einer Online-Version möglich sind, und wählen Sie die aus Ihrer Sicht
besonders geeigneten aus. Denken Sie daran, dass Sie für die Teilnehmer Möglichkeiten
vorsehen müssen, sich zu einer verabredeten Zeit zum Spiel zu treffen. Ferner sind für nicht
besetzte Positionen die Bestellungen automatisch anhand eines Bestellmusters zu generieren.
Nachdem Sie Anforderungen an die Online-Version des Beer Distribution Game spezifiziert
haben, wählen Sie die zur Umsetzung des Konzeptes geeignete Informationstechnologie.
Beziehen Sie Java (für Client und Server), sowie Perl und PHP (für den Server) als
Programmiersprachen in die Evaluation ein. Prüfen Sie auch, ob der Einsatz von Corba sinnvoll
ist. Berücksichtigen Sie bei der Evaluation, dass die Simulation eingebettet in eine Webseite mit
gängigen Browsern verwendbar sein soll. Abschliessend erstellen Sie ein geeignetes
Objektmodell und nehmen die Implementation vor.
Wir erwarten von Ihnen u.a.:
l
l
l
l
ein angemessenes Studium von Fachliteratur und Webseiten
einen strukturierten Quellcode Ihres Programmes
eine sorgfältige Dokumentation Ihrer Arbeit1
eine Vorgehensweise nach den Grundsätzen wissenschaftlichen Arbeitens
Herr Prof. Dr. Paul Schönsleben und Herr Jörg Nienhaus vom ETH-Zentrum für Unternehmenswissenschaften (BWI) werden Sie in die Thematik einführen und Ihre Arbeit begleiten. Die
Beurteilung der Arbeit nimmt Herr Prof. Carl August Zehnder vom Institut für
Informationssysteme vor.
Zürich, 2. Oktober 2001
Prof. Dr. Paul Schönsleben
Jörg Nienhaus, Assistent
1
Als Basis für die formale Gestaltung Ihres Berichtes sowie für eine allgemeine Einführung in die Methodik des wissenschaftlichen
Arbeitens empfehlen wir: Theisen, Manuel René: Wissenschaftliches Arbeiten, 8. Aufl., München: Verlag Franz Vahlen, 1997
Seite 7/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
4
Das Beer Distribution Game
4.1
Die Entstehung2
Das traditionelle Beer Distribution Game entstand in den Anfängen der „systems dynamic“ in
den sechziger Jahren. Seit drei Jahrzehnten wird das Spiel, welches einen äusserst einfaches,
modellhaftes Produktions- und Distributionswerkzeug nachbildet, verwendet als eine Einführung
in das Systemdenken in verschiedensten Schulungen, sowie bei Aus- und Weiterbildungskursen.
4.2
Das traditionelle Beer Game3
Unter dem traditionellem Beer Game wird das ursprüngliche Brettspiel verstanden. Vier Spieler
bzw. Teams sitzen in einer Reihe und verkörpern eine Lieferkette, nämlich die Fabrik (Factory),
das Distributionszentrum (Distribution), den Grosshändler (Wholesaler) und den Einzelhändler
(Retailer). Jeder Spieler bestellt bei seinem Lieferanten die notwendigen/vermuteten Bedarfe und
versucht gleichzeitig, die Bedarfe seiner Kunden zu decken. Dabei hat er keine Kenntnisse über
die Bestellvorgänge ausserhalb seines Bereiches. Die Endkundenbestellungen (externe
Nachfrage) werden durch einen verdeckten Kartenstapel vor dem Einzelhändler erzeugt. Die
Bestände an Lager, im Wareneingang und auf Transport werden durch Spielsteine (meist eben
Bierdeckel) verkörpert. Durch die Felder Wareneingang und Transport werden die Verzögerung
durch den Transport und die Warenannahme simuliert.
Jeder Spielstein an Lager erzeugt in jeder Spielrunde Lagerkosten von $0.50. Jeder
Bestellrückstand (Backlog – Unfähigkeit, dem Bedarf seines Kunden nachzukommen) ergibt pro
Spielrunde Kosten von $1. Die Gesamtkosten pro Spielstation setzen sich somit wie folgt
zusammen:
Gesamtkosten = Σ (Lagerbestände * $0.50) + Σ (Bestellrückstände* $1.00)
Das Ziel des Retailers, Distributors, Wholesalers und der Factory ist die Gesamtkosten zu
minimieren, entweder individuell oder für das ganze System
Zu beachten ist, dass Bestellrückstände doppelt so hoch „bestraft“ werden wie Lagerbestände.
Der Moderator des Spiels achtet darauf, dass unter den Spielern keine Informationen (ausser
Bestellungen) ausgetauscht werden und veranlasst die Spieler synchron zu spielen. Sämtliche
Vorgänge (Bestände, Lieferungen, Bestellrückstände und Bestellungen) werden durch jeden
Teilnehmer auf einem Statistikblatt festgehalten. Der Moderator führt die anschliessende
Diskussion und Auswertung der Statistikblätter und fasst gewonnene Erkenntnisse zusammen.
Die Verblüffung der Spieler während der Besprechung der Ergebnisse besteht im Normalfall
darin, dass die externe Nachfrage (Kartenstapel bei Einzelhändler) durchs ganze Spiel hindurch
konstant war mit der einzigen Ausnahme von einem Sprung von vier auf acht Einheiten zwischen
der vierten und fünften Spielrunde.
2
3
Vgl: Sterman D 1992
Vgl: Burkhalter P. 2001
Seite 8/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
4.3
Theoretische Grundlagen – der Bullwhip Effekt
Das Ziel des Beer Games ist es den Teilnehmern den sogenannten „Bullwhip Effekt“ (auch
bekannt als „whiplash“ oder „whipsaw“) Effekt auf eine einfache und eindrückliche Art zu
demonstrieren4.
Der Bullwhip Effekt ist nichts anderes als eine Verstärkung der Variabilität der
Nachfrageamplitude über die Stufen einer Supply Chain Kette. Der Grund lässt sich finden in der
fehlenden (globaler) Information der tatsächlichen Nachfrage beim Endverbraucher. Oder anders
ausgedrückt: Die Bestellmuster teilen eine gemeinsame Eigenschaft. Die Variabilität in der
Bestellung für den Lieferanten (upstream) sind immer grösser als die Bestellungen des eigenen
Kunden (downstream).
Allgemeine Symptome solcher Variabilitäten können übermässige Lagerbestände, schlechte
Produktionsvorhersagen, ungenügende oder überhöhte Kapazitäten, schlechter Kundenservice
aufgrund von nicht verfügbaren Produkten oder lange Rückstände in der Lieferung sein.
Abbildung 1: Zunahme der Variabilität der Nachfrage in der Supply Chain
In der Literatur (Lee, Simchi-Levi) werden vier Hauptgründe für die Entstehung des Bullwhip
Effekt aufgeführt, welche sich aber schliesslich immer auf die (lokal) fehlende globale
Information der Verbrauchsmenge des Endkunden zurückführen lässt. Die Gründe sind:
1. Demand forecast updating
Die Vorhersage für die kommenden Bestellmengen wird mangels Informationen oft aus
vergangenen Bestellungen des direkten Kunden abgeleitet (und nicht von der tatsächlichen
Nachfrage des Endkunden). Diese Vorhersagen werden laufend angepasst und dann an den
eigenen Lieferanten weitergegeben. Da die Vorhersagen nicht nur den wahren Verbrauch,
sondern auch die jeweiligen Sicherheitsbestände beinhalten, sind die Fluktuationen der
4
Vgl: Lee H.L. et al: 1997
Seite 9/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
Bestellmengen über die Zeit oft viel grösser als die tatsächliche Nachfrage. Je länger die
Supply Chain ist, desto grösser werden die Fluktuationen.
2. Lead Time
Die Durchlaufzeit hat einen signifikanten Einfluss auf die Variabilität der Bestellungen.
Gründe lassen sich finden in der Erhöhung der Sicherheitsbestände und somit der
Bestellmengen.
3. Batch Ordering
Aus Gründen der Kosteneffizienz (Bestell- und Transportkosten) wird oft in Batches (Losen)
bestellt. Dabei werden hereinkommende Bestellungen des eigenen Kunden nicht sofort an
den eigenen Lieferanten weitergegeben sondern zuerst gesammelt. Die Auslösung einer
Bestellung kann zum Beispiel wöchentlich oder monatlich erfolgen oder dann, wenn ein „full
truck load“ erreicht wird.
Genau dies führt aber zu einer massiven Steigerung der Variabilität der Nachfrage zwischen
Kunde und Lieferant und führt zum Bullwhip Effekt.
4. Price fluctuation
Bestellungen werden oft nicht kontinuierlich ausgelöst sondern dann getätigt, wenn die Ware
gerade günstig gekauft werden kann (Währungsschwankungen, Mengenrabatte, Spezielle
Aktionen usw.). Dieses sogenannte „forward buying“ widerspiegelt offensichtlich die
tatsächliche Nachfrage nicht und zeigt sich direkt im Bullwhip Effekt wieder.
5. Inflated Orders (Rationing and shortage gaming)
Wenn der Lieferant in Verzug ist und die Bestellmengen die Kapazitäten überschreiten,
müssen die Lieferungen rationiert werden. Dies geschieht oft so, dass die Kunden nur einen
Teil (z.B. 50%) der bestellten Ware erhalten.
Ist bekannt, dass Lieferschwierigkeiten auftreten können, bestellen Kunden oft die mehrfache
Menge und/oder bei verschiedenen Lieferanten. Diese Reaktion führ zwar zu einem lokalen
Optimum (der Kunde erhält schnellstmöglich seine 100%), doch global gesehen führt dies zu
einer massiven Überschätzung des Kundenbedarfs.
Sobald sich die Lage beruhigt, verschwinden die Bestellungen plötzlich und
Bestellannullierungen häufen sich.
Lösungsansätze zur Vermeidung des Bullwhip Effekts
Natürlich lassen sich die Symptome teilweise auch lokal bekämpfen, indem zum Beispiel Preise
stabil gehalten werden (Every day low price – EDLP), Strafen für Annulationen eingeführt
werden, die Durchlaufzeiten reduziert oder externe Logistikpartner (kleinere Losgrössen,
economy of scale) zugezogen werden.
Die einzige Möglichkeit, den Bullwhip Effekt längerfristig zu vermeiden oder zumindest
einzuschränken, bleibt aber, die Information über die Nachfrage des Endkunden auf der gesamten
Lieferkette global verfügbar zu machen. Dies entspricht grundsätzlich einer Weitergabe der
jeweiligen Absatzdaten. Genau dies soll mit dem Beer Game veranschaulicht werden.
Seite 10/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
5
Situationsanalyse
5.1
Existierende Varianten des Beer Games
5.1.1 Brettspiel – Klassische Variante5
Beim klassischen Brettspiel werden die Funktionen Retailer,
Wholesaler, Distributor und Factory von je einem Mitspieler oder
einem Mitspielerteam übernommen. Kundenbestellungen liegen
anhand eines vorbereiteten, verdeckten Kartenstapels vor. Die
Auswertung erfolgt von Hand, ev. durch Nachführen einer Excel
Tabelle mit anschliessender graphischer Auswertung.
Exemplarisch ist unten das Ergebnis eines Spiels beim
Lieferantentag eines Schweizer Maschinenbau-Unternehmens zu
sehen. Die Fotos dieser Seite wurde ebenfalls bei diesem Anlass
aufgenommen.
Bestellungen
45
40
Abbildung 2: Lieferantentag
eines Schweizer MaschinenbauUnternehmens
35
30
Kunde
EinzelhŠndler
25
Grossist
20
Verteiler
Produzent
15
10
5
0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
Woche
Lagerbestand
40
20
0
0
1
2
3
4
5
6
7
8
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
EinzelhŠndler
-20
Grossist
Verteiler
Produzent
-40
-60
-80
-100
Woche
Abbildung 3: Das Beer Distribution
Game Brettspiel
5
Vgl. Sterman J. D.
Seite 11/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
5.1.2 Computerized Beer Game6
Diese
Variante
liegt
anhand einer Windowsapplikation
vor.
Im
Gegensatz
zum
klassischen
Brettspiel
kann jedoch nur ein
menschlicher Mitspieler
daran teilnehmen, die
restlichen
Funktionen
werden vom Computer
übernommen.
Als Erweiterung zum
klassischen Beer Game
wurden drei weitere Modi
eingeführt:
ð Global Information Mode:
Lagerbestände und Bestellungen der gesamten Lieferkette sind global verfügbar. Dies erlaubt
dem Mitspieler die Bestellungen besser zu planen und die Dynamik der Logistikkette besser
zu verstehen.
ð Short Lead Time Mode:
Die zweistufige Verzögerung durch den Transport und den Wareneingang wird um eine Stufe
reduziert. Dies führt zu einer schnelleren Reaktion auf Bestellungen und führt somit
normalerweise zu einem kleineren Backlog.
ð Centralized Mode:
Dem Spieler sind alle Informationen verfügbar. Die Bestellungen werden direkt in der
„Factory“ platziert und werden so schnell wie möglich durch das System zum „Retailer“
gesendet (es werden keine Lager ausser beim Retailer selbst geführt).
Zudem ist es möglich die Bestellstrategie der Computermitspieler festzulegen. Folgende
Optionen sind pro automatischem Mitspieler möglich (s, S, Q und M können als Variablen für
jede Stufe festgelegt werden):
ð s-S:
Sobald der Lagerbestand unter „s“ fällt, wird vom System eine Bestellung platziert, um das
Lager wiederum auf die Menge „S“ zu bringen
6
Vgl: Simchi-Levi 2000 (mit beiliegender CD)
Seite 12/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
ð s-Q:
Sobald der Lagerbestand unter „s“ fällt, wird eine Bestellung der Menge „Q“ ausgelöst.
ð Order to S:
In jeder Spielrunde platziert das System eine Bestellung so, dass der Lagerbestand wiederum
auf „S“ gebracht wird. (direkte Weitergabe der Bestellung vom Kunden an den Lieferanten).
ð Order Q:
Das System bestellt in jeder Runde konstant Q Einheiten.
ð Update s:
Wenn der Lagerbestand unter „s“ fällt, wird bestellt, bis „s“ erreicht wird. Die maximale
Bestellmenge beträgt „S“. Die eigene Bestellung wird aus dem Schnitt und der
Standardabweichung früherer Bestellungen errechnet.
ð Echelon:
Sobald der Lagerbestand unter „s“ fällt, wird bestellt bis „s“ erreicht wird unter Einhaltung
der maximalen Bestellmenge von S. „s“ wird für jede Station in der Lieferkette nach einer
modifizierten Version der Standard Echelon Formel berechnet.
Die Auswertung des Spiels erfolgt anhand einer einfachen Graphik (vgl. unten) und einer
Anzeige der Bestellmenge des Spielers über die einzelnen Spielperioden. Ausserdem lässt sich
von allen Stationen den Durchschnitt und die Standardabweichung der Bestellungen anzeigen.
Abbildung 4: Graph Auswertung des Computerized Beer Games
Seite 13/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
5.1.3 Simple ++ Variante7
Diese Variante wurde entwickelt in einer Studienarbeit am Betriebswissenschaftlichen Institut
der ETH Zürich. Es ging darum, verschiedene Bestellstrategien im Beer Game zu testen und
auszuwerten. Aus diesem Grund ist wiederum (maximal) nur ein Mitspieler zugelassen, die
restlichen Stationen werden interaktiv vom Computer mit der ihm zugeteilten Strategie bedient.
Diese Version bietet zudem die Möglichkeit, die Bestellmengen eines gespielten klassischen Beer
Games in entsprechende Tabellen einzugeben und danach eine Auswertung dieses Spiels
durchzuführen.
Folgende Strategien wurden implementiert:
ð Nachspielen:
Ein klassisches Beer Game wird nachgespielt (Eingabe der jeweiligen Bestellmengen in
Tabellen)
ð Interaktiv:
Einem Mitspieler wird ermöglicht, die eigene Strategie im Wettbewerb mit den verbleibenden
Spielstationen und den ihnen zugeteilten Bestellstrategien zu testen.
ð Bestellbestand:
Die Losgrösse wird anhand der klassischen Andler Formel8 bestimmt.
ð MRP (Material Requirements Planning):
Hierzu müssen die Bedarfe der Kunden bekannt sein. Diese Strategie macht aus Sicht dieser
Arbeit jedoch wenig Sinn, da es im Beer Game genau darum geht, dass diese Informationen
nicht vorhanden sind, as den zu demonstrierenden Bullwhip Effekt9 ja auslöst.
ð Between
Sobald der Lagerbestand unter eine bestimmte Menge (m) fällt wird, eine Bestellung
ausgelöst berechnet aus einer Lagerbestandsobergrenze minus die noch an Lager
vorhandenen Menge (inkl. Backlog).
ð Sensitiv
Wie „Between“, ausser, dass für den Lagerbestand auch noch die Bestände in Arbeit
(Transport, Wareneingang) berücksichtigt werden.
ð Random1X
Spezifikation der Parameter Random_min, Random_max sowie Orderstop.
7
Vgl. Burkhalter J.P. 2001
Vgl Schönsleben P. 1998
9
Vgl. Lee H.L. 1997
8
Seite 14/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
ð Strategie1X
Es wird grundsätzlich die Bestellmenge des Kunden direkt an den Lieferanten weitergegeben.
Variationen werden gemacht im Umgang mit dem Backlog.
ð Strategie2X
Bestellmenge errechnet sich durch die Mittelung der letzten y Bestellungen (moving
average). Variationen liegen wiederum im Umgang mit dem Backlog.
Eine ausführliche Beschreibung der einzelnen Strategien sowie deren Auswertung kann in der
Studienarbeit nachgelesen werden.
5.2
Beurteilung
Das Ziel dieser Arbeit ist es in erster Linie, eine Variante des Spiels zu implementieren, welche
es vier Mitspielern ermöglichen sollte, gleichzeitig miteinander online über das Internet im
selben Spiel zu spielen.
Diese Anforderung wird in keiner der bereits vorhandenen Lösungen erfüllt. Die vorliegende
Arbeit soll nun genau diese Lücke schliessen.
Seite 15/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
6
Umsetzungskonzept
6.1
Pflichtenheft / Leitlinien für die Umsetzung
Aus der Aufgabenstellung lassen sich folgende vier Hauptanforderungen herauslesen:
1. Es soll über das Internet gespielt werden können. Mehrere Spieler sollen an einem Spiel
teilnehmen können und gleichzeitig sollen mehrere Spiele gleichzeitig stattfinden können.
2. Das Spiel soll mit möglichst vielen Clients (Betriebssystemen und Browsern) direkt
kompatibel sein. Eine hohe Benutzerfreundlichkeit soll angestrebt werden.
3. Die Spieler sollen sich das Spiel, an welchem sie teilnehmen, selber auslesen können. Dies
ermöglicht es mit bestimmten Teilnehmern zu einer verabredeten Zeit zu spielen.
4. Die Auswertung der Resultate soll automatisch vorgenommen werden
Drei weitere Anforderungen erscheinen sinnvoll:
5. Der Computer soll nicht besetzte Rollen übernehmen können oder Spieler ersetzen, welche
ausgefallen sind, damit das Spiel nicht unterbrochen wird.
6. Es soll eine Zeitlimite pro Spielrunde eingeführt werden, damit ein einzelner Spieler nicht das
ganze Spiel blockieren kann. Bei Ablauf dieser Zeitlimite übernimmt der Computer die
Bestellung für den betreffenden Spieler
7. Die Spielsprache soll englisch oder mehrsprachig sein, da auch Spieler, bzw. Institute aus der
Westschweiz (EPFL Lausanne) Interesse an diesem Spiel bekundet haben.
6.2
Auswahl der Programmiersprache, Entwicklungsumgebung
In Absprache mit dem Betreuer haben wir uns auf die Programmiersprache Java festgelegt. Als
Entwicklungsumgebung wurde der JBuilder (5) von Borland gewählt. Der Client soll demnach in
Form eines Java-Applets umgesetzt werden.
Es hat sich gezeigt, dass Skriptsprachen wie Perl, PHP, ASP und Javascript für die obigen
Anforderungen wenig geeignet sind. Gründe sind unter anderem:
è Zuwenig/nicht Interaktiv (z.B. für bewegte Abläufe/Animationen oder clientseitige
Validierung von Eingabeparametern)
è Code wird bei grossen Programmen schnell unübersichtlich
Programmiersprachen haben diese Nachteile weniger. Insbesondere Java hat sich geradezu
angeboten für die Umsetzung dieser Aufgabe. Zudem wird bei Java die Möglichkeit geboten,
eine ausgewachsene Entwicklungsumgebungen zu verwenden.
Seite 16/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
6.3
Java – offene Probleme 10
Um ein Applet sauber zum laufen zu bringen müssen viele Feinheiten beachtet werden. Zum
Beispiel variieren die verschiedenen Browser (wie zum Beispiel der Internet Explorer oder
Netscape) im Level der Unterstützung des JDK und jeder Browser verfolgt einen anderen Ansatz
in der Implementation von Java.
6.3.1 Java Support
Ein verbreitetes Problem ist, dass das JDK mit welchem das Applet entwickelt wurde nicht
kompatibel sind mit den Virtual Machines (VM) der verschiedenen Browser. Viele Benutzer
verwenden noch „alte“ Browser, welche JDK1.1.5 oder frühere Versionen benutzen. Swing wird
erst in JDK 1.1.7 eingeführt. Es ist sogar so, dass Java 2 – welches Swing voll unterstützt –
teilweise nicht einmal in der neusten Browsergeneration implementiert ist.
Folgende Browser unterstützen das JDK 1.1:
è Netscape 4.06 (und höher)
è Internet Explorer 4.01 (und höher)
6.3.2 Unterschiede in der Java Implementation
Es gibt zwar Richtlinien und Spezifikationen welche (Java-) Funktionen von den Browsern
unterstützt werden müssen, die Implementation derselben sowie aller Erweiterungen bleibt
jedoch den Herstellern überlassen.
Ein Unterschied liegt zum Beispiel in der Implementation des „Security Managers“ sowie der
„Security Levels“. So wird „Medium Security“ in verschiedenen Browsern verschieden
interpretiert und verschiedene Aktionen werden zugelassen oder eben nicht.
Ausserdem werden von den Herstellern sogar Anpassungen in der Kernfunktionalität von Java
gemacht, inklusive der vollständigen Auslassung bestimmter Eigenschaften. Dies führt zu
unterschiedlichen (Fehl-) Verhaltensweisen der Applets in verschiedenen Browsern. Es ist
praktisch unmöglich, immer um alle diese Fehler herumzukodieren.
Ein weiterer Unterschied ist festzustellen beim Verhalten der Applets auf verschiedenen
Betriebssystemen desselben Browsers. Beispielsweise können „multi-threaded applets“ auf einer
Plattform laufen, auf einer anderen aber nur sequentiell ausgeführt werden aufgrund von
Problemen des „Threading Models“. (Macintosh zum Beispiel unterstützt das Multitasking erst
ab System OSX vollständig).
Diese Unterschiede machen ein ausgedehntes Testen und Debuggen unumgänglich.
10
Vgl: Web Application Developer’s Guide, Borland Software Corporation
Seite 17/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
6.4
Lösungsansätze für die Implementierung11
6.4.1 Ausschliessliche Verwendung von JDK 1.1.x und früher
Programmieren der Applets in JDK 1.1.x und früheren Versionen beinhaltet eine Limitation auf
die Funktionalität (und Bugs) dieser Versionen. Die meisten Browser unterstützen nun JDK
1.1.5, nicht aber die Swing Komponenten, welche in JDK 1.1.7 eingeführt werden.
Wenn Applets mit modernen Entwicklungsumgebungen (basierend auf Java2) geschrieben
werden, muss streng darauf geachtet werden, dass nur AWT Komponenten verwendet werden
und nicht von den neuen Komponenten Gebrauch gemacht wird.
Der grösste Nachteil in der Verwendung von JDK 1.1.x und früher liegt darin, dass die
komfortablen, erweiterten Funktionen von Java2 nicht genutzt werden können. Zudem muss
Rücksicht auf die Umsetzung in den verschiedenen Browsern und Betriebssystemen genommen
werden.
6.4.2 Installieren von nicht Kern Java Klassen auf den Clients
Wenn nicht Kern Java Klassen auf den Clients installiert werden, kann unter Anpassung des
„Classpath“ ein download umgangen werden für Klassen, welche vom Applet verwendet werden.
Dies ist jedoch grundsätzlich nur in einer Intranet-Umgebung möglich.
6.4.3 Unterstützung für nur einen Browsertyp
Wenn das Applet für nur eine Version eines bestimmten Browsers programmiert wird, kann der
Aufwand für speziellen Code und Unterhalt sowie umfangreichem Debugging minimiert werden.
Dies in Kombination mit einer Implementation in JDK 1.1.x würde viele Probleme verhindern.
Dies ist jedoch wiederum nur in Intranet Umgebungen möglich, welche durch eine „IS policy“
geregelt werden.
6.4.4 Verwenden des Java Plug-in
Die meisten Browserprobleme können durch die Verwendung des Java Plug-in von Sun
umgangen werden. Dieses Plug-in ist derzeit die einzige Möglichkeit, allen Browsern dieselbe
Version des JDK zur Verfügung zu stellen. Ein grosser Vorteil dieser Lösung zeigt sich
ausserdem darin, dass auch die Swing Klassen (Java2) unterstützt werden.
Wird von einem Browser zum ersten Mal eine „plug-in enabled“ HTML Seite aufgerufen, erfolgt
automatisch die Aufforderung, das Plug-in herunterzuladen. Einmal installiert hat die Client
Maschine die offizielle Virtual Machine von Sun und das aktuellste JDK (inklusive Swing) lokal
installiert.
Der grösste Nachteil bei dieser Variante ist die Notwendigkeit eines einmaligen Downloads von
etwa 5MB.
11
Vgl: Web Application Developer’s Guide, Borland Software Corporation
Seite 18/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
6.4.5 Verwenden von Java Web Start12
Java Web Start ist eine neue Applikationsentwicklungstechnologie von Sun Microsystems,
welche das Ausführen beliebiger Applets oder ausgewachsener Java Applikationen von einem
Link auf einer Webseite im Browser ermöglicht. Wenn das Programm nicht bereits auf dem
Computer vorhanden ist, werden alle nötigen Daten heruntergeladen und lokal in einem Cache
gespeichert, so dass sie auch zu einem späteren Zeitpunkt wieder ausgeführt werden können.
Sicherheit wird gewährleistet durch die Ausführung der Programme in einer sogenannten
„Sandbox“, welche vergleichbar ist zur „Sandbox“, in welcher die herkömmlichen Applets
ausgeführt werden. Ausserdem ermöglicht es Java Web Start die neuste Java 2 Technologie zu
verwenden – mit jedem beliebigen Browser.
Diese Variante kombiniert die Vorteile einer Java Applikation mit einer sicheren Umgebung. Der
Nachteil besteht darin, dass Java Web Start sehr neu ist und noch kaum auf einem Computer
installiert ist. Der Download (full package international) hat eine Grösse von 8.7MB.
6.5
Zusammenfassung der Lösungsansätze
Lösungsansatz
Verwendung von...
maximal sJDK 1.1.x
Vorteile
Nachteile
Kein Download nötig - Technologie Es werden verschiedene JDKs
wird von den meisten gängigen verwendet
welche
zu
Browsern unterstützt
Inkompatibilitäten führen können.
Eine Beschränkung auf bestimmte
Browserversionen ist notwendig
Neuere Features (Swing...) werden
nicht unterstützt.
Dasselbe JDK für alle Betriebssystem Download des Java Plug-in 1.3
und Browser
notwendig (einmalig ca. 5 MB)
Java Plug-in
Hochgradig kompatibel
Unterstützt alle Funktionalitäten von
Java 2 (inklusive Swing)
Java Web Start
Java Applikationen (und Applets) Java Web Start muss lokal installiert
können übers Internet
in einer sein (einmaliger Download von 8.7
sicheren
Umgebung
ausgeführt MB)
werden.
Hochgradig kompatibel
Unterstütz alle Funktionalitäten von
Java 2 (inklusive Swing)
12
Vgl: http://java.sun.com/products/javawebstart/
Seite 19/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
6.6
Die Wahl
Obwohl sowohl das Plug-in als auch Java Web Start grosse Vorteile in Bezug auf
plattformübergreifende Kompatibilität aufweisen, wurde zusammen mit dem Betreuer die Lösung
der ausschliesslichen Verwendung des JDK 1.1.x gewählt.
Der Grund, welcher den Ausschlag gegeben hat liegt in der Benutzerfreundlichkeit. Ein
Download wird so nicht vorausgesetzt und jedermann hat direkten Zugriff auf das Spiel. Diese
Lösung schliesst jedoch die Verwendung von Swing aus. Zudem wird ein umfangreiches Testen
für die verschiedenen Betriebssysteme und Browsern notwendig.
Seite 20/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
7
Diskussion und Auswahl der Architektur
Eine Client-Server Architektur zeigte sich bald als ideal für die Umsetzung dieses Spieles. Dies
aus mehreren Gründen:
1. Applets können aus Sicherheitsgründen nur mit dem Server kommunizieren, von dem sie
heruntergeladen worden sind. Eine direkte Kommunikation zwischen Applets ist nicht
möglich.
2. Der Server kann alle Spielstände halten – bei Ausfall eines Clients gehen somit keine
Informationen verloren.
3. Der Server kann beendete Spiele speichern und für Auswertungen weiterverwenden oder zu
einem späteren Zeitpunkt wieder zugänglich machen.
Punkt 1 könnte zwar umgangen werden, indem nicht Applets, sondern Java Applikationen auf
den Clients verwendet werden. Diese Lösung bietet dem Spielteilnehmer aber die grossen
Vorteile der Sicherheit der Applets nicht mehr und kommt aus diesem Grund nicht in Frage.
Zudem würde sich eine Client-Server Architektur auch ohne diese Einschränkung geradezu
anbieten.
7.1
Umsetzungsentscheid Programmarchitektur
7.1.1 Der Client
Der Client wird in Form eines Java Applets umgesetzt. Der Client erhält alle relevanten Daten
zum Spiel direkt vom Server und kommuniziert ausschliesslich mit diesem. Das GUI besteht aus
obgenannten Gründen (vgl. Umsetzungskonzept) ausschliesslich aus AWT Komponenten.
7.1.2 Der Server
Der Server selbst ist eine Java Applikation, welche die gesamte Spiellogik beinhaltet. Neben der
Zuweisung der verschiedenen Clients zu den Spielen hat er auch die Aufgabe, verschiedene nicht
besetzte Rollen selbst zu übernehmen. Zudem hat er die Aufgabe, beendete Spiele abzuspeichern,
um sie zu einem späteren Zeitpunkt wieder abrufen zu können.
Seite 21/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
7.2
Kommunikationskonzept
In Java gibt es drei Möglichkeiten über das Internet zu kommunizieren. Diese werden
nachfolgend kurz besprochen:
7.2.1 http-requests (request/response)
Der Vorteil von http-requests ist, dass diese Methode sehr einfach ist. Zudem benutzt sie nur den
Port 80 und hat somit keine Probleme mit Firewalls. Http-requests funktionieren aber nur
unidirektional, das heisst, der Server kann von sich aus nicht kommunizieren und die Verbindung
bleibt nicht dauerhaft bestehen. Da nur über einen Port kommuniziert wird, muss die Benutzer-ID
jedem request mitgegeben werden. Ein Broadcast von Nachrichten oder nur schon das Versenden
einer Nachricht von Client-Server-Client ist schwierig und setzt ein regelmässiges Abfragen des
Servers durch alle Clients voraus. Dies würde für unser Spiel zwangsläufig einen recht hohen
Kommunikations-Overhead erzeugen.
7.2.2 Socket connection
Die Socket connection bietet eine sehr schlanke und effiziente Kommunikation: Die Verbindung
ist bidirektional, das heisst, Client und Server können kommunizieren und die Erkennung der
Clients erfolgt über die Portnummern.
Die Nachteile dieser Kommunikationsart liegen in der komplexeren Umsetzung – ein eigenes
Kommunikationsprotokoll muss implementiert werden. Zudem wird eine Rekonfiguration
eventueller Firewalls notwendig, da weitere Ports neben dem Port 80 für die Kommunikation
benötigt werden. In unseren Tests mit den interessierten Instituten der EPFL und der UNI Bern
zeigten sich aber keinerlei Probleme.
7.2.3 RMI – Remote Method Invocation
RMI ermöglicht wie die Socket Connection eine bidirektionale Kommunikation. RMI ist sehr
elegant, da die Kommunikation über direkte Methodenaufrufe von Server auf Client und
umgekehrt erfolgt. RMI ist objektorientiert.
Die Nachteile liegen wiederum in der komplexen Umsetzung – es werden spezielle Stub und
Skeleton Klassen benötigt. Zudem ist eine RMI Registry notwendig. Leider hat diese bei
Internetverbindungen (noch) oft Schwierigkeiten. Zudem wird eine RMI Verbindung von den
meisten HTTP-Proxyservern nicht zugelassen. Dies bedeutet, dass RMI via http getunnelt werden
muss, was über die Anbindung an einen beliebigen Port passiert. Eine Rekonfiguration
eventueller Firewalls ist auch hier unumgänglich.
7.2.4 Umsetzungsentscheid
Für die Umsetzung dieses Spiels haben wir uns für die Socket Connection entschieden. Der
Grund liegt vor allem in der bidirektionalen, sauberen und effizienten Kommunikation. Die
Socket Connection ist ausserdem weniger problematisch als RMI.
Seite 22/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
8
Die Umsetzung
Für die Umsetzung wurde je ein Package für den Server und eins für den Client erstellt. Im
Folgenden werden die einzelnen Packages (alphabetisch) mit ihren Klassen kurz vorgestellt. Eine
ausführlichere Beschreibung der wichtigeren Klassen befindet sich in den Kapiteln 9 und 10.
8.1
Kurzübersicht über die Packages und ihre Klassen
8.1.1 Das Package „beergame.server
BeergameServer.java
Das eigentliche Programm mit der main() Methode. Beim
Aufstarten liest er die gespeicherten PlayerId’s und initialisiert
das Spiel. Während der Laufzeit verwaltet er die Spiel- und
Spielerlisten. Als Parameter kann die Anzahl zugelassener
Verbindungen (resp. Spieler) übergeben werden
Diese Klasse sollte in das Startup-Verzeichnis des hostenden
Servers aufgenommen werden.
(Ausführliche Beschreibung: Abschnitt 9.1, Seite 29)
Game.java
Die Spiellogik. Verwaltet und steuert das jeweilige Spiel. Hier
werden Spiel- und Spielermodi, alle Stati, Namen,
Bestellungen, Lagerbestände usw. gespeichert. Ebenso sind
alle Methoden enthalten, welche für die Spielsteuerung
zuständig sind.
Pro Spiel wird eine Instanz dieser Klasse erzeugt. Wird ein
Spiel regulär abgeschlossen, wird das jeweilige Objekt auf die
Festplatte in den Ordner „Saved Games“ ausgeschrieben. Dies
ermöglicht es später, auch bei einem Server-Shutdown wieder
auf die Spiele zuzugreifen.
(Ausführliche Beschreibung: Abschnitt 9.6, Seite 34)
HelloThread.java
Der Thread welcher für den Verbindungsaufbau
verantwortlich ist. Er hört auf einem vorgegebenen basePort,
welcher die Applets auch kennen. Sobald ein Applet gestartet
wird, teilt dieser Thread der neuen Verbindung einen fest
zugewiesenen Port zu, auf dem die zukünftige
Kommunikation dann stattfindet
(Ausführliche Beschreibung: Abschnitt 9.2, Seite 30)
Seite 23/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
ListenThread.java
Pro neue Verbindung wird ein ListenThread erzeugt. Dieser
wartet auf Nachrichten des entsprechenden Clients und leitet
diese weiter.
(Ausführliche Beschreibung: Abschnitt 9.4, Seite 33)
PlayerId.java
Die Spielerverwaltung. Enthält die Spielerlisten und pro
Spieler einen Vektor der bereits gespielten Spiele. Enthält
auch die Methoden um gespeicherte Spiele von der Festplatte
auszulesen. Diese Klasse wird in regelmässigen Abständen
auf Veränderungen geprüft und auf die Festplatte
ausgeschrieben.
(Ausführliche Beschreibung: Abschnitt 9.5, Seite 33)
SendThread.java
Pro Verbindung wird ein SendThread eröffnet. Dieser sendet
die Servernachrichten an den ihm zugehörigen Client. Der
SendThread enthält für den jeweiligen Spieler auch alle
relevanten (spielerspezifischen) Informationen.
(Ausführlich Beschreibung: Abschnitt 9.3, Seite 30)
Strategy.java
Diese Klasse enthält die verschiedenen Strategien, welche den
Computerspielern zugeteilt werden können. Dazu werden
unter anderem Methoden zur Verfügung gestellt, um den
Moving-Average sowie die Standardabweichung über
beliebige Datensätze zu berechnen.
(Ausführliche Beschreibung: Abschnitt 9.7, Seite 37)
Seite 24/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
8.1.2 Das Package „beergame.client“
Das Package „beergame.client“ enthält folgende Klassen (in alphabetischer Reihenfolge):
BeergameClient.java
Das Applet selbst, welches für den Verbindungsaufbau mit
dem Server verantwortlich ist zudem vor allem das GUI mit
den Action Listeners enthält.
(Ausführliche Beschreibung: Abschnitt 10.1, Seite 39)
ClientListener.java
Ein Thread, welcher auf dem zugewiesenen Port auf Messages
vom Server wartet, diese dann verarbeitet und weiterleitet
(Ausführliche Beschreibung: Abschnitt10.2, Seite 42)
ClientSender.java
Ein Thread, welcher Nachrichten über eine Socket Connection
vom Client an den Server verschickt
(Ausführliche Beschreibung: Abschnitt 10.3, Seite 43)
DlgGameStart.java
Ein Dialog, der vor dem Spielstart aufgerufen wird und
abfragt, ob die Spieler bereit sind.
DlgJoinGame.java
Ein Dialog, der das Interface zur Verfügung stellt, einem Spiel
beizutreten. Er stellt die Funktionen zur Verfügung, die
gewünschte Rolle auszuwählen und enthält Infos zum Spiel.
Bei Änderungen (z.B. eine Rolle wird an einen anderen
Spieler vergeben) wird er automatisch aktualisiert.
(Ausführliche Beschreibung: Abschnitt 10.12, Seite 56)
DlgJoinRequest.java
Ein Dialog der den Spielinhaber um Zutrittserlaubnis für
andere Spieler anfragt.
DlgLogon.java
Ein Dialog, der beim Aufrufen der Beergame Homepage
erscheint und den Benutzer nach seinem Namen und
Emailadresse (=ID) abfragt.
DlgNewGame.java
Ein Dialog, welcher dem Spieler die Möglichkeit bietet ein
neues Spiel zu kreieren. Unter anderem können hier der
Spielmodus, die Computerspieler sowie verschiedene
Spielparameter festgelegt werden.
(Ausführliche Beschreibung: Abschnitt 10.11, Seite 55)
Seite 25/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
GameLogic.java
Methoden, welche die eigentliche Spiellogik des Clients
enthalten und den gesamten Spielablauf sowie den Status des
Clients festhalten und steuern.
(Ausführliche Beschreibung: Abschnitt 10.4, Seite 43)
ListData.java
Eine kleine Klasse, welche (ursprünglich) die vom Server
empfangenen Listen identifiziert. Die Klasse wird kaum mehr
gebraucht, da der grösste Teil der Listen mittels Arrays und
Vectors versandt wird.
ModalParent.java
Ein Interface, welches bei Dialogen die Parent-Klasse auf
Modal stellt (Browser unterstützen keine modalen Dialoge im
Moment).
NextRoundTransition
Ein Thread, welcher an die Klassse gameLogic.java angehängt
ist. Er ist u.a. verantwortlich für die Animationen nachdem
eine neue Runde gestartet hat (Ankunft Lastwagen, Ankunft
der neuen Bestellung)
(Beschreibung: Abschnitt 10.6, Seite 48)
OrderPerformedTransition.java
Eine Thread, welcher an die Klasse gameLogic.java
angehängt ist. Er ist verantwortlich für die Animationen.
Unter anderem verschiebt er die Fässer und lässt den
Lastwagen fahren.
(Beschreibung: Abschnitt Error! Reference source not
found., Seite Error! Bookmark not defined.)
PnlGoodsReceiving.java
Ein Panel, welches in BeergameClient.java importiert wird
und die Graphik und Animation des Wareneingangs zur
Verfügung stellt.
(Beschreibung: Abschnitt 10.13, Seite 57)
PnlImage
Eine allgemeine Klasse, welches die Klasse Panel um die
Funktion erweitert ein Bild an einer beliebigen Stelle zu
platzieren.,
PnlSignal.java
Ein Panel, welches das Lichtsignal zur Verfügung stellt,
welches den Status der einzelnen Spieler anzeigt. Die Lichter
können einzeln angesteuert werden.
Seite 26/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
PnlStatistics.java
Ein Panel, in dem die verschiedenen Auswertungscharts
gezeichnet werden. Es werden umfangreiche Funktionen zur
Verfügung gestellt um einen oder mehrere Graphen pro Chart
zu zeichnen und dynamisch zu aktualisieren. Die Achsen
können automatisch den übergebenen Werten angepasst
werden.
(Ausführliche Beschreibung: Abschnitt 10.10, Seite52)
PnlStock.java
Ein Panel, welcher die Graphik sowie die Animation für das
Lager zur Verfügung stellt.
(Beschreibung: Abschnitt 10.13, Seite 57)
PnlTransport.java
Ein Panel, welcher die Graphik sowie die Animation für den
Transport zur Verfügung stellt.
(Beschreibung: Abschnitt 10.13, Seite 57)
Ref.java
Eine Klasse, welche Referenzen (wie Spielerstati, Spielmodi,
usw.) auf Integers abbildet, um den Kommunikationsaufwand
klein zu halten. Diese Klasse wird auch vom Server
importiert.
(Ausführliche Beschreibung: Abschnit 10.9,Seite 52)
Res.java
Eine Klasse, welche alle String Ressourcen enthält. Dies
ermöglicht es, den Client in mehreren Sprachen anzubieten.
Dieses Klasse wird auch vom Server importiert.
(Ausführliche Beschreibung: Abschnitt 10.8, Seite 51)
ServerMessage.java
Die Klasse, welche die verschiedenen Formate der
ServerMessages (Stings, Integers, Arrays, Vectors) definiert
sowie alle ServerMessages als Referenz enthält um
Programmierfehlern vorzubeugen.
(Ausführliche Beschreibung: Abschnitt 10.7, Seite 50)
8.1.3 Das Package „beergame.client.images“
Dies ist ein Sub-Packge von beergame client und enthält die Bildressourcen für das Spiel.
Seite 27/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
8.2
Das Spielkonzept
Wie bereits erwähnt können die Applets aus Sicherheitsgründen untereinander nicht
kommunizieren. Die Clients in der obigen Darstellung haben also keine direkte Verbindung
untereinander.
Um die verschiedenen Clients im Server unterscheiden zu können wird pro Client ein Send- und
ein Listen- Thread erzeugt, welcher die Applets eindeutig identifiziert. Somit entsteht im Server
eine virtuelle Abbildung der Clients, welche es den realen Cients ermöglicht quasi direkt
miteinander zu kommunizieren.
Wird eine Message einem Send Thread mittels der Methode „addMsg“ übergeben, sendet dieser
diese Message automatisch an seinen Client weiter. Umgekehrt, wenn der Listen Thread des
Servers eine Message von seinem Client erhält, hängt dieser dessen Id an die Nachricht an.
Danach wird die Message-Id angeschaut und die Message mit den Message-Parametern an die
entsprechende Methode weitergeleitet, welche sich entweder im eigenen SendThread, in einem
anderen SendThread oder direkt in der Klasse Game befindet.
Eine Identifizierung des Clients im Server ist möglich, da pro Client-Server-Verbindung eine
eigene Portnummer verwendet wird und somit jeder ListenThread nur Messages seines eigenen
Clients erhält.
Seite 28/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
9
Klassen des Package beergame.server
9.1
BeergameServer.java13
Die Klasse BeergameServer ist die main( ) – Klasse des Servers. Um den Server zu starten wird
also diese Klasse aufgerufen. Sinnvollerweise sollte diese Klasse automatisch gestartet werden
durch ein Bootscript auf dem jeweiligen hostenden Server.
Als Argument kann die Anzahl zulässiger gleichzeitiger Verbindungen übergeben werden. Dies
java –classpath “ ... “ beergame.server.BergameServer –connections=50
entspricht genau der Anzahl Ports, welche im Server reserviert werden. Standardmässig ist 200
eingestellt.
public class BeergameServer extends Thread {
public final static int basePort = 8890;
static Hashtable playerlist = new Hashtable();
static Hashtable gamelist = new Hashtable();
static boolean availablePort[];
static int maxConnections = 200;
private static boolean shutdown = false;
private static int warningTime = 60; //minutes
private static int socketnum ;
...
Als Basis Port ist 8890 eingestellt. Dies
bedeutet, dass die erste Verbindung den Port
8890 zugeteilt bekommt, für jede weitere
Verbindung wird ein Port höher oder ein
freigewordener Port dazwischen verwendet.
Der
HelloThread,
welcher
für
den
Verbindungsaufbau zuständig ist, hört immer
auf dem Basisport minus 1, in diesem Fall also
auf 8889.
Weiter verwaltet die Klasse BeergameServer zwei Hashtables und zwar eine für die Spieler- und
eine für die Spielliste. Die Spielerliste enthält die Namen aller Spieler, die Online sind. Damit die
Spieler und Spiele auch eindeutig identifiziert werden können, müssen die Namen jeweils
eindeutig sein. Sobald ein Spieler sich aber abmeldet oder ein Spiel beendet wurde, steht der
Name wieder zur Verfügung.
Ein sauberer „Shutdown“ kann vollzogen werden, indem im Verzeichnis, in dem sich auch das
Package beergame.server befindet, ein (leeres) Textfile erzeugt oder abgelegt wird mit dem
Namen „shutdown.txt“. Dies wird am besten automatisch durch den hostenden Server vollzogen
durch einen Eintrag im „Shutdown-script“. Die Klasse BeergameServer prüft mittels eines
Threads periodisch (jede Minute), ob sich ein File dieses Namens in dem oben erwähnten
Verzeichnis befindet. Falls ja, wird allen Clients eine Meldung geschickt, dass der Server in einer
bestimmten Zeit (=warningTime) abgeschaltet wird. Gleichzeitig werden keine neuen
Verbindungen mehr akzeptiert. Standardmässig ist hier eine Stunde eingestellt. Diese Zeit sollte
genügen, dass alle Spiele noch zu Ende gespielt werden können.
13
Diese Klasse wurde in Anlehnung an das Musterbeispiel des JBuilder programmiert. Vgl.
JBuilder 5 Professional, Samples, Chess
Seite 29/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
HelloThread.java14.
9.2
Wie der Name der Klasse schon sagt, ist der HelloThread ein Thread, der für den
Verbindungsaufbau verantwortlich ist. Der BeergameServer instanziert in der run() Methode
genau einen HelloThread. Er liest sich aus dem BeergameServer die maximale Anzahl der
Verbindungen aus und reserviert sich die Ports.
Die eigene run() – Methode wartet auf eine Verbindung eines Clients auf dem Port 8889. Sobald
sich eine Client mit der Message „Hello“ meldet, wird diesem mittels der Methode
getPortNumber() des BeergameServers einen neuen Port zugewiesen. Gleichzeitig wird ein neuer
SendThread erzeugt, der dann wiederum einen ListenThread für diese Verbindung instanziert.
Damit ist eine neue Verbindung eröffnet und der HelloThread wartet wiederum auf den nächsten
Verbindungsversuch eines Clients.
Der HelloThread besitzt eine finalize()- Methode, welche den Socket bei Abbruch des
Programms schliesst. Im Anhang 13.1 befindet sich eine Skizze, welche den gesamten Ablauf ein
wenig deutlicher zu veranschaulichen versucht.
9.3
SendThread.java
Für jede Verbindung wird durch den HelloThread eine Instanz der Klasse SendThread erzeugt.
Die Instanz SendThread hat grundsätzlich zwei Aufgaben. Zum einen leitet sie Nachrichten an
den zugehörigen Client weiter, zum anderen ist sie aber auch verantwortlich für das Handling
empfangener Nachrichten durch den ListenThread. Der SendThread ist als virtuelles Abbild des
Clients im Server zu verstehen. Die Zustände des Clients werden also im SendThread
gespeichert:
class SendThread extends Thread {
public int myChainLink;
public boolean isReady =false;
public boolean timeout =false;
private final int port;
private Vector msgque = null;
private Vector playedGameVector = null;
private ServerSocket serverSocket = null;
private ObjectOutputStream os ;
private int timeoutCount=0;
private boolean isPlaying =false;
private Game game = null;
private PlayerId playerId;
private Strategy strategy;
...
è Die Variable „myChainLink“ beinhaltet die Rolle des
Clients. (0=Customer, 1=Retailer, 2= Wholesaler, 3=
Distributor, 4= Factory).
è Der Vektor „playedGameVector“ beinhaltet eine
Referenz auf alle Spiel, welche der zugehörige Spieler
bereits beendet hat.
è In der Variablen „game“ ist die Referenz auf die Instanz
des Objekts Game gespeichert, welches der Spieler
entweder selbst erzeugt hat, oder bei dem er beigetreten
ist. (vgl. auch Klasse Game.java)
è Die Variable „playerId“ enthält alle Informationen zum
Spieler selbst (vgl. auch Klasse PlayerId.java)
è Die Variable "strategy" enthält die Spielstrategien für Computerspieler. Diese wird benötigt, falls
der Client unerwarteterweise ausfällt. In diesem Fall übernimmt der SendThread die letzte
Bestellung für die aktuelle Runde (falls diese noch nicht ausgeführt ist) und übergibt die
gespielte Rolle dem Computer zum weiterspielen (vgl auch Klasse Strategy)
14
Diese Klasse wurde in Anlehnung aus einem mitgelieferten Musterbeispiel des JBuilder
programmiert .Vgl. JBuilder 5 Professional, Samples, Chess
Seite 30/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
Der SendThread arbeitet folgendermassen: Empfangene Nachrichten werden vom ListenThread
in den Vektor „msgque“ (=message
...
smsg = (ServerMessage) msgque.firstElement();
Queue) eingefügt. Diese Nachrichten
msgque.removeElementAt(0);
...
werden dann der Reihe nach
if (smsg.msgid.equals(ServerMessage.BYE) || smsg.msgid.equals(ServerMessage.DEAD)) {
handleBye(smsg);
abgearbeitet. Als erstes wird die
if (smsg.msgid.equals(ServerMessage.DEAD))
break alive;
Message-ID angeschaut und dann
}
else if (smsg.msgid.equals(ServerMessage.NAME))
aufgrund dieser entschieden, welche
handleName(smsg);
else if (smsg.msgid.equals(ServerMessage.LIST))
Methode für die Verarbeitung zuständig
sendMessage(smsgList(),os);
else if (smsg.msgid.equals(ServerMessage.GAME_LIST))
sendMessage(new
ist oder ob die Nachricht an den eigenen
ServerMessage(0,ServerMessage.GAME_LIST,getGameList()),os);
else if (smsg.msgid.equals(ServerMessage.GET_SAVED_GAME))
Client gesandt werden soll (siehe links).
handleGetSavedGame(smsg);
else if (smsg.msgid.equals(ServerMessage.APPLY_NEW_GAME))
handleApplyNewGame(smsg);
else if (smsg.msgid.equals(ServerMessage.CREATE_NEW_GAME))
handleCreateNewGame(smsg);
else if (smsg.msgid.equals(ServerMessage.NEW_GAME_CANCELLED))
handleNewGameCancelled(smsg);
else if (smsg.msgid.startsWith(ServerMessage.INVITE))
handleInvitation(smsg);
else if (smsg.msgid.startsWith(ServerMessage.JOIN))
handleJoin(smsg);
else if (smsg.msgid.startsWith(ServerMessage.GAME_ACCEPT))
handleGameAccept(smsg);
else if (smsg.msgid.equals(ServerMessage.REFUSE_INVITATION)
handleRefuseInvitation(smsg);
else if (smsg.msgid.equals(ServerMessage.REFUSED_INVITATION))
handleRefusedInvitation(smsg);
else if (smsg.msgid.equals(ServerMessage.R_GAME_UPDATE))
sendMessage(smsg,os);
else if (smsg.msgid.equals(ServerMessage.SETUP_GAME))
sendMessage(smsg,os);
else if (smsg.msgid.equals(ServerMessage.QUIT_GAME))
handleQuitGame();
else if (smsg.msgid.equals(ServerMessage.SETUP_COMPUTER_GAME))
sendMessage(smsg,os);
else if (smsg.msgid.equals(ServerMessage.ADD_PLAYER))
sendMessage(smsg,os);
else if (smsg.msgid.equals(ServerMessage.REMOVE_PLAYER))
sendMessage(smsg,os);
else if (smsg.msgid.equals(ServerMessage.GAME_IS_READY))
sendMessage(smsg,os);
else if (smsg.msgid.equals(ServerMessage.CLIENT_READY))
handleClientReady(smsg);
else if (smsg.msgid.equals(ServerMessage.CLIENT_REPORTS_READY))
sendMessage(smsg,os);
else if (smsg.msgid.equals(ServerMessage.START))
sendMessage(smsg,os);
else if (smsg.msgid.equals(ServerMessage.ORDER_PERFORMED))
handleOrderPerformed(smsg);
else if (smsg.msgid.equals(ServerMessage.TIME_UP))
handleTimeUp(smsg);
else if (smsg.msgid.equals(ServerMessage.CLIENT_REPORTS_TIME_OUT))
sendMessage(smsg,os);
else if (smsg.msgid.equals(ServerMessage.HUMAN_PART_TO_COMPUTER))
sendMessage(smsg,os);
else if (smsg.msgid.equals(ServerMessage.NEXT_ROUND))
sendMessage(smsg,os);
else if (smsg.msgid.startsWith(ServerMessage.CHAT))
handleChat(smsg);
else if(smsg.msgid.equals(ServerMessage.STATUS))
sendMessage(smsg,os);
else if(smsg.msgid.equals(ServerMessage.GAME_OVER)) {
sendMessage(smsg,os);
handleGameOver();
...
Es würde wenig Sinn machen, auf jede
Methode einzeln einzugehen, die
wichtigsten werden aber im Folgenden
kurz beschrieben:
•
addMsg(): Fügt eine neue Message in
den Vector „msgque“ ein
•
handleName(): Identifiziert den Spieler
anhand seiner Email-Adresse und prüft
das Passwort. Dieses ist momentan
standardmässig
auf
„password“
eingestellt und wird nicht abgefragt.
Falls der Spieler existiert, wird eine
Liste seiner bereits gespielten Spiele
zurückgegeben, ansonsten wird eine
neue Spieler-Id (=PlayerId) erzeugt.
•
handleChat():
Sendet
je
nach
Spielmodus eine Chatnachricht als
Broadcast an alle Spieler, nur an die
Spieler des eigenen Spiels oder gar
nicht weiter.
•
sendToAllComakers(): Diese Methode
sendet eine übergebene Nachricht an
alle Mitspieler weiter.
•
•
updateIdlePlayers() und updateGameList(): Führen die jeweiligen Listen in den Clients nach.
•
handleApplyNewGame(): Prüft, ob der Spieler berechtig ist, ein neues Spiel zu erzeugen. Falls
der Spieler noch kein Game referenziert hat und „isPlaying=false“ ist, wird
GRANT_NEW_GAME zurückgegeben, ansonsten REFUSE_NEW_GAME.
Seite 31/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
•
handleCreateNewGame(): Erzeugt ein neues Objekt „game“ mit den übergebenen Parametern
•
handleNewGameCancelled(): Setzt den Spieler wieder auf „isPlaying=false“ zurück und
aktualisiert die Spielerliste.
•
handleJoin(): Diese Methode ist verantwortlich für den gesamten Prozess einem Spiel als Spieler
beizutreten und danach bei Erfolg dem neuen Spieler die Spielparameter zu
übergeben(setupThisPlayer). Dabei wird im SendThread des neuen Spielers unter anderem eine
Referenz auf die entsprechende Objekt „game“ gemacht.
•
handleInvitation(): Ist ähnlich wie die handleJoin() Methode, nur, dass hier ein Spieler eingeladen
wird, einem Spiel beizutreten. Der eingeladene Spieler kann die Einladung entweder annehmen
(und seine Rolle aussuchen) oder ablehnen.
•
handleClientReady(): Prüft vor dem Spiel, ob alle Spieler zugewiesen und bereit sind und startet
danach das Spiel.
•
handleTimeUp(): Falls ein Spieler die Zeitlimite überschritten hat, übernimmt der Computer die
Bestellung für die entsprechende Rolle. Dies verhindert, dass ein einzelner Spieler das gesamte
Spiel blockieren kann.
•
handleOrderPerformed(): Diese Methode übernimmt die Bestellung des jeweiligen Clients und
informiert die anderen Mitspieler über den neuen Status (was zum Beispiel zu einem Wechsel des
Lichtsignals in den Applets führt). Sobald alle menschlichen Mitspieler die Bestellung ausgeführt
haben (entweder eigenhändig oder durch timeout), wird die Transition für die nächste Runde
ausgelöst.
•
handleGameOver(): Dem entsprechenden Spieler wird einen Eintrag in den Vektor
„playedGames“ in der PlayerId gemacht. Dadurch ist es möglich, später die Auswertung des
Spieles wieder anzuschauen.
•
handleGetSavedGame(): Liest das gewünschte Spiel von der Festplatte aus und sendet die
Parameter an den Client.
•
handleQuitGame(): Diese Methode wird aufgerufen wenn der Client ein Spiel beendet hat, oder
ausgefallen ist. Falls das Spiel noch läuft wird der Client durch einen Computerspieler ersetzt.
Falls der Client einem Spiel zugewiesen war, das Spiel aber noch nicht gestartet hat wird die
Rolle wieder für andere Spieler verfügbar gemacht. Ist das Spiel regulär beendet worden, wird
die Referenz auf das Objekt Game auf null gesetzt, aus der Spielliste entfernt und das Objekt der
Garbage Collection überlassen.
Seite 32/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
9.4
ListenThread.java
Pro Client-Server Verbindung wird ein ListenThread instanziert, welcher auf Nachrichten des
Clients wartet. Pro Verbindung existieren also immer ein Listen- und ein SendThread-Objekt im
Server. Der ListenThread wird durch den SendThread erzeugt, welcher als Parameter gerade sich
selbst sowie den zur Verbindung zugehörigen Socket übergibt. ...
Sobald eine Message ankommt, wird der Message als Id den smsg.port = sendThread.getPort();
Port zugewiesen, auf dem sie hereingekommen ist und direkt sendThread.addMsg(smsg);
...
dem SendThread mittels der Methode „addmsg()“zur weiteren
Verarbeitung übergeben.
9.5
PlayerId.java
Die Klasse PlayerId verwaltet die verschiedenen Spieler sowie deren Daten. Die Spieler werden
anhand ihrer E-Mailadresse identifiziert. Die E-Mailadresse hat den Vorteil, dass sie weltweit
eindeutig ist. Der Einfacheit halber wird aber intern diese Id auf eine Nummer abgebildet. Dies
ist vor allem nützlich für die Indexierung in den Hashtables usersByUser, usersById und
usersByEmail. Pro Spieler werden die E-Mailadresse, das Passwort und der Name sowie die
gespielten Spiele abgespeichert, wobei momentan das Passwort standardmässig auf „password“
eingestellt ist und von den Benutzern nicht eingegeben wird. Ausserdem verwaltet es Methoden,
um beendete Spiele ebenfalls als Objekte auf die Festplatte auszuschreiben. Das Zieldirectory ist
der Ordner „SavedGames“, welcher im gleichen Verzeichnis liegt wie das beergame.server
Package.
Das Objekt PlayerId („implements Serializable“). wird selbst periodisch auf die Festplatte in das
Objekt „users.obj“ ausgeschrieben Nach einem Server-Shutdown wird es jeweils wieder frisch
eingelesen. Auf diese Weise gehen die Daten auch bei einem Serverausfall nicht verloren.
Auch hier werde ich ganz kurz auf die wichtigsten Methoden eingehen:
• addPlayer(): Erzeugt – falls die Id noch nicht existiert – einen neuen Spieler mit den übergebenen
Parametern.
• update() und updatePlayer(): Aktualisieren die Daten des Spielers.
• writeUserFile(): Schreibt die Instanzen des Objekts PlayerId in das Objekt „users.obj“ auf die
Festplatte über einen ObjectOutputStream aus.
• readUserFile(): Versucht, die Spielerliste (users.obj) von der Festplatte einzulesen. Diese
Methode wird jeweils beim Start des „BeergameServer“ aufgerufen
• saveGame(): Schreibt ein beendetes Spiel über einen ObjectOutputStream auf die Festplatte aus.
Diese Methode wird durch die jeweilige Instanz des Objekt „Game“ in der Methode gameOver()
aufgerufen.
• getSavedGame(): Liest ein gespieltes Spiel von der Festplatte aus und sendet die Parameter an
den aufrufenden Client zurück. Ein Client sieht nur die Spiele, an denen er selbst beteiligt war.
Wollen andere Spiele eingesehen werden, muss man sich unter der jeweiligen anderen EmailSeite 33/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
Adresse einloggen. Dies ist vor allem der Fall, wenn während einer Vorlesung oder
Übungsstunde die Spiele nochmals besprochen werden sollen.
9.6
Game.java
Die Klasse Game ist eigentlich die Kernklasse des ganzen Spiels. Sie enthält alle Daten, Zustände
und Modi der jeweiligen Spiele. Jedes Spiel kennt seine Mitspieler durch eine Referenz auf die
jeweiligen SendThreads. Diese Referenzen werden in einem Array verwaltet, wobei die
Arraynummer gleichzeitig die ID für die Spielrolle ist. Der Retailer hat die ID 1, der Wholesaler
die 2, der Distributor die 3 und die Factory die 4. Vom Computer selbst werden noch die 0
(Customer) und die 5 (Supplier) verwaltet.
Weiter kennt jedes Objekt „game“ (=Instanz de Klasse Game) den Spielinhaber. Auch dieser
wird über eine Referenz auf seinen SendThread in der Variablen „gameOwnerSendThread
verwaltet“. Der Spielinhaber ist normalerweise der Erzeuger des Spiels. Fällt dieser jedoch aus,
wird automatisch derjenige menschliche Mitspieler Spielinhaber, welcher die kleinste SpielerId
hat. Gleichzeitig mit dem Übergang wird die Variable „needsPermissionToJoin“ auf „false“
gesetzt. Verbleiben keine menschlichen Mitspieler mehr, wird das (Objekt) Spiel gelöscht.
Für die Spieldaten werden drei zweidimensionale Arrays verwaltet und zwar für die Bestellungen
(order[ ][ ]), den Lagerbestand(stock[ ][ ]) und die ausgelieferten Güter (goodsInTransport[ ][ ]),
wobei der erste Index jeweils die Runde und der zweite die Id des Comakers bzw. Mitspielers
angibt. Die Arrays haben also die Dimension [#Spielrunden][Länge der Supply Chain], wobei die
Länge der Supply Chain 6 ist inklusive des Customers und des Suppliers.
Weitere Arrays, welche verwaltet werden:
comakerName[ ]
Die Namen der jeweiligen Mitspieler
comakerStatus[ ]
Der Status der jeweiligen Spieler (Mensch, Computer, Tentative,
Idle...)
comakerStrategy [ ]
Die Strategie des jeweiligen Computerspielers
comakerReady [ ]
Der Zustand des jeweiligen Spielers (ready, not_ready, timout..)
Die wichtigsten Integers:
currentRound
die aktuelle Runde im Spiel
timePerRound
die Zeit, welche pro Bestellung zur Verfügung steht und bei Ablauf
zu einem Timeout führt
gameLength
die Anzahl Runden des aktuellen Spiels
gameMode
der Modus des Spiels (Klassisch, Klassisch Plus, Computer
Simulation)
openGame
der Status des Spiels, bestimmt, ob Spieler nur eingeladen
(INVITE-Funktion) werden können oder ob diese auch selbständig
dem entprechenden Spiel beitreten (JOIN-Funktion) können.
Seite 34/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
chatMode
bestimmt, ob Chat aktiviert ist oder nicht
Die wichtigsten Booleans:
isRunning
zeigt an, ob das Spiel im Gange ist
humansPlaying
zeigt an, ob menschliche Mitspieler am Spiel beteiligt sind oder ob
eine Computer Simulation läuft
Jedes Spiel erhält eine Spiel-ID, welche relevant ist, um gespeicherte Spiele wieder laden zu
können. Diese setzt sich zusammen aus dem Datum (Jahr, Monat, Tag, Stunde, Minute) sowie
dem Spielnamen und der Endung „.obj“. Diese ID ist eindeutig, da auch der Spielname selbst zur
Laufzeit eindeutig ist. Ein Beispiel einer Spiel-ID könnte so aussehen:
2002.1.18 14.17 Cardinal.obj
Diese Id wird gerade auch als Namen des entsprechenden Spiel-Objekts verwendet welches auf
der Festplatte abgespeichert wird und auch so in der Liste der gespielten Spiele angezeigt.
Wird ein neues Spiel erzeugt, werden alle übergebenen Parameter des Clients gesetzt und der
„Initial State“ erzeugt. Das Spiel beginnt bei Runde 1. Runde 0 wird vom Computer vorgegeben
und entspricht für alle Comakers einer Bestellung von 4. In Runde 1 sind jeweils 12 Stück an
Lager, 4 im Wareneingang und 4 im Transport für jeden Comaker.
Falls der Spielmodus auf Computer Simulation eingestellt ist, wird das gesamte Spiel mit den
vorgegebenen Strategien durchgerechnet und am Schluss die Resultate dem Client gesamthaft zur
graphischen Auswertung und Darstellung übergeben. Ansonsten wird geprüft, ob der
Spielerzeuger alleiniger Spieler ist und falls ja wird das Spiel gestartet. Haben zusätzliche Rollen
den Status „Human“, wird gewartet, bis alle Spieler zugewiesen sind.
Im Folgenden werden wiederum kurz die wichtigsten Methoden beschrieben:
• smsgGameInfo( ): Hat als Rückgabewert den Typ ServerMessage und liefert relevante
Informationen zu einem bestehenden Spiel. Diese Methode wird gebraucht, wenn ein Client ein
Join ausführt oder eine Invitation erhält.
• updateGameRegistrationDialog( ): Diese Methode schickt eine Message zu den Clients, um die
verfügbaren Rollen in der Eingabemaske „PnlJoinGame“ zu aktualisieren. Dies ist notwendig, da
die Zuweisung von Rollen an Spieler parallel erfolgen kann. Sobald ein Spieler eine Rolle
zugeteilt bekommen hat, werden in den Clients die Buttons für diese Rolle auf inaktiv geschaltet.
Sobald alle Rollen vergeben sind, schliessen sich Eingabemasken in den Clients mit einer
Meldung, das Spiel sei ausgebucht, automatisch wieder.
• checkGameReady(): Prüft, ob alle Rollen entweder an menschliche Mitspieler oder an den
Computer vergeben sind und schickt – falls diese Bedingung erfüllt ist - die Meldung
GAME_IS_READY an die Spieler dieses Spiels.
Seite 35/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
• checkGameStart(): Diese Methode wartet auf die Bestätigung aller Clients, dass sie bereit seien
und startet danach das Spiel durch die Methode startGame().
• smsgSetupGame(): Liefert als Rückgabewert eine ServerMessage, welche an den entsprechenden
Client versandt wird. Diese Message enthält alle relevanten Parameter, um den Client für das
entsprechende Spiel aufzusetzen.
• makeTransition(): Diese Methode wird jeweils am Ende einer Spielrunde aufgerufen, wenn alle
Spieler ihre Bestellung aufgegeben haben (entweder regulär oder durch timeout). Sie enthält den
Ablauf für den Übergang von einer zur nächsten Runde. Dazu werden zum einen für jeden
Spieler die Bestände berechnet und nachgeführt (Lager, Wareneingang, Transport).
Anschliessend werden die Spieler wieder auf „isReady=false“ gesetzt und „currentRound“ um
eins erhöht. Danach werden direkt die Bestellungen der Computerspieler (falls vorhanden) für die
nächste Runde mit den entsprechenden Strategien berechnet (Methode makeComputerOrders())
und die Computerspieler wieder auf „isReady=true“ gesetzt. Zum Schluss wird geprüft, ob das
Spiel im Modus Computersimulation läuft. Falls ja wird direkt (rekursiv) wiederum die Methode
makeTransition() aufgerufen. Ansonsten werden an die Mitspieler die ServerMessage
NEXT_ROUND versandt und auf deren Bestellungen gewartet. Ist die letzte Spielrunde erreicht
(currentRound==gameLength),
wird
die
Methode
handleGameOver()
oder
handleComputerGameOver() aufgerufen.
• sendToAllComakers(): Diese Methode existiert auch in der Klasse SendThread selbst. Im
Gegensatz dazu wird hier aber die übergebene Message an die SendThreads aller menschlichen
Mitspieler verteilt. Dieselbe Methode in den SendThreads schickt die Message nur an die anderen
SendThreads der menschlichen Mitspieler (und nicht an sich selbst).
• handleGameOver(): Diese Methode beendet das Spiel und setzt alle SendThreads auf Null. Dies
ist notwendig, um das Spiel danach auf die Festplatte mittels der Methode
PlayerId.saveGame(this) auszuschreiben. (Es soll ja nur das Objekt „game“ und nicht die
SendThreads auch gespeichert werden.)
• handleComputerGameOver(): Diese Methode wird am Ende eines Spiels aufgerufen, welches im
Simulationsmodus durchgerechnet wurde. In diesem Falle besitzt der Client, welcher die
Simulation spezifiziert hat noch keine Daten. Diese werden nun zusammengestellt und dem
entsprechenden Client mittels eines Vectors in einer ServerMessage zur Verfügung gestellt.
Spiele, welche im Simulationsmodus durchgerechnet worden sind, werden nicht abgespeichert,
da sie einfach reproduzierbar sind.
Seite 36/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
9.7
Strategy.java
Die Klasse Strategy enthält alle Strategien, welche den Computerspielern zur Verfügung stehen.
Zurzeit sind drei Strategien implementiert:
Strategie keepTotalGoodsZero():
Diese Strategie berechnet, wie viel bestellt werden muss, um gerade auf den Lagerbestand null zu
kommen. Grundsätzlich ist diese Strategie ungeeignet, da sie keine Rücksicht auf vergangene
Bestellungen oder Sicherheitsbestände nimmt. Sie war geplant für den Einsatz bei einem
Spielertimeout. Sie wird im Moment aber nicht mehr eingesetzt.
Strategie keepLevelOfStock():
Dies ist für den vorgegebenen klassischen Spielablauf (resp. Bestellmuster) systembedingt
eigentlich beinahe die idealste Strategie. Sie gibt die erhaltenen Bestellungen jeweils 1:1 weiter.
Es wird somit angestrebt, die gesamten Waren in Transport auf einem konstanten Niveau zu
halten. Dies verhindert riesige Überbestellungen und gleichzeitig die damit verbundenen
Lieferschwierigkeiten der Supplier.
Strategie movAvrgStdDev():
Diese Strategie verhält sich ähnlich wie die menschlichen Mitspieler. Sie berechnet aus dem
gleitendendem Mittelwert (moving average) und der Standardabweichung die gewünschte Menge
an Artikel in der eigenen Fabrik. Diese wird dann verglichen mit der tatsächlichen Menge und
sollte die Differenz negativ sein, wird eine Bestellung ausgelöst. Die Definition „alle Artikel“
sieht folgendermassen aus (Anmerkung: negativer Lagerbestand bedeutet Lieferrückstand):
ActualTotalGoods= Lager (negativ oder positiv) + Bestand im Wareneingang + Bestand im
Transport + bestellte, aber noch nicht gelieferte Artikel + letzte Bestellung.
Der gewünschte Bestand an totalen Artikeln wird mit folgender Formel berechnet:
PreferredTotalGoods = ceil (delay * movingAverage + securityFactor * standardDeviation)
, wobei der Delay der Verzögerung zwischen Bestellung und Lagereingang entspricht.
Die Bestellung sieht nun folgendermassen aus:
Order = (PreferredTotalGoods>ActualTotalGoods) ? PreferredTotalGoods-ActualTotalGoods : 0;
Wird also lange Zeit auf einem konstanten Nivea bestellt, wird die Standardabweichung kleiner
und der Sicherheitsbestand wird gesenkt. Sind die Schwankungen in den Bestellungen gross,
wird auch der Sicherheitsbestand erhöht. Dies führt über die gesamte Supply-Chain eben genau
zum Bullwhip-Effekt, welcher sich hier sehr schön zeigt, da jeder Comaker eine grössere
Bestellung weitergibt als vom Vorgänger erhalten.
Diese Bestellstrategie wird momentan auch eingesetzt bei einem Timout oder Totalausfall eines
menschlichen Mitspielers.
Seite 37/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
Wiederum werden kurz die wichtigsten Methoden dieser Klasse beschrieben:
• KeepLevelOfStock(): berechnet den Rückgabewert für die Strategie „Keep Level Of Stock“
• MovAvrgStdDev(): berechnet den Rückgabewert für die Strategie des gleitenden Mittelwertes
und der Standardabweichung.
• KeepTotalGoodsZero(): berechnet den Rückgabewert für die gleichnamige Strategie.
• GetTotalGoods(): berechnet für einen Comaker die totale Anzahl Waren, welche sich bei ihm im
Umlauf befinden oder bestellt sind.
• GetStandardDeviation(): berechnet aus einem gegebenen Array die Standardabweichung aus den
Daten der letzten x Perioden.
• GetMovingAverage(): berechnet aus einem gegebenen Array den gleitenden Mittelwert für die
letzten x Perioden.
Seite 38/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
10
Klassen des Package beergame.client
10.1
BeergameClient.java
Diese Klasse ist das eigentliche Applet (extends Applet). Sie hat zwei Hauptaufgaben: Zum einen
ist sie für den Verbindungsaufbau zum Server verantwortlich, zum anderen stellt sie den grössten
Teil der Komponenten (inklusive ActionListeners) für das Graphical User Interface (GUI) zur
Verfügung. Ebenso verwaltet sie den Status und spezifische Informationen zum Spieler, welche
nicht den Spielablauf betreffen.
Sobald das Applet geladen ist wird direkt nach dem Konstruktor die Methode „startSocket“
aufgerufen. Diese liest als erstes die Sprache des Benutzerinterfaces aus den Parametern der
aufrufenden HTML Seite aus und versucht danach die Socketverbindung zum HelloThread des
Servers zu starten. Ist der Verbindungsaufbau erfolgreich, wird vom Server die Portnummer für
die zukünftige Kommunikation zurückgegeben. Ansonsten wird auf dem Client eine
Fehlermeldung ausgegeben. Parallel dazu beginnt die Initialisierung des Applets bzw. aller
Komponenten, welche für den Benutzer sichtbar sind - unter Berücksichtigung der gewählten
Sprache. Als letztes wird der LogonDialog aufgerufen, welcher es dem Benutzer ermöglicht, sich
für das Spiel anzumelden. Damit ist der Client aufgesetzt und für die weiteren Schritte bereit (vgl
auch Screenshots ab Seite 60).
Die wichtigsten Variablen:
Name
Typ
Beschreibung
host
String
IP-Adresse des Servers
language
String
Die Sprache des Benutzerinterfaces. Wird beim
Applet Start als Parameter aus dem HTML-Text
ausgelesen.
pass
String
Das Passwort des Benutzers.
login
String
Der Benutzername des Spielers. Dieser muss unter
allen angemeldeten Spieler eindeutig sein.
loggedOn
Boolean
Gibt an, ob der Benutzer beim Server angemeldet
ist.
res
RessourceBundle
Enthält alle Strings der ausgewählten Sprache
(language), auf welche mit einem Schlüssel
zugegriffen wird.
ourServer
ClientListener
Ein Listener-Thread, welche auf Nachrichten des
Servers wartet.
gameLogic
GameLogic
Klasse, welche alle Methoden und Zustände des
laufenden Spiels enthält, an dem sich der Spieler
beteiligt.
Seite 39/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
clock
Thread
Stellt die Timer Funktion zur Verfügung, welche
die verbleibende Zeit pro Runde angibt.
base
URL
Die URL des Hosts. Wird benötigt als Referenz für
den Speicherort der Bilder.
mt
MediaTracker
Tracker, welche den Downloadstatus der Bilder
prüft.
clrComaker
Color
Die zugewiesenen Farben der einzelnen Comakers
Die wichtigsten Methoden:
• startClock():
erzeugt einen neuen Thread „clock“ und startet diesen (Methode run()). Diese Methode wird zu
Beginn jeder Spielrunde aufgerufen.
• stopClock()/resumeClock():
hält die Uhr an beziehungsweise lässt sie weiterlaufen. Diese zwei Methoden werden zurzeit
nicht mehr verwendet.
• destroyClock():
zerstört den Thread clock
• run():
lässt den Timer von der Startzeit (ein Spielparameter) an abwärts laufen. Ist die verbleibende Zeit
kleiner als 30s, wird die Farbe des Timers auf gelb, bei weniger als 10s auf rot umgestellt. Hat
der Timer 0 erreicht, wird die Methode gameLogic.handleTimeUp() in der Klasse gameLogic
aufgerufen.
• fillPlayerList():
aktualisiert die Liste der Spieler. Angezeigt werden jeweils die Spieler, welche online sind und
noch keinem Spiel zugewiesen sind. Da die Spieler ausgewählt werden können, wird jeweils
nicht die gesamte Liste ersetzt (was die Selektion auch löschen würde), sondern es werden nur
diejenigen Spieler hinzugefügt, welche neu sind und diejenigen Spieler entfernt , welche in der
neuen Liste nicht mehr erscheinen.
• fillGameList():
analog zur Methode fillPlayerList(). Es werden jeweils diejenigen Spiele angezeigt, welche noch
Plätze für Mitspieler frei haben.
• fillPlayedGameList():
Methode, welches die Liste der bereits gespielten Spiele aktualisiert. Direkt nach der Anmeldung
werden die dazu notwendigen Parameter dem Client vom Server für den betreffenden Spieler
übergeben, welcher dann diese Methode aufruft.
Seite 40/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
• showMessageDialog():
ruft ein neues Fenster auf mit den übergebenen Strings als Message und Titel. Dieser Dialog wird
hauptsächlich für Statusmeldungen und Warnungen verwendet.
• showGraphWindow():
ruft ein neues Fenster mit dem übergebenen StatisticsPanel auf. Diese Methode kann dazu
verwendet werden, die einzelnen Auswertungsgraphiken vergrössert darzustellen. Diese Methode
ist aber in der jetzigen Version nicht in Verwendung. Sie kann für eine spätere Erweiterung des
Programms eingesetzt werden
• bttnLogon_actionPerformed():
je nach Status wird hier der Spieler ein- oder ausgeloggt. Ist der Spieler noch nicht eingeloggt,
wird die Methode startSocket() aufgerufen, ansonsten wird die Socketverbindung geschlossen.
• bttnInvite_actionPerformed():
liest den markierten Spieler aus der „PlayerList“ aus und prüft die Auswahl auf Korrektheit (nicht
der eigene Name, nicht kein Namen) und ruft bei Erfolg die Methode gameLogic.invitePlayer()
auf, welche den Parameter für die weitere Verarbeitung schliesslich an den Server sendet.
Gleichzeitig wird dem Spieler eine Meldung über die erfolgte Einladung ausgegeben.
• bttnJoinGame_actionPerformed():
liest das markierte Spiel aus der „GameList“ aus und extrahiert den Spielnamen (Name des Spiels
ohne bereits zugewiesene Spieler). Anschliessend wird die ServerMessage JOIN mit diesem
Parameter an den Server bzw. den eigenen Listen-und SendThread im Server versandt, welcher
die weitere Verarbeitung und Prüfung übernimmt. Dem Spieler wird eine Meldung ausgegeben,
dass die Anfrage erfolgt ist.
• bttnNewGame_actionPerformed():
Sendet die ServerMessage „APPLY_NEW_GAME“ an den Server, welcher weitere Prüfungen
übernimmt und bei Erfolg eine Message „GRANT_NEW_GAME“ zurückschickt, welche im
Client dann zum Offnen des Panels „PnlNewGame“ führt.
• bttnViewGame_actionPerformed()
liest aus der Liste„ListPlayedGames“ das markierte Spiel aus, prüft die Auswahl und sendet
diesen Parameter bei Erfolg in der ServerMessage GET_SAVED_GAME an den Server. Dieser
versucht, das gewünschte Spiel von der Festplatte auszulesen und sendet alle notwendigen
Parameter für die Auswertung an den Client zurück.
Weiter sind verschiedene ActionListeners für die Auswertung der Graphiken implementiert,
welche es erlauben, einzelne Graphen ein- oder auszublenden, den Verlauf der Graphen über die
einzelnen Runden zu verfolgen oder die Auswertungsgraphiken von den Einzelgraphen auf die
überlagerten Graphen umzuschalten.
Seite 41/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
10.2
ClientListener.java
Diese Klasse (ein Thread) ist das Gegenstück zum SendThread im Server. Es hört über einen
ObjecteInputStream auf Nachrichten vom Server und leitet diese entsprechend der MessageID an
die verantwortlichen Methoden weiter. Eine
...
if (smsg.msgid.equals(ServerMessage.BYE)) {
Instanz dieser Klasse wird in der Methode
bgc.logonEnable(false); //disable the world
break;
„startSocket“
des
BeergameClient
}
else if (smsg.msgid.equals(ServerMessage.WELCOME))
aufgerufen. Die meisten Messages werden
handleWelcome(smsg);
else if (smsg.msgid.equals(ServerMessage.STATUS))
direkt in der Klasse „GameLogic“ behandelt,
handleStatus(smsg);
else if (smsg.msgid.equals(ServerMessage.PLAYERNAME_IN_USE))
ein paar wenige in dieser Klasse selbst.
handlePlayerNameInUse();
else if (smsg.msgid.equals(ServerMessage.PLAYED_GAMES))
bgc.fillPlayedGameList(smsg);
else if (smsg.msgid.equals(ServerMessage.INVITED))
bgc.gameLogic.handleInvited(smsg);
else if (smsg.msgid.equals(ServerMessage.GRANT_NEW_GAME))
bgc.gameLogic.handleGrantNewGame(smsg);
else if (smsg.msgid.equals(ServerMessage.REFUSE_NEW_GAME))
handleRefuseNewGame(smsg);
else if (smsg.msgid.equals(ServerMessage.GAME_CREATED))
handleGameCreated(smsg);
else if (smsg.msgid.equals(ServerMessage.GAME_NAME_EXISTS))
handleGameNameExists(smsg);
else if (smsg.msgid.equals(ServerMessage.REFUSED_INVITATION))
handleRefusedInvitation(smsg);
else if (smsg.msgid.equals(ServerMessage.JOIN_REQUESTED))
bgc.gameLogic.handleJoinRequest(smsg);
else if (smsg.msgid.equals(ServerMessage.JOIN_REFUSED))
bgc.showMessageDialog(bgc,bgc.res.getString("JOIN_REFUSED"),"Info");
else if (smsg.msgid.equals(ServerMessage.GAME_ACCEPTED))
bgc.showMessageDialog(bgc,MessageFormat.format(bgc.res.getString("ACCE
PTED_YOUR_INVITATION"),new Object[]{smsg.msg[0]}),"Info");
else if (smsg.msgid.equals(ServerMessage.R_GAME_UPDATE))
bgc.gameLogic.handleUpdateJoinGame(smsg);
else if (smsg.msgid.equals(ServerMessage.NOT_ASSIGNED))
bgc.showMessageDialog(bgc,bgc.res.getString("COULD_NOT_ASSIGN"),"");
else if (smsg.msgid.equals(ServerMessage.SETUP_GAME))
bgc.gameLogic.setupGame(smsg);
else if (smsg.msgid.equals(ServerMessage.SETUP_COMPUTER_GAME))
bgc.gameLogic.handleSetupComputerGame(smsg);
else if (smsg.msgid.equals(ServerMessage.ADD_PLAYER))
bgc.gameLogic.handleAddPlayer(smsg);
else if (smsg.msgid.equals(ServerMessage.REMOVE_PLAYER))
bgc.gameLogic.handleRemovePlayer(smsg);
else if (smsg.msgid.equals(ServerMessage.GAME_IS_READY))
bgc.gameLogic.handleGameIsReady(smsg);
else if (smsg.msgid.equals(ServerMessage.CLIENT_REPORTS_READY))
bgc.gameLogic.handleOtherClientReady(smsg);
else if (smsg.msgid.equals(ServerMessage.CLIENT_REPORTS_TIME_OUT))
bgc.gameLogic.handleOtherClientTimeOut(smsg);
else if (smsg.msgid.equals(ServerMessage.HUMAN_PART_TO_COMPUTER))
bgc.gameLogic.handleHumanPartToComputer(smsg);
else if (smsg.msgid.equals(ServerMessage.START))
bgc.gameLogic.handleStart();
else if (smsg.msgid.equals(ServerMessage.NEXT_ROUND))
bgc.gameLogic.handleNextRound(smsg);
else if (smsg.msgid.equals(ServerMessage.GAME_OVER))
bgc.gameLogic.handleGameOver(smsg);
else if (smsg.msgid.equals(ServerMessage.LIST))
bgc.fillPlayerList(smsg.msg[0]);
else if (smsg.msgid.equals(ServerMessage.GAME_LIST))
bgc.fillGameList(smsg.msg[0]);
else if (smsg.msgid.equals(ServerMessage.CHAT))
bgc.infoText.append(smsg.msg[0] + "\n");
else {
System.out.println("ClientListener ignored " + smsg.msgid);
}
Die wichtigsten davon werden hier wiederum
kurz aufgeführt:
• handleWelcome(): Schliesst den Logon
Dialog, falls dieser noch offen ist und setzt
die Willkommensüberschrift im GUI
• handleRefuseNewGame():
Ruft
einen
Message Dialog auf und informiert den
Spieler, dass die Erzeugung eines neuen
Spiels abgelehnt wurde, da er bereits bei
einem Spiel angemeldet sei.
• handleGameNameExists(): Informiert den
Spieler über einen Message Dialog, dass der
gewählte Spielname bereits in Gebrauch ist.
• handleGameCreated():
Schliesst
den
NewGame Panel und setzt im Client die Id
des eigenen Spielers.
• handlePlayerNameInUse(): Informiert
den
Spieler über einen Message Dialog, dass der
gewählte Spielername bereits in Gebrauch ist.
• handleRefusedInvitation(): Informiert den
Spieler über einen Message Dialog, dass ein
eingeladener Spieler die Einladung abgelehnt
hat.
Seite 42/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
10.3
ClientSender.java
Der Client Sender ist neben dem ClientListener verantwortlich für die Kommunikation zwischen
Client und Server. Der Thread ist Verantwortlich für das Versenden der übergebenen Nachrichten
an den (spielereigenen) ListenThread im Server. Dies geschieht über einen Socket mittels des
ObjectOutputStreams. Erzeugt wird die (einzige) Instanz der Klasse im BeergameClient in der
Methode „startSocket()“.
Muss eine Nachricht an den Server versandt werden, wird diese dem Vektor „msgque“ – eine
Warteschlange - des ClientSenders mittels der Methode „sendMsg()“ angehängt. Die Nachrichten
werden dann der Reihe nach abgearbeitet und an den Server weitergeleitet.
Zu einer Nachricht (Message) gehört immer die MessageId. Dazu kommen die Daten in Form
von Strings, String Arrays (ein oder zweidimensional), Integer oder Integer Arrays (ein oder
zweidimensional). Die Datentypen können auch kombiniert (z.B. je als String- und Integer
Doppelarrays) übergeben werden.
10.4
GameLogic.java
Diese Klasse ist die eigentlich Kernklasse im Client für den Spielablauf. Sie enthält alle Zustände
und Informationen des laufenden Spiels sowie die Methoden zur Spielsteuerung.
Grundsätzlich erhält die Klasse GameLogic alle relevanten Daten vom Server und verändert
diese nicht. Die Berechnungen und Zustandstransitionen finden grundsätzlich im Server statt,
genauer in der Klasse beergame.Server.Game und im eigenen SendThread. Dies verhindert den
Verlust von Daten und/oder Zuständen, falls ein Client ausfällt. Der Client ist jedoch
verantwortlich für die Darstellung und Auswertung der erhaltenen Daten. Die einzigen
Berechnungen, die der Client durchführt, betreffen die Kosten und die Waren im Wareneingang beides Datensätze welche redundante Information darstellen: Die Waren im Wareneingang
entsprechen genau den Waren in Transport der letzten Runde und die Kosten lassen sich direkt
aus den Lagerbeständen ableiten. Auf diese Weise wird versucht, den Kommunikationsaufwand
klein zu halten.
Die wichtigsten Variablen sind:
Name
Typ
Beschreibung
comakerId
Integer
Die Id der Rolle des Spielers: 1-> Retailer, 2->
Wholesaler 3-> Distributor 4-> Factory. 0 ist
reserviert für den Customer und 5 für den Supplier.
gameId
String
Die Id des Spiels. Sie setzt sich zusammen aus
aktuellem Datum, Zeit, sowie Namen des Spiels
und
der
Endung
"obj".
(vgl.
beergame.Server.Game)
gameName
String
der Name des Spiels
comakerName
String[ ]
der Array von Namen aller Comaker
actualRound
Integer
die aktuelle Runde des Spiels
Seite 43/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
gameMode
Integer
der Spielmodus (klassisch, klassisch plus, ...)
chatMode
Integer
legt fest, ob chatten möglich ist oder nicht
gameOver
boolean
zeigt an, ob das Spiel läuft oder beendet ist
gameLength
Integer
die Anzahl Spielrunden
timePerRound
long
die Zeit pro Bestellung (in Millisekunden)
actualCosts
double
die aktuellen Kosten des Spielers
iAmReady
boolean
der Status des Spielers
playerReady
Integer [ ]
der Status aller Comaker
order
Integer [ ][ ]
Die Bestellungen aller (bisherigen) Runden und
aller Spieler
stock
Integer [ ][ ]
Die Lagerbestände aller (bisherigen) Runden und
Spieler
goodsReceiving
Integer [ ][ ]
Die Waren im Wareneingang aller (bisherigen)
Runden und Spieler. Diese Daten werden vom
Server nicht übermittelt – sie entsprechen ganz
einfach den Zahlen „goodsInTransport minus eine
Runde. Übersichtlichkeitshalber wird diesen Daten
im Client aber ein eigener Array zur Verfügung
gestellt.
goodsInTransport
Integer [ ][ ]
Die Waren in Transport aller bisherigen Runden
und Spieler
Beschreibung der wichtigsten Methoden:
• handleGrantNewGame(): Erzeugt eine Instanz der Klasse PnlNewGame und stellt dem Spieler
damit eine Eingabemaske zur Verfügung, um ein neues Spiel zu definieren.
• invitePlayer(): Erzeugt eine ServerMessage mit dem Parameter des Namens des Spielers, der
eingeladen werden soll.
• handleInvited(): Erzeugt eine Instanz der Klasse DlgJoinGame, welche dem Spieler ein Interface
zur Verfügung stellt dem entsprechenden Spiel beizutreten oder die Einladung abzulehnen.
• handleJoinRequest(): Erzeugt eine Instanz der Klasse DlgJoinRequest. Dem Spieler eröffnet sich
ein Dialogfenster, in dem er die Anfrage eines anderen Spielers, seinem Spiel beizutreten
ablehnen oder akzeptieren kann.
Seite 44/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
• handleUpdateGameInfo(): aktualisiert die zur Verfügung stehenden Rollen im Panel „Join
Game“. Sobald eine Rolle an einen anderen Spieler vergeben ist, wird der Button der
entsprechenden Rolle inaktiviert und der Name des neuen Spielers unter der Rolle aufgeführt.
Dieses Vorgehen ist notwendig, da die Spieler parallel den verschiedenen Spielen beitreten
können. Ein Spieler erhält diese Nachricht nur, wenn er sich gerade im Status der Rollenauswahl
(Join/Invitation) des entsprechenden Spiels befindet.
• handleSetupComputerGame(): Diese Methode wird aufgerufen, falls für ein Spiel eine
Computersimulation (also nur Computerspieler) durchgeführt worden ist. Alle relevanten
Parameter werden dem Client vom Server in Form eines Vektors zur Verfügung gestellt. Zuerst
wird die Methode SetupGame() mit den entsprechenden Parametern aufgerufen. Danach werden
die Datensätze der Bestellungen, Lagerbestände und Waren in Transport gesetzt und zum Schluss
die Methode evaluateGame() aufgerufen, welche die verschiedenen Auswertungsgraphiken
erzeugt.
• setupGame(): In dieser Methode werden alle relevanten Parameter für das Spiel gesetzt. Es sind
dies unter anderem die comakerId, die aktuelle Runde (1), die Spiellänge, die Zeit pro Runde, der
Spielmodus, der Chatmodus, die Spielernamen, der Spielnamen selbst sowie die Booleans
gameOver und gameIsSet. Des Weiteren werden die Arrays order, stock, goodsInTransport und
playerReady instanziert. und die Anfangswerte in denselben gesetzt. In einem weiteren Schritt
wird das Panel „TopOverview“ zusammengestellt, welches die Übersicht über das gesamte Spiel
enthält. Alle Graphik Panels werden instanziert und danach die Methode „setLabels()“ und
„setTheSignals()“ aufgerufen. Schliesslich werden noch die Strings der jeweiligen Sprache
gesetzt und der Initialzustand der „LastOrderList“ erstellt.
• handleQuitGame(): Diese Methode setzt alle relevanten Spielparameter zurück.
• handleAddPlayer(): Fügt einem Spiel einen weiteren Spieler hinzu und setzt seine Parameter.
• handleRemovePlayer(): Wird aufgerufen, wenn ein Spieler das Spiel verlässt, bevor es gestartet
hat. Die entsprechende Rolle wird wieder auf „verfügbar“ geschaltet. Seine im Spiel gesetzten
Parameter werden rückgängig gemacht.
• handleGameIsReady(): Diese Methode wird aufgerufen, sobald alle Rollen in einem Spiel
vergeben sind. Sie erzeugt einen Dialog, in dem der Spieler abgefragt wird, ob er bereit sei.
• handleIamReady(): Wird aufgerufen, sobald der Spieler im Dialogfeld „DlgGameStart“ seine
Bereitschaft bekundet hat. Sie sendet eine Statusmeldung an den Server. Sobald alle Mitspieler
bereit sind, wird das Spiel gestartet.
• handleOtherClientReady(): Diese Methode wird aufgerufen, wenn sich ein anderer Mitspieler
diese Spiels als „bereit“ gemeldet hat. Die Methode setzt das Signal des entsprechenden Spielers
danach auf grün.
• handleStart(): Setzt den entsprechenden Spieler auf iAmReady=false, aktualisiert das GUI, setzt
den ChatMode (enable/disable), schreibt eine Statusmeldung und setzt die Signale auf Orange für
Seite 45/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
alle Nicht-Computer Spieler. (Die Computerspieler haben ihre Bestellung bereits ausgeführt.)
Schliesslich wird der Timer gestartet und auf die erste Bestellung gewartet.
• handleTimeUp(): Falls der Spieler die Bestellung der aktuellen Runde ausgeführt hat, passiert
nichts, ansonsten wird dem Server die ServerMessage TIME_UP übermittelt und der
„timeUpCounter“ um eins erhöht.
• handleOtherClientTimeUp(): Setzt eine Statusmeldung, sobald ein anderer Spieler in das
Timeout gelaufen ist und setzt dessen Signal auf Rot.
• handleHumanPartToComputer(): Diese Methode wird aufgerufen durch eine ServerMessage,
falls ein anderer Spieler ausgefallen (bzw. die SocketConnection abgebrochen) ist. Die Rolle des
entsprechenden Spielers wird mit dem Vermerk „(Computer)“ ergänzt und eine Statusmeldung
wird ausgegeben. Das Signal des entsprechenden Spielers wird auf Rot gestellt.
• handlePerformOrder(): Diese Methode wird aufgerufen durch das drücken des „send“ Buttons.
Der Eingabewert des Feldes „Order“ wird geprüft und bei Bedarf eine Fehlermeldung
ausgegeben (kein Integer, Bestellung zu gross oder zu klein). Ansonsten wird der Boolean
„iAmReady“ auf true gesetzt, die „LastOrderList“ nachgeführt und der Order-Graph aktualisiert
sowie der Button „send“ inaktiviert. Zum Schluss wird dem Server die Bestellung des Spielers
mittels der ServerMessage ORDER_PERFORMED übermittelt.
• handleNextRound(): Sobald alle Clients bestellt haben (regulär oder mit timeout), ruft der Server
über eine ServerMessage diese Methode auf, welche den Übergang zur nächsten Runde einleitet.
Zuerst wird der Boolean „iAmReady“ auf false gesetzt, die neue Rundennummer übernommen,
die Bestellungen sowie die Lagerbestände, Waren in Wareneingang und die Waren in Transport
aller Comaker nachgeführt. Die jeweiligen Kosten werden berechnet und ebenfalls dargestellt.
Methoden zur Berechnung der aktuellen Kosten:
Die Stati der Mitspieler werden mittels des
private synchronized double[][] getCostsPerRoundByComaker(int round) {
Arrays
playerReady[]
gesetzt
und
double[][] costs= new double[scLength+2][round+1];
for (int cm=1;cm<=scLength;cm++) {
anschliessend das GUI mittels der Methode
costs[cm]= calculateCosts(cm,round);
}
„setLabels()“
und
„setTheSignals“
return costs;
}
aktualisiert. Ebenfalls werden die Graphen
private synchronized double[] calculateCosts(int comaker, int actualRound){
„Bestellungen“ und „Kosten pro Runde“ neu
double costs[] = new double[actualRound+1];
for(int i=0;i<=actualRound;i++){
gezeichnet. Zum Schluss wird eine
costs[i] = (stock[i][comaker]>=0)?stock[i][comaker]*0.5:-stock[i][comaker]*1.0;
}
return costs;
Statusmeldung geschrieben, der Button
}
„Send“ aktiviert und der Timer für die
nächste Runde gestartet.
• setLabels(): Ist verantwortlich für die Aktualisierung des GUI. Sie wird aufgerufen zu Beginn des
Spieles und vor jeder neuen Runde. Sie wird auch im „Evaluationsmodus“ verwendet, um die
verschiedenen Runden nochmals anschauen zu können. Dabei werden je nach Spielmodus und
Spieler-ID die relevanten Labels aktualisiert, sichtbar oder unsichtbar gemacht. Im Falle eines
Timeouts des Spielers wird jetzt die vom Computer ausgeführte Bestellung im Graph rot
nachgezeichnet und eine Statusmeldung ausgegeben.
Seite 46/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
• setTheSignals(): Setzt, beziehungsweise aktualisiert die Signale der einzelnen Comaker im
Übersichtspanel, je nach Status der einzelnen Mitspieler. Der Status wird vom Server mittels des
Arrays „playerReady[]“ übergeben.
• enableGloabalInformationMode() & disableGlobalInformationMode() Machen die Informationen
aller anderen Mitspieler im Übersichtspanel sichtbar, respektive unsichtbar. Namentlich sind
dies die Bestellungen, der Lagerbestand, der Wareneingang sowie die Waren in Transport und
die Kosten.
• handleGameOver(): Diese Methode wird – wie alle anderen Methoden, welche das Spiel steuern
– über eine ServerMessage vom Server aufgerufen. Zuerst wird der Boolean gameOver auf True
gesetzt und der Timer gestoppt. Anschliessend wird die Spiel-Id in die Liste der gespielten Spiele
aufgenommen. (Der Server hat das Spiel bereits auf die Festplatte gespeichert.) Schliesslich wird
der Button „send“ auf „show evaluation“ (in der jeweiligen Sprache) umbenannt und wieder
aktiviert.
• evaluateGame(): Kann nach dem Spielende durch den Spieler aufgerufen werden, indem er auf
den Button „show evaluation“ klickt. Dadurch wird in einem ersten Schritt das GUI auf die
Auswertung vorbereitet: es werden verschiedene Card-Layouts vom Modus „Game“ auf
„Evaluation“ umgeschaltet, danach werden die Arrays neu sortiert nach Comaker und Runde
(während des Spiels Sortierung nach Runde und Comaker) sowie ein neuer Array erstellt mit den
Kosten aller Mitspieler. Anschliessend werden aus allen Datensätzen (Bestellungen, Lager,
Kosten usw.) die maximalen und minimalen Werte bestimmt. In einem weiteren Schritt werden
dann die Graphen vorbereitet, die Achsen mit den Maximal und Minimalwerten initialisiert, die
Farben gesetzt (Beschriftung, Achsen, Kurven) und die entsprechenden Werte übergeben.
• handleShowSingleGraphs():Schaltet die Ansicht auf die Einzelgraphiken um.
• handleShowCombinedGraphs(): Schaltet die Ansicht auf die überlagerten Graphen um.
• handleDrawComakerGraph(): Schaltet je nach Zustand der Checkboxes einzelne Kurven ein oder
aus in den Diagrammen mit den überlagerten Kurven und setzt gleichzeitig die Signale der
Mitspieler auf Rot oder Grün – je nachdem, ob ihre Daten dargestellt werden.
• goBackAll(): Ein Button Event. Setzt in der Auswertung die Datensätze auf die Runde 1 zurück
und inaktiviert die Buttons „GoBackAll“ und „GoBackOne“. Die Graphen werden anschliessend
nachgeführt.
• goBackOne(): Ein Button Event. Setzt in der Auswertung die Runde um eins zurück und ruft die
Methode redrawGraphs() mit der aktuellen Runde auf.
• goForwardOne(): Ein Button Event. Setzt in der Auswertung die Runde um eins vor und ruft die
Methode „redrawGraphs()“ mit der entsprechenden Runde auf.
• goForwardAll(): Ein Button Event. Setzt in der Auswertung die letzte Runde
(currentRoundView=gameLength) und ruft ebenfalls die Methode „redrawGraphs()“ auf.
Seite 47/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
• redrawGraphs(): Setzt die Werte in den Diagrammen entsprechend der gewünschten Runde und
führt ein „repaint()“ durch. Diese Methode ruft ebenfalls die Methode „setLabels()“mit den
entsprechenden Parametern auf, damit in der Übersicht auch die Zahlenwerte der entsprechenden
Runde zur Verfügung stehen.
• enableChat() & disableChat(): Aktivieren bzw. deaktivieren die Möglichkeit, Chatnachrichten zu
versenden.
10.5
OrderPerformedTransition
Diese Klasse – ein Thread – übernimmt die Steuerung der Animation des GUI nachdem der
jeweilige Spieler seine Bestellung aufgegeben hat. Ein Thread ist notwendig, da die Animation
zeitlich gesteuert wird.
Das Bewegen des Lastwagens:
...
//drive the truck away
int truckMovingTime=3; //sec
double locY=220.0;
int y;
for (int x=400;x<=600;x++) {
locY-=40.0/200;
y = (int) java.lang.Math.floor(locY);
gL.bgc.pnlTransport.setLocation(x,y);
try {sleep(truckMovingTime*1000/200);} catch (Exception ex) {}
}
...
.
Als erstes erfolgt der Übergang der Waren im Wareneingang ins Lager. Dabei wird der
Wareneingang Fass für Fass ab- und das Lager aufgebaut. Danach erfolgt die Auslieferung an
den Kunden und die Entladung der Waren in Transport. Zuletzt wird der Lastwagen
zurückgefahren.
Die Klasse ist direkt der Klasse GameLogic.java angegliedert, da sie logisch zu dieser gehört.
10.6
NextRoundTransistion
Dieser Thread wird jeweils zu Beginn einer neuen Runde aufgerufen. Er übernimmt die
Animation für das GUI welche den Übergang von einer zur nächsten Runde betreffen. Unter
anderem wird der beladene Lastwagen des Zulieferers „hineingefahren“, sowie die Ankunft der
neuen Bestellung des Kunden animiert. Bevor dieser Thread ausgeführt wird, wird geprüft er ob
der OrderPerformedThread bereits abgeschlossen ist. Ist dieser noch am Laufen, wird auf dessen
Abschluss gewartet
Das warten auf den Abschluss des OrderPerformedThreads:
...
while (gL.opTisRunning) {
try {sleep(1000);} catch (Exception ex) {}
}
...
Seite 48/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
Seite 49/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
10.7
ServerMessage.java
ADD_PLAYER
APPLY_NEW_GAME
BYE
CHAT
CLIENT_READY
CLIENT_REPORTS_READY
CLIENT_REPORTS_TIME_OUT
CREATE_NEW_GAME
DEAD
GAME_ACCEPT
GAME_ACCEPTED
GAME_CREATED
GAME_IS_READY
GAME_LIST
GAME_NAME_EXISTS
GAME_OVER
GRANT_NEW_GAME
GET_SAVED_GAME
HUMAN_PART_TO_COMPUTER
INFO
INFORMATION
INVITE
INVITED
JOIN
JOIN_ACCEPT
JOIN_ACCEPTED
JOIN_CHOOSE_PART
JOIN_REFUSE
JOIN_REFUSED
JOIN_REQUEST
JOIN_REQUESTED
LIST
LOGON_LIST
NAME
NEW_GAME_CANCELLED
NOT_ASSIGNED
NOTE
NEXT_ROUND
ORDER_PERFORMED
PLAYED_GAMES
PLAYER_LIST
PLAYERNAME_IN_USE
QUIT_GAME
REFUSE_INVITATION
REFUSED_INVITATION
REFUSE_NEW_GAME
REMOVE_PLAYER
R_GAME_UPDATE
SETUP_COMPUTER_GAME
SETUP_GAME
START
STATUS
TIME_UP
UPDATE_INFO
UPDATE_USER
WELCOME
Die Klasse ServerMessage definiert die Form der Nachrichten,
welche zwischen dem Server und dem Client versandt werden.
Dazu werden als Referenz alle Message ID’s als public static final
Strings zur Verfügung gestellt.
Jede Message wird anhand ihrer Message-ID identifiziert. Jede
Message enthält Parameter in Form von Strings, Integers, StringArrays, Integer-Arrays oder eines Vektors. Für jede Message-ID
besteht demnach eine Methode, welche die übergebenen Message
Parameter verarbeitet. Links ist eine List der ID’s zu sehen, welche
für das BeerGame eingeführt wurden
Es werden im Moment sieben verschiedene Message Typen zur
Verfügung gestellt. Diese unterscheiden sich in den verschiedenen
Kombinationen der versandten Datentypen. Folgende Aufrufe
werden Unterstützt:
1.
(int port, String messageId, String message)
2.
(int port, String messageId, String[] message)
3.
(int port, String messageId, int messageInt)
4.
int port, String messageId, int[] message)
5.
(int port ,String messageId , String message, int messageInt)
6.
(int port, String messageId, String[] message,int[] messageInt)
7. (int port, String messageId, Vector msgVector)
Seite 50/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
10.8
Res.java
Die Klasse Res (Ressourcen) macht nichts anderes als über einen Schlüssel Strings einer
bestimmten Sprache zur Verfügung zu stellen. Alle verwendeten Strings für das GUI werden
von dieser Klasse bezogen mittels des Aufrufs:
res.getString(„KEY“)
Je nach Initialisierung des Applets wird der dem Schlüssel entsprechende String in einer anderen
Sprache zurückgegeben. Im Moment werden Englisch, Deutsch und Spanisch unterstützt.
Die Strings sind folgendermassen abgelegt:
static final Object[][] contents = {
...
....{ "WELCOME", "Welcome ","Willkommen " , "Bienvenidos " },
{ "CUSTOMER", "Customer","Kunde", "Cliente“ },
{ "RETAILER", "Retailer" ,"Einzelhändler", "Detailliste"},
{ "WHOLESALER", "Wholesaler" ,"Grosshändler", "Mayoriste"},
...
{ "WAIT_FOR_APPROVE", "please wait for approve to join the game \"{0}\" as {1}"
, "Anfrage um dem Spiel \"{0}\" als {1} beizutreten wurde gesendet"
, "por favor espera una respuesta"},
...
Der erste String ist der Schlüssel, die folgenden Strings stellen die betreffenden Rückgabewerte
dar. In die geschweiften Klammern werden zur Laufzeit aus dem Programm Werte eingesetzt. Im
Beispiel
oben
mit
dem
Schlüssel
„WAIT_FOR_APPROVE“ der Spielname (in
Methoden welche das gewünschte „res“Objekt zur Verfügung stellen
Hochkommata) und die ausgewählte Rolle. Auf
public Object[][] getContents() {
diese Weise ist es auf sehr einfach möglich, bei
setLanguageArrays(LANGUAGE);
return languageArray;
Bedarf das Programm um weitere Sprachen zu
}
erweitern.
private void setLanguageArrays(int language) {
for (int i=0;i<contents.length;i++) {
languageArray[i][0]=contents[i][0];//the key
if (contents[i][language]!="*")
languageArray[i][1]=contents[i][language];
else
languageArray[i][1]=contents[i][1];//take the english string
}
}
Im Moment werden in dieser Klasse um die 200
verschiedene Strings in verschiedenen Sprachen
verwaltet.
Seite 51/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
10.9
Ref.java
public static final int CUSTOMER = 0;
public static final int RETAILER = 1;
public static final int WHOLESALER = 2;
public static final int DISTRIBUTOR =3;
public static final int FACTORY =4;
public static final int SUPPLIER=5;
public static final int WAREHOUSE =10;
public static final int GOODS_RECEIVING =11;
public static final int TRANSPORT = 12;
//comaker status
public static final int MY_PART=20;
public static final int HUMAN=21;
public static final int COMPUTER=22;
public static final int NOT_YET_ASSIGNED=23;
public static final int TENTATIVE=24;
public static final int NOT_PLAYING=25;
public static final int INVITATION=30;
public static final int JOIN=31;
public static final int UPDATE=32;
public static final int STRATEGY_KEEP_LEVEL_OF_STOCK=50;
public static final int STRATEGY_MOV_AVRG=51;
public static final int PLAYER_NOT_READY=60;
public static final int PLAYER_NOT_READY_TIMOUT=61;
public static final int PlAYER_IS_READY=62;
public static final int PlAYER_IS_READY_TIME_OUT=63;
public static final int PlAYER_NOT_ASSIGNED=64;
public static final int PlAYER_SIGNAL_DARK=65;
public static final int PLAYER_SIGNAL_GREEN=66;
public static final int PLAYER_SIGNAL_RED=67;
public static final int GAME_NOT_READY=70;
public static final int GAME_IS_READY=71;
public static final int MODE_CLASSIC=80;
public static final int MODE_COMPUTER_SIMULATION=81;
public static final int MODE_GLOBAL_INFORMATION=82;
public static final int MODE_OPEN_GAME=83;
public static final int MODE_PRIVATE_GAME=84;
public static final int MODE_CHAT_ENABLED=85;
public static final int MODE_CHAT_DISABLED=86;
public static final int MODE_JOIN_NEEDS_PERMISSION=87;
public static final int MODE_JOIN_NEEDS_NO_PERMISSION=88;
Die Klasse Ref (References) enthält wie der Name
schon sagt Referenzen. Hier werden „sprechende
Namen“ für die Kommunikation zwischen Server und
Clients oder für den einfacheren Zugriff von Methoden
auf Integers abgebildet.
Dies ist vor allem nützlich, wenn relative Bezüge
wichtig werden, also wenn zum Beispiel der
Vorgänger oder der Nachfolger eines Comakers
angesprochen werden soll. Der „Wholesaler“ kann
dann zum Beispiel sein:
è Ref.RETAILER+1 (Nachfolger)
è Ref.DISTRIBUTOR –1 (Vorgänger)
oder noch allgemeiner:
è this.comakerId
Vorgänger)
+-1
(für
Nachfolger
bzw.
und so weiter.
Ausserdem wird durch die Abbildung der Strings auf
Integers die Kommunikation zwischen Server und
Client vereinfacht.
public static final String PART_FREE_SIGN=" * ";
10.10 PnlStatistics.java
Diese Klasse erweitert – wie der Name andeutet – die Klasse Panel. Sie stellt ein Panel zur
Verfügung, dessen Funktion es ist, Graphen aus übergebenen Werten darzustellen. Diese Klasse
wurde sehr allgemein programmiert. So ist es möglich, Instanzen dieser Klasse in
verschiedensten Ausprägungen zu erzeugen. Zum einen können die Achsen dynamisch an die
übergebenen Werte angepasst oder fix vorgegeben werden. Es können beliebig viele überlagerte
Kurven gezeichnet werden (oder eben auch nur eine). Ebenso können die Farbe der Achsen, der
Beschriftung und der Kurven selbst beliebig gewählt werden. Es ist sogar möglich, einzelne
Kurvensegmente anders einzufärben. Dies wird zum Beispiel verwendet, um bei einem Timeout
die Bestellungen welche vom Computer ausgeführt worden sind von denjenigen des
menschlichen Mitspielers zu unterscheiden.
Intern werden alle Werte als „Double“ behandelt, der Methode können aber auch Arrays von
Integern übergeben werden, welche dann intern „gecasted“ werden. Die Methode besitzt folgende
Vier Konstruktoren:
Seite 52/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
1. PnlStatistics(double[][] values,int graphAreaWidth, int graphAreaHeigth)
2. PnlStatistics(int[][] values,int graphAreaWidth, int graphAreaHeigth)
3. PnlStatistics(int[] values,int graphAreaWidth, int graphAreaHeigth)
4. PnlStatistics(int graphAreaWidth, int graphAreaHeigth
Die wichtigsten Variablen dieser Klasse werden im folgende kurz beschrieben:
graphAreaWidth
Integer
Die Breite, welche der Graph im Panel haben soll in Pixel
graphAreaHeight
Integer
Die Höhe, welche der Graph im Panel haben soll in Pixel.
clrDefaultGraph
Color
Die Farbe, welche standardmässig für das Zeichnen einer
Kurve verwendet wird
clrAxes
Color
Die Farbe,, in der die Achsen gezeichnet werden.
Standardmässig ist „darkGray“ eingestellt
clrFont
Color
Die Farbe in der die Achsenbeschriftung gezeichnet wird.
Standardmässig ist „darkGray“ eingestellt.
Die folgenden Variablen sind als „private“ deklariert, können aber über entsprechende „set“
Methoden eingestellt werden:
offsetLeft,
offsetRight,
offsetTop,
offsetBottom
Intgers
Die Offsets, welche die Ränder der Fläche des Graphen
angeben. Diese Variablen sind „private“, können aber über
eine „setOffset“ Methode gesetzt werden.
initialXAxesLength
initialMinValueY
initialMaxValueY
Integers
Geben die Anfangswerte (in Einheiten der übergebenen
Werte) der Achsen vor.
scaleAxes
Boolean
Gibt an, ob die Achsen den Werten angepasst werden
sollen. Falls der Wert auf true gesetzt ist, werden die
Achsen bei Überschreiten der Maximal- bzw.
Minimalwerte frisch berechnet. Dies führt dazu, dass der
Graph die gegebene Fläche immer optimal ausfüllt.
Die Beschreibung der wichtigsten Methoden dieser Klasse:
Seite 53/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
è setValues(): Diese Methode ist überladen. Das heisst es können Werte für den
entsprechenden
Graph
in
verschiedenster Form übergeben
//get the draw params
werden. Konkret unterstützt werden:
if(scaleAxes) {
for (int arrayId=0;arrayId<values.length;arrayId++) {
if (maxArrayLength<values[arrayId].length){
zweidimensionale „Double“ Arrays,
maxArrayLength=values[arrayId].length;
}
zweidimensionale „Integer“ Arrays,
for (int i=0;i<values[arrayId].length;i++) {
if (values[arrayId][i]<minValueY){
minValueY=(int) java.lang.Math.floor(values[arrayId][i]);
und eindimensionale „Integer“
}
if (values[arrayId][i]>maxValueY){
maxValueY=(int) java.lang.Math.ceil(values[arrayId][i]);
Arrays.
Dazu
kommt
die
}
}
Möglichkeit, ebenfalls die Fläche
}
}
tickDistanceX= ((double)(graphAreaWidth-offsetLeft-offsetRight)/(maxArrayLength-1));
des Graphen (neu) zu setzen. Bei
tickDistanceY= ((double)(graphAreaHeight -offsetBottom-offsetTop)/(maxValueY-minValueY));
den zweidimensionalen Arrays
clear(g);
//draw the axes
setForeground(clrFont);
entspricht die erste Dimension
g.setColor(clrAxes);
g.setFont(new java.awt.Font("Arial", 0, 10));
immer einer Kurve und die zweite
x0=offsetLeft;
xMax=(int) java.lang.Math.floor((maxArrayLength-1)*tickDistanceX+offsetLeft);
Dimension der entsprechenden
yMin=graphAreaHeight-offsetBottom;
y0=(int) java.lang.Math.floor(graphAreaHeight+minValueY*tickDistanceY-offsetBottom);
yMax=(int) java.lang.Math.floor((graphAreaHeight-(maxValueY-minValueY)*tickDistanceY-offsetBottom));
Werte dieser Kurve. Um die Klasse
g.drawLine( x0,y0,xMax,y0); //x-axes
g.drawLine( x0,yMin,x0,yMax); //y-axes
allgemein zu halten, werden alle
//draw the ticks on the x-axes:
for (int i=1;i<maxArrayLength-1;i++){
Werte intern
auf „Doubles“
int tickX=(int) java.lang.Math.floor(x0+i*tickDistanceX);
if (i%10==0){
abgebildet.
(Eine
Bestellung
1 wird
g.drawLine(tickX,y0-3,tickX,y0+3);//the big ticks
g.drawString(Integer.toString(i),tickX-5,y0+12);
}
also als 1.0 behandelt.) Wie leicht
else if (i%5==0 && tickDistanceX*5>=20){//20 points for fontlenght
g.drawLine(tickX,y0-3,tickX,y0+3);//the big ticks
g.drawString(Integer.toString(i),tickX-5,y0+12);
einzusehen ist, beeinflusst das die
}
else if (tickDistanceX>=1.5)
Genauigkeit der Darstellung nicht
g.drawLine(tickX,y0-1,tickX,y0+1);//the small ticks
}
//draw the ticks on the y-axes
(im
Gegenteil),
einzig
der
for (int i=0;i<=maxValueY-minValueY;i++) {
int tickY=(int) java.lang.Math.floor(graphAreaHeight-(offsetBottom+i*tickDistanceY));
Speicherbedarf
erhöht
sich
leicht,
if ((i+minValueY)%500==0 && tickDistanceY*200<10){//don't draw 500 if we draw in 200er steps
g.drawLine(x0-3,tickY,x0+3,tickY);
g.drawString(Integer.toString(i+minValueY),x0-17,tickY+3);
wenn Doubles anstelle von Integern
}
if ((i+minValueY)%200==0 && tickDistanceY*200>=10){
g.drawLine(x0-3,tickY,x0+3,tickY);
verwendet werden.
g.drawString(Integer.toString(i+minValueY),x0-17,tickY+3);
public synchronized void paint(Graphics g) {
maxArrayLength=initialXAxesLength;
minValueY=initialMinValueY;
maxValueY=initialMaxValueY;
}
if ((i+minValueY)%100==0 && tickDistanceY*100>=10 ){//don't draw 50 if we draw in 20er steps
g.drawLine(x0-3,tickY,x0+3,tickY);
g.drawString(Integer.toString(i+minValueY),x0-17,tickY+3);
}
if ((i+minValueY)%50==0 && tickDistanceY*50>=10 &&tickDistanceY*20<10 ){//don't draw 50 if we draw in 20er steps
g.drawLine(x0-3,tickY,x0+3,tickY);
g.drawString(Integer.toString(i+minValueY),x0-17,tickY+3);
}
else if ((i+minValueY)%20==0 && tickDistanceY*20>=10){//>=10 point font
g.drawLine(x0-3,tickY,x0+3,tickY);
g.drawString(Integer.toString(i+minValueY),x0-17,tickY+3);
}
else if ((i+minValueY)%10==0 && tickDistanceY*10>=10){//>=10 point font
g.drawLine(x0-3,tickY,x0+3,tickY);
g.drawString(Integer.toString(i+minValueY),x0-17,tickY+3);
}
else if (tickDistanceY>=1.5)
g.drawLine(x0-1,tickY,x0+1,tickY);
}
//draw the different graphs
for (int arrayId=0;arrayId<values.length;arrayId++) {//for each graph to draw
if(graphsToDrawContains(arrayId)) {
for (int i=0;i<values[arrayId].length-1;i++) {//the segments of the graphs
int p1X=(int)java.lang.Math.floor(i*tickDistanceX+offsetLeft);
int p1Y=(int)java.lang.Math.floor(graphAreaHeight-(values[arrayId][i]-minValueY)*tickDistanceY-offsetBottom);
int p2X=(int)java.lang.Math.floor((i+1)*tickDistanceX+offsetLeft);
int p2Y=(int)java.lang.Math.floor(graphAreaHeight-(values[arrayId][i+1]-minValueY)*tickDistanceY-offsetBottom);
//set the color of the segment
if (clrSegment[arrayId][i]==null) {
if (clr[arrayId]==null)
clrSegment[arrayId][i]=clrDefaultGraph;
else
clrSegment[arrayId][i]=clr[arrayId];
}
g.setColor(clrSegment[arrayId][i]);
//draw the next segment (strength: 2 points)
g.drawLine(p1X,p1Y,p2X,p2Y);
g.drawLine(p1X,p1Y+1,p2X,p2Y+1);
}
}
}
è setGraphsToDraw(): Diese Methode
bestimmt, welche Graphen der
übergebenen
Doppelarrays
gezeichnet werden sollen. So ist es
möglich, auf einfachste Weise
einzelne
Graphen
ein
oder
auszublenden, ohne die gesamten
Werte frisch übergeben zu müssen.
è setInitialAxes():
Über
diese
Methode
kann
der
Anfangswertebereich (oder je nach
dem Boolean „scaleAxes“ der
Fixwertbereich) der Achsen gesetzt
werden.
}
Seite 54/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
è setGraphColor(): Über diese Methode kann die Farbe eines bestimmten Graphen gesetzt
werden
è getGraphColor()/getValues()/getGraphsToDraw() geben die entsprechenden Werte zurück.
Diese Methoden werden verwendet, um eine Vergrösserung des ausgewählten Graphen in
einem neuen Fenster darzustellen. Die Funktion „showGraphWindow“ ist in der Klasse
„BeergameClient“ implementiert, wird aber derzeit nicht verwendet.
è paint(): diejenige Methode, welche die Graphen schliesslich zeichnet. Um einen kleinen
Eindruck der Umsetzung des gesamten Spieles zu erhalten ist diese Methode – stellvertretend
für viele andere – auf der vorhergehenden Seite abgedruckt worden.
10.11 DlgNewGame.java
Diese Klasse stellt das Interface zum Erstellen eines neuen Spiels in einem Dialog zur
Verfügung. Grundsätzlich hätte diese Klasse auch direkt in die Klasse „BeergameClient“
integriert werden können. Da die Klasse BeergameClient aber bereits sehr gross ist (über 1500
Zeilen), wurde für alle GUI Komponenten, für welche es sinnvoll war, eine eigene Klasse erstellt.
Allein diese Klasse hat wiederum über 600 Zeilen...
Eine Instanz dieser Klasse wird erzeugt durch die ServerMessage „GRANT_NEW_GAME“,
nachdem der Spieler auf den Button „New Game“ geklickt hat und der Server die Berechtigung
für die Erstellung eines neuen Spiels erteilt hat. Grundsätzlich ist diese Klasse nichts anderes als
eine Eingabemaske für die Spielparameter. Ergänzt wird sie durch ActionHandlers, welche die
Maske dynamisch anpassen aufgrund der Eingaben des Benutzers. Ein paar wenige Methoden
übernehmen die Prüfung der (lokalen) Korrektheit der eingegebenen Daten. Ist diese Prüfung
erfolgreich abgeschlossen, werden die Parameter an den Server übermittelt und nach einer
weiteren Prüfung bei Erfolg das Spiel erzeugt (vgl. Methode handleCreateNewGame() in
beergame.server.SendThread).
Die wichtigsten Methoden dieser Klasse sind:
• bttnOk_actionPerformed(): aufgerufen durch den ActionListener des Ok-Buttons. Diese
Methode liest alle Werte der Eingabemaske ein und überprüft sie soweit möglich auf
Korrektheit und gibt bei Bedarf Fehlermeldungen bzw. Feedback zurück. Ist die Prüfung
erfolgreich abgeschlossen, werden die Parameter in Arrays gepackt und mittels der
ServerMessage CREATE_NEW_GAME an den Server versandt.
• bttnCancel_actionPerformed():
sendet
an
den
Server
die
Message
NEW_GAME_CANCELLED, damit der Server weiss, dass dieser Spieler wieder verfügbar
ist. Ausserdem wird die Eingabemaske (dieses Objekt)s geschlossen.
• handlePlayerStrategyEnabled(): diese Methode wird ebenfalls durch einen ActionListener
aufgerufen. Sobald eine Spielrolle auf„Computer“ umgestellt worden ist, wird eine Auswahl
von Strategien eingeblendet, welche dieser Computerspieler spielen kann. Ansonsten wird
diese Auswahl wieder ausgeblendet.
Seite 55/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
10.12 DlgJoinGame
Diese Klasse stellt – sehr ähnlich der Klasse DlgNewGame – dem Spieler eine Maske für die
Auswahl einer Rolle für ein Spiel zur Verfügung. Ein Objekt dieser Klasse wird erzeugt und
dargestellt wenn der Spieler ein Spiel markiert und den Join-Button geklickt hat oder wenn ein
Spielinhaber einen bestimmten Spieler zum Mitspielen eingeladen hat.
Die Maske stellt – neben den Informationen zum betreffenden Spiel - vier Buttons zur Verfügung
– je einen für den Retailer, Wholesaler, Distributor und die Factory. Je nachdem, ob die Rollen
bereits vergeben sind (an einen anderen Mitspieler oder vom Computer besetzt), sind die Buttons
aktiv oder nicht. Ist dieser Panel offen und ein anderer Spieler wählt gleichzeitig eine Rolle des
entsprechenden Spiels aus, wird der Button der entsprechenden Rolle sofort durch eine
ServerMessage inaktiviert. Diese verhindert, dass bereits vergebene Rollen ausgewählt werden
können. Sind alle Rollen vergeben ohne dass der Spieler eine Rolle gewählt hat, wird dieses
Panel geschlossen und dem Spieler eine Meldung ausgegeben, dass er zu langsam gewesen sei
und sein Glück bei einem anderen Spiel versuchen soll....
Hat sich der Spieler für eine Rolle entschieden und den entsprechenden Button angeklickt, wird
die Anfrage an den Server übergeben, welche diese nochmals prüft und bei Erfolg dem Spieler
die gewünschte Rolle zuteilt. Darauf wird dieses Dialogfenster geschlossen und das Spiel mittels
der Methode „setupGame()“ aufgesetzt. Sobald alle Rollen vergeben sind, startet dann das Spiel.
Die wichtigsten Methoden sind:
• setPlayersAvailable(): aktiviert oder inaktiviert die Buttons der verschiedenen Rollen. Wird
durch den Konstruktor des Objekts und durch ServerMessages aufgerufen.
• updatePlayerStatus(): setzt den Namen eines neu hinzugekommenen Spielers und ruft die
Methoden „setPlayersAvailable()“ sowie „checkGameFull()“ auf.
• sendAcceptInvitation(): sendet die ausgewählte Rolle an den Server. (Anmerkung: der Name
der Methode ist etwas unglücklich und ist auf die Tatsache zurückzuführen, dass ich zuerst
die Invitation-Funktion und erst später die Join Funktion implementiert habe. Die Methode
wird natürlich aber auch bei einem JOIN aufgerufen.)
• bttnRefuseInvitation_actionPerformed: teilt dem Server (bzw. danach dem entsprechenden
Client) mit, dass der Spieler diesem Spiel nicht beitreten möchte.
Seite 56/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
10.13 PnlStock.java, PnlGoodsReceiving.java und PnlTranport.java
Diese Klassen sind wiederum aus der Klasse „BeergameClient“ ausgelagert worden, um diese ein
wenig schlanker zu machen. Diese drei Klassen beinhalten die Grafiken für die Darstellung des
Lagers, der Waren im Wareneingang und der Waren in Transport.
Methode paint() in PnlStock.java
...
//the ground below the barrels
g.drawImage(imgGround, 0, 200, this);
//the barrels itself
int counter=0;
int z=4;
while (z>=0) {
int x=0;
while (x<=3){
int y=3;
while (y>=0) {
if (++counter>nrOfBarrels)
return;
g.drawImage(imgBarrel, x*38+y*20, z*40-y*20+62, this); //img=38x43
y--;
}
x++;
}
z--;
}
...
Methode paint() in PnlTransport.java
...
int counter=0;
int offsetX=70;
int offsetY=-20;
//1.) z-axes from top to bottom => calculate the height and rest to draw
//2.) y-axes from behind to front
//3.) x-axes from right to left
Die Hauptaufgabe besteht jeweils in der
korrekten Darstellung der Anzahl
Fässer. Diese Aufgabe hat sich als
ziemlich „tricky“ herausgestellt. Der
Grund
ist,
dass
die
Fässer
„dreidimensional“ dargestellt werden,
also hintereinander und aufeinander
gestapelt. Dies erfordert – neben der
Umrechnung eines dreidimensionalen
Zustandes auf ein zweidimensionales
Bild – eine Berücksichtigung der
Sichtbarkeit, je nach Blickwinkel des
Beobachters (die Waren im Lager und
Lagereingang haben eine andere
Ausrichtung als die Waren in
Transport). Die Methoden mussten
dementsprechend umgestellt werden
(siehe links.).
//nrOfBarrels=47;
int n=(nrOfBarrels>48)?48:nrOfBarrels;//maximum load of truck is 48
int xMax = 4;
int yMax = 3;
int zMax = 4;
int ZzMax = (int) Math.floor((double) n/(xMax*yMax)); // nrOfBarrels / BarrelsPerLevel
int XzMax = (int) Math.floor((double) (Math.floor( (n%(xMax*yMax))/yMax) ));
int YxzMax =
(n%(xMax*yMax))%yMax;
Wie links zu sehen ist, wird die
Aufgabe ungleich komplizierter, wenn
ein Blickwinkel von vorne unten
for (int z=0;z<=ZzMax;z++) {
for (int x = xMax-1;x>=((z==0)?(xMax-1-XzMax):0); x--) {
gewählt wird. Hier muss für die
for (int y = 0; y < ((z==0 && x==xMax-1-XzMax)?YxzMax:yMax) ;y++) {
//imgBarrelSmall=30x34
Sichtbarkeit zuerst das Fass zuoberst
//calculation from 3-d to 2-d
g.drawImage(imgBarrel, x*20+y*20+offsetX, (zMax-ZzMax+z)*31-y*6+x*1+offsetY , this);
und zuhinterst gezeichnet werden. Aus
}
}
diesem Grund muss errechnet werden,
}
g.drawImage(this.imgTruck,0,40,this);
wo sich dieses Fass (und danach alle
...
anderen) im dreidimensionalen Raum befinden. In der ersten Methode kann einfach ein Counter
mitgezählt werden, welcher die Schleife unterbricht, sobald die Anzahl notwendiger Fässer
gezeichnet ist.
Seite 57/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
11
mögliche Erweiterungen des Spieles
Selbstverständlich wären noch viele Erweiterungen des Spieles möglich gewesen. Da die Zeit der
Diplomarbeit aber beschränkt war, musste ich Prioritäten setzen. Für den Fall, dass zu einem
späteren Zeitpunkt dieses Programm erweitert werden soll, führe ich nachfolgend ein paar Punkte
auf, bei denen angesetzt werden könnte.
Implementation weiterer Strategien:
Dies wäre sicher eine interessante Erweiterung, welche wenig Aufwand erfordert, da das
Programm eigentlich bereits einen ausgewachsenen Simulator mit vollständiger (graphischer)
Auswertung zur Verfügung stellt. Methoden für die Berechnung von Standardabweichung und
gleitendem Mittelwerte sind ebenfalls bereits implementiert. Es könnte sehr einfach gezeigt
werden, wie verschiedene weitere Spielstrategien sich unter – und gegeneinander verhalten.
Ausserdem könnten dem Spieler zusätzliche Parameter zum Einstellen zur Verfügung gestellt
werden, wie zum Beispiel die Anzahl Perioden des gleitenden Mittelwerts oder der
Sicherheitsfaktor.
Weitere Bestellmuster des Endkunden:
Eine weitere wenig aufwändige Erweiterung wäre die Variation des Bestellmusters des
Endkunden. Hier könnte zum Beispiel mit Zufallsschwankungen, Saisonalitäten und anderen
Einflüssen gespielt werden.
Erweiterte Animationen im Client:
Um den Ubergang von einer Runde auf die nächste den Spielteilnehmern besser verständlich zu
machen, wäre es sinnvoll ,vermehrt Animationen einzuführen. Es könnte dies zum Beispiel ein
kleiner Kran sein, der die Fässer transportiert.
Erweiterung der Spiellogik und des Spielablaufs:
Eine Möglichkeit würde bestehen in der Variation der Länge der Supply Chain. Das Spiel wurde
extra sehr allgemein programmiert, um solche Erweiterungen zu ermöglichen. Eine kleine
Herausforderung wäre wahrscheinlich das GUI dynamisch anzupassen.
Eine weitere Idee wäre, das Spiel von einer Kette auf eine baumartige Struktur zu erweitern. Dies
würde bedeuten, dass es zum Beispiel eine Fabrik, zwei Verteiler, vier Grosshändler und acht
Kleinhändler geben könnte.
Benutzeranmeldung per E-Mail:
Für eine eindeutige Identifizierung der Spieler könnte das Passwort auf die angegebene EmailAdresse versandt werden. Dies würde zum einen die Gültigkeit der Email-Adresse überprüfen
und zum anderen den Spieler vor „unberechtigem“ Anmelden unter seiner Email-Adresse
schützen. Es muss allerdings abgewogen werden, ob diese Massnahmen wirklich notwendig sind.
Ein Vorteil (und gleichzeitig eine zusätzlich Erweiterung) könnte sein, dass man zum Beispiel für
eine Übung unter Student en, die Teilnehmer per Email direkt für ein bestimmtes Spiel einladen
könnte.
Seite 58/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
Seite 59/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
12
Spielbeschreibung
Im Folgenden wird versucht – unter Zuhilfenahme von Screenshots - einen kleinen Eindruck
des Spieles zu vermitteln.
12.1
Die Anmeldung
Sobald das Applet geladen ist, wird die Socketverbindung gestartet und der Logon-Dialog
erscheint. Hier kann der Spieler seinen Namen und seine E-Mail-Adresse eingeben. Die E-MailAdresse wird verwendet als globaler- und der Name als lokaler Identifikator. Diese bedeutet, dass
der Spieler mittels seiner E-Mail-Adresse immer seine früher gespielten Spiele wieder ansehen
kann. Der Name selbst identifiziert den Spieler hingegen nur während der gerade laufenden
Session eindeutig. Ist der Name bereits in Verwendung, muss eine anderer gewählt werden.
Seite 60/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
12.2
Der Welcome-Screen
Nach einer erfolgreichen Anmeldung hat der Spieler mehrere Möglichkeiten: Falls er die Regeln
des Beergames noch nicht kennt, kann er sich mittels der Beschreibungen einlesen. Ein ChatFenster steht zur Verfügung um sich mit anderen Spielern zu unterhalten.
Auf der rechten Seite des Applets finden sich verschiedene Listen: In der Spielerliste werden alle
Spieler aufgeführt, welche online und noch keinem Spiel zugewiesen sind. In der Liste der
offenen Spiele werden jene Spiele angezeigt, welche noch Plätze (- * - ) freihaben. In der vierten
Liste auf der rechten Seite werden die Spiele aufgeführt, welche der Spieler bereits absolviert hat.
Als Spiel-ID wird jeweils das Datum, die Uhrzeit und der Spielname verwendet.
Nun kann der Spieler entweder ein neues Spiel erzeugen, bei einem Spiel mitspielen oder sich die
Auswertungen eines alten Spiels ansehen
Seite 61/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
12.3
Ein neues Spiel erzeugen
Ein neues Spiel kann erzeugt werden über den Knopf „neues Spiel“:
Verschiedene Parameter können nun festgelegt werden:
è Der Spielname: Ist der Name des Spiels und muss in der laufenden Session eindeutig sein.
Das heisst es können nicht zwei Spiele mit dem gleichen Namen gleichzeitig existieren.
Diese ID wird auch verwendet (zusammen mit Datum und Uhrzeit) für die spätere
Wiedererkennung des Spieles.
è Der Spielmodus: Drei Varianten sind möglich. Der klassische Modus entspricht dem
klassischen Brettspiel. Die Rundenzahl ist auf 25 festgelegt und Informationen der
anderen Mitspieler sind während des Spiels nicht verfügbar. Im Modus „klassisch plus“
können weitere Einstellungen gemacht werden. Die Rundenzahl kann gewählt, der Chat
eingeschaltet sowie die Informationen aller Mitspieler verfügbar gemacht werden. Der
dritte Modus ist die Computer-Simulation. Hier gibt es nur Computerspieler Es können
aber verschiedene Strategien für die einzelnen Computerspieler festgelegt und die
Auswirkungen anschliessend in der Auswertung studiert werden
è Die Zeit pro Spielrunde: Gibt an, wie viel Zeit pro Bestellung zur Verfügung steht. Läuft
diese Zeit ab, übernimmt der Computer für den betreffenden Spieler die Bestellung. Dies
verhindert, dass ein einzelner Spieler das gesamte Spiel blockieren kann.
è Die Anzahl Runden: Entspricht der Anzahl zu spielenden Runden. Das Minimum ist zehn
und das Maximum 99.
Seite 62/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
è Die Spielerauswahl: Hier stehen wiederum drei Varianten zur Verfügung. Der Spieler
kann seine eigene Rolle auswählen und festlegen, welche Rollen von anderen Mitspielern
und welche von Computerspielern übernommen werden sollen.
è Die Strategieauswahl: Hier kann der Spieler für die Computerspieler die zu spielende
Strategie auswählen. Zurzeit sind zwei Strategien verfügbar. „moving average & standard
deviation“ berechnet die zu bestellende Menge aus den Zahlen der Vergangenheit mittels
gleitendem Mittelwert und Standardabweichung. „keep level of stock“ versucht das Lager
auf einem möglichst Konstanten Niveau zu halten. Die erstere entspricht etwa dem
Verhalten eines menschlichen Mitspielers.
è Kommentar für andere Mitspieler. Hier kann beliebiger Text eingegeben werden, welcher
andere Mitspieler später sehen bei der Anmeldung zu diesem Spiel.
è Checkbox „Informationen aller Comaker verfügbar“: Falls sie aktiviert wird (nicht im
Modus klassisch) sind während des Spiels in der Übersicht die Informationen aller
Mitspieler sichtbar (Lager, Bestellung...)
è Checkbox „chat während des Spiels eingeschaltet“ : Schaltet die Chatfunktion während
des Spiels ein und ermöglicht es den Spielern sich zu unterhalten.
è Checkbox „andere Spieler dürfen diesem Spiel beitreten“: Falls aktiviert, können sich
andere Spieler mittels des Knopfes „mitspielen“ zu diesem Spiel anmelden. Andernfalls
kann nur der Spielerzeuger die anderen Mitspieler einladen mitzuspielen (mittels des
Knopfes „Spieler einladen“
è Checkbox „Erlaubnis notwendig
um diesem Spiel beizutreten“.
Falls
aktiviert,
kann
der
Spielerzeuger eine Anfrage
akzeptieren
oder
ablehnen.
Ansonsten werden die Spieler
direkt dem Spiel zugewiesen
Seite 63/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
12.4
Beitreten zu einem Spiel
Einem Spiel beitreten ist entweder per Einladung (Knopf „Spieler Einladen“ ) oder per
Einschreiben (Knopf „mitspielen“) möglich. Beides führt schliesslich zum unten sichtbaren
Dialog:
Angezeigt werden die wichtigsten Informationen zum Spiel, sowie die noch verfügbaren Rollen,
welche durch Drücken des jeweiligen Knopfes ausgewählt werden kann. Wählt ein anderer
Spieler gleichzeitig eine Rolle aus, wird diese (bzw. der entsprechenden Knopf) sofort inaktiv
und der Name des entsprechenden Spielers wird angezeigt. Dies verhindert das Anmelden zweier
Spieler für dieselbe Rolle.
Seite 64/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
12.5
Spielstart
Sobald eine Rolle gewählt wurde, oder ein eigenes Spiel kreiert hat wechselt die Ansicht des
Applets auf „Spielstart“ wie unten abgebildet. Jedem Spieler ist eine eigene Farbe zugeordnet,
welche auch für die Auswertung verwendet wird.
Andere Spieler können weiterhin dem Spiel beitreten. Noch nicht besetzte Rollen werden mit
einem Stern „*“ gekennzeichnet. Sobald alle Spieler zugewiesen sind (respektive alle Rollen an
von Menschen oder Computerspielern besetzt sind) ist das Spiel bereit und ein Dialog erscheint,
welcher die Spieler nach Ihrer Bereitschaft abfragt und über den Spielstart informiert. Sobald alle
Spieler „bereit“ zurückgemeldet wird der Button „senden“ aktiviert und der Timer gestartet.
Seite 65/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
12.6
Das Spiel
Sobald alle Spieler bereit sind, wird das Spiel gestartet. Die
Bestellungen müssen nun aufgrund der vorhandenen Informationen
gemacht werden. Ein Spielaublauf könnte folgendermassen aussehen:
Runde 4: Das Spiel hat begonnen
Runde 16: Der Bullwhip-Effekt hat voll zugeschlagen
Die Spieler geben nun in jeder Runde in der vorgegebenen Zeit
ihre Bestellungen auf.
Sehr schön ist hier zu sehen wie sich die Achsen der Graphen
dynamisch den Werten anpassen.
Runde 17: Das Lager erholt sich langsam
Runde 18: Das Spiel hat sich eingependelt
Runde 22: Der Spieler hat ein Timeout...
Runde 25: Spielende. Mittels des Knopfs “Auswertung”
werden die Auswertungsgaphiken sichtbar.
Das Segment des Bestellungs-Graphen wird rot gefärbt
Seite 66/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
12.7
Die Auswertung
Die
Auswertung
des
obigen Spieles ist links zu
sehen. Es werden zwei
verschiedene
Ansichten
angeboten.
Die erste enthält die
Einzelgraphiken
der
Bestellungen,
Lagerbeständen, Kosten pro
Runde und summierten
Kosten aller Mitspieler.
Sehr schön ist der Bullwhip
Effekt zu Beobachten,
wenn mittels der Buttons
oberhalb der Graphiken
durch die einzelnen Runden
gefahren wird.
In der zweiten Darstellung
werden
die
Graphen
überlagert gezeigt. Zudem
werden in einer Tabelle die
totalen
Kosten
pro
Mitspieler, die Kosten pro
Runde,
die
durchschnittliche
Bestellmenge sowie die
Standardabweichung
der
Bestellungen angezeigt.
In dieser Darstellung können die Kurven der Mitspieler einzeln ein oder ausgeblendet werden.
(im obigen Beispiel ist der Endkunde ausgeblendet).
Zuunterst können die Kosten der gesamten Supply Chain abzulesen welche ja zu minimieren
waren...
Seite 67/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
12.8
Auswertung einer Computer Simulation
Den Spielern wird die Möglichkeit geboten Computersimulationen durchzuführen. Dazu können
den einzelnen Computerspielern verschieden Strategien zugeordnet werden und anschliessend die
Auswirkungen in der Auswertung betrachtet werden. Simuliert werden kann über eine beliebige
Anzahl Runden zwischen 10 und 99. Die Rundenzahl wurde auf 99 beschränkt, da sich bis dahin
alle Systeme eingependelt haben und damit die Kommunikation zwischen Client und Server nicht
zu sehr belastet werden kann. Die Datenmenge welche der Server dem Client übermitteln muss
beläuft sich auf knapp 4kB bei 99 Runden (3772byte). Dies ist zwar sehr wenig im Vergleich zu
einem Bild auf einer normalen Webseite, kann aber bei vielen Aufrufen doch etwas ausmachen,
wenn z.B. 1000 Runden möglich wären.
Nachfolgend ist eine Simulation über 35 Runden zu sehen, in welcher allen Computerspielern die
Strategie „moving average & standard deviation“ zugeordnet worden ist.
In den Charts auf der linken Seite ist der Verlauf bis zur Runde 15 ersichtlich, rechts ist jeweils
das gesamte Spiel zu sehen.
Seite 68/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
12.9
Animationen
Es ist recht schwierig Animationen in einem Textdokument darzustellen J .Dennoch um einen
kleinen Eindruck zu vermitteln habe ich hier noch zwei Bilder angehängt. Auf dem einen ist zu
sehen, wie der Lastwagen gerade ins Bild hinein fährt. Auf dem Anderen kommt gerade die
Bestellung des Kunden an.
Für den vollen Live-Effekt kann ich nur ein ausprobieren des Spieles empfehlen...
Seite 69/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
13
Anhang
Im Folgenden habe ich noch drei Ablaufdiagramme angehängt, welche ich für die erste
Zwischenpräsentation verwendet habe. Aus zeitlichen Gründen war es mir nicht mehr möglich
diese vollständig auf den aktuellsten Stand zu bringen. Einzelne Abläufe (oder Teile des
angegebene Source Code) können demnach leicht vom Original abweichen. Dennoch dienen Sie
als gutes Beispiel wie die Prozesse in diesem Spiel etwa ablaufen.
Anhang
Seite 70/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
Seite 71/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
13.1
Übersicht über die Klassen und ihre Abhängigkeiten
Seite 72/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
13.2
Der Server Start Prozess / Start of a socket connection
Seite 73/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
13.3
The Invitation Process – a three way handshake
Seite 74/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
Seite 75/76
Diplomarbeit, The Beer Distribution Game
Christoph Duijts
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of TechnologyZurich
14
Literaturverzeichnis
Burkhalter J. P. 2001:
Simulation der Dynamik in Supply Chains mit Hilfe des
Simulators SIMPLE ++,
Studienarbeit ETHZ, BWI, Team Prof. Paul Schönsleben
Flanagan D. 1998:
JAVA IN A NUTSHELL,
O’Reilly Verlag Köln
Hieber R. u.a. 2001:
Supporting the Beer Game by the simultaneous use of simulation
software
Center of Enterprise Science (BWI), Swiss Federal Institut of
Technology ETH Zürich
Lee H. u.a. 1997:
The Bullwhip Effect in Supply Chains
Sloan Management Review
Schönsleben, P. 1998:
Integrales Logistikmanagement,
Springer Verlag, Berlin Heidelberg
Sedgewick R. u.a. 1999:
Algorithms in Java Part 1-4
Addison-Wesley
Simchi-Levi D. u.a.. 2000:
Designing and Managing the Supply Chain,
Irwin McGraw-Hill
Sterman J.D. :
Teaching Takes Off, Flight Simulators for Management Education
„The Beer Game“
Sloan School of Management, Massachusetts Institute of
Technology (MIT):
http://web.mit.edu/jsterman/www/sdg/beergame.html
Theisen M.R. 1997
Wissenschaftliches Arbeiten, 8. Auflage
Verlage Franz, München
Weitere:
Beer Game Präsentationsordner
Caglar D. u.a. 2000:
Beer Game
http://beergame.iems.nwu.edu/index.html
Seite 76/76
Herunterladen