Java Projekt: Tick Tack Toe
Rechnerpraktikum aus Programmierung
Kerstin Baumgarten
11-01-2004
Name: Kerstin Baumgarten
Matrikelnummer: 0025447
Lehrveranstaltung: Rechnerpraktikum aus Programmierung
Leiter: Dr. Michael Hahsler
Semster: WS 2003/04 – 2. Halbsemester
E-mail: h0025447@wu-wien.ac.at
Projektziel:
Es soll ein lauffähiges Programm geschrieben werden, damit verschiedene Spieler (pro Spiel 2 Mitspieler) an verschiedenen Orten, das Spiel Tic Tac Toe miteinander spielen können.
Lösung:
Verschiedene Spieler sollen über eigene Clients (ausführbar über Telnet / bzw. mittels Gui) über Sockets mit einem Server kommunizieren. Die Clients können auch über verschiedene Rechner laufen und über einen gemeinsamen Server mittels Sockets kommunizieren. Die Programmiersprache ist Java. Für das Gui wurden Swing-Klassen verwendet.
Das
Spiel Tic Tac Toe:
Das Spielfeld von Tic Tac Toe besteht aus 9 Feldern, die in drei Reihen und Spalten angeordnet sind. Ziel des Spieles ist es 3 gleiche entweder waagrecht, senkrecht oder diagonal nebeneinander zu setzen.
Am Anfang sind alle Felder frei. Zwei Spieler ziehen abwechselnd. Ein Zug eines Spieler besteht aus dem Besetzen eines beliebigen bis dahin freien Felds. Wenn ein Spieler 3 Felder waagrecht, senkrecht oder diagonal besetzen kann, hat er sofort gewonnen. Sind alle Felder belegt und kein Spieler konnte 3 Felder waagrecht, senkrecht oder diagonal besetzen so endet das Spiel unentschieden.
Use Case Diagramm:

Das Use Case “Anmelden” beschreibt die Anmeldeprozedur eines neuen Spielers. Er gibt zuerst seinen Namen ein und sucht dann aus der Liste einen Mitspieler aus.
Fehlszenario: Wenn schon ein User mit diesem Name angemeldet ist, muss er einen anderen Namen wählen.
Das Use Case „Spielen“ beschreibt den Spielablauf. Züge werden durch klicken auf das Spielfeld gesetzt und dann überprüft ob sie korrekt oder falsch sind.
Fehlszenario: Falsche Züge müssen korrigiert werden. Gezogen wird abwechselnd.
Das Use Case „Hilfe“ gibt Anleitung wie man mit dem Programm umzugehen hat.
Das Use Case „Anleitung“ gibt Hilfe zu den Spielregeln.
Das Use Case „Abmelden“ beschreibt den Abmeldevorgang. Der Spieler muss aus der Liste der möglichen Mitspieler entfernt werden. Während eines Spiels muss der Mitspieler verständigt werden.
Sequenzdiagramm:

Klassendiagramm:
Das Klassendiagramm gibt den Zusammenhang der selbstgeschriebenen Klassen wieder. Javastandardklassen wie aus java.net.*; java.io.*; java.util.*; java.awt.*; java.awt.event.*; javax.swing.*; verwendet wurden, werden nicht extra beschrieben. Für die Kommunikation zwischen Server und Client wurden Standardsockets mit Klassen und Methoden verwendet auch das Gui wurde mit Standardswing Klassen geschrieben.
Wenn ein Client sich beim Server anmelden will dann wird ein
neues ServerThread-Objekt für ihn erzeugt. Wenn 2 Spieler ein Spiel spielen
teilen sie sich ein gemeinsames Objekt der Klasse TTTSpiel.
Klassendiagramm
Server:

Klassendiagramm
fürs Gui:

Die Klasse ActionAdapter und WindowClosingAdapter sind Standardinterfaces die im TTTClient als Eventhandler implementiert werden.
Klassen im Detail:
Hier werden die Klassen, die im Klassendiagramm beschrieben wurden, detailliert mit ihren Instanzvariablen und Methoden beschrieben.
Klassendiagramm
Server - Detail:

Klassendiagramm
fürs Gui - Detail:

Klassenbeschreibung für den Server:
Klasse:
public
class TTTServer
Main-Methode zum Starten des Server. Eine Spielerliste wird erzeugt, jeder Spieler der sich anmeldet wird in diese Spielerliste eingetragen. Die Spielerliste steht jedem Spieler zur Verfügung. Die Methode ServerSocket.accept() wartet auf Clients die sich anmelden. Für jeden Client wird ein TTTServerThread-Objekt erzeugt.
Methode:
public
static void main (String[] args) throws IOException
Klasse:
public
class TTTServerThread extends Thread
Jedes Objekt dieser Klasse gehört zu einem Spieler. Der TTTServerThread wickelt die oberste Ebene der Kommunikation Client-Server ab.
Hier werden die Reader und Writer für die Socket-Kommunikation instanziert.
Instanzvariablen:
private
Socket socket = null;
private
TTTProtokoll protokoll;
Methoden:
public
TTTServerThread (Socket socket, LinkedList spielerliste)
Konstruktor bekommt die Socket und die Spielerliste vom TTTServer.
Ein Objekt von TTTProtokoll wird erzeugt. Dieses wickelt die Details der
Kommunikation zwischen Client - Server bez. einem anderen Client ab.
public void
run()
Run-Methode für den Thread. Wartet auf Messages vom Client und verarbeitet
sie mit Hilfe von Methoden vom TTTProtokoll.
Klasse:
public class TTTProtokoll
Wickelt die Details des TTTProtokolls der Server-Client Kommunikation und
der Client1- Server - Client2 Kommunikation ab. Definiert die verschiedenen
Stati im Spiel.
Konstanten der
Stati:
final int
INIT = 0;
final int
START = 1;
final int
NAME = 2;
final int
SPIELPARTNER = 3;
final int NAMENEU
= 4;
final int SPIELEN
= 5;
static final int
SPIELENDE = 6;
final int
EINGELADEN = 7;
final int
AUFANTWORTWARTEN = 8;
final int
AUFANTWORTWARTENWEITERSPIELEN = 9;
final int
NAMEEINGEGEBEN = 10;
Instanzvariablen:
private int
status;
private
TTTSpieler spieler; //merkt sich die
Daten der Spieler
private
LinkedList sliste; //Liste der
anderen Mitspieler vom Server
private TTTSpiel
spiel; //Das Spiel das gerade
gespielt wird
private
PrintWriter out; //Writer zum
Client
private boolean
erster; //True wenn dieser Spieler das
Spiel beginnt, sonst false
Instanzvariable
für die Weiterspielprozedur:
private int
ichspieleweiter; //0 noch keine
Antwort, 1 = ja, 2 = nein!
private int
partnerspieltweiter; //0 noch keine
Antwort, 1 = ja, 2 = nein!
Instanzvariable
für die Einladungsprozedur:
private TTTProtokoll
einladungsprotokoll; //Protokoll vom Einlader, der ein Spiel spielen will
Konstruktor:
public
TTTProtokoll (LinkedList spielerliste)
Methoden:
public void
austragen ()
Spieler wird aus der Spielerliste ausgetragen
public
String processInput (String inputLine)
Messages vom Client werden verarbeitet
Einzelne Befehle die das Protokoll versteht
Zurückgeschickt werden Nachrichten die dem Client geschickt werden
public
String processOutput ()
Der Server kann den Spieler eine 2te Botschaft schicken.
Es wird im Unterschied zum ProcessInput werden keine Nachrichten vom Client
verarbeitet. Es werden nur Nachrichten aufgrund vom Status des Spiels an den
Client geschickt. Diese Methode wird vom ServerThread-Objekt nach der Methode
ProcessInput aufgerufen.
public
String hallo ()
Gibt den Hallotext aus
public String
anleitung ()
Gibt die Spielanleitung aus
public
String hilfe ()
Gibt den Hilfetext zum Benützen des Spiels aus
public String
spielerlisteausgeben ()
Spielerliste außer mich selbst
public boolean
namefinden (String spielername)
prüft ob der Name in der Spielerliste enthalten ist
public
TTTProtokoll getprotokollvanderen (String spielername)
liefert das Protokollobjekt des anderen Spielers
public
TTTProtokoll getprotokollvanderen ()
Liefert das Protokollobjekt des anderen Spielers wie oben, nur setzt die
Methode voraus dass das Spiel schon existiert
public void
ausgeben(String aus)
Schickt direkt eine Botschaft über den Socket zum Client
Methoden fürs
Einladungsprotokoll:
public boolean
einladen (TTTProtokoll fragerprotokoll)
ist vom Mitspieler einladen worden
public void
einladungsantwort (TTTSpieler mitspieler, boolean antwort)
Die Methode mit der ein Spieler einem anderen seine Antwort schicken kann,
ob er spielen will.
Der Sender ruft diese Methode vom Empfänger auf, damit er Spielstatus vom
Empfänger ändern kann.
Methoden fürs
Weiterspielprotokoll:
public void
partnerwillweiterspielen (boolean jn)
Methode ähnlich wie Einladungsantwort verwendet, nur wird sie verwendet um
den Partner mitzuteilen ob man ein weiters Spiel spielen will.
Methoden fürs
Partnerverständigen:
public void
partnerverstaendigen ()
Wenn der Andere ‚bye’ sagt damit der Andere das weiss schickt dem Partner
eine Message über die Socket.
Set -
Get – Methoden:
Setzt und liefert Werte von privaten Instanzvarieblen
public TTTSpieler
getspieler ()
public void
setspielspieler2 (TTTSpiel sp2)
public int
getstatus ()
public void
setstatus (int stat)
public void
setout (PrintWriter sout)
public
PrintWriter getout ()
Klasse:
class TTTSpiel
Die Klasse speichert ein Spiel und seine Zustände, 2 TTTSpieler
beziehungsweise TTTProtokolle benützen ein Spiel.
Stati des
Spiels:
final int IMSPIEL
= 1;
final int
GEWONNEN = 2;
final int UNENTSCHIEDEN
= 3;
final int
DEBUG = 5;
Instanzvariablen:
private
TTTSpieler spieler1; //1 Spieler
private
TTTSpieler spieler2; //anderer Spieler
char []
spielfeld;
private boolean
bindran; //wechselt je nach Spielzug von True nach False
private int
spielstatus; //1 = im Spiel, 2 = Gewonnen, 3 = Unentschieden
Konstruktor:
public TTTSpiel
(TTTSpieler s1, TTTSpieler s2)
Methoden:
public
String toString ()
Gibt Spielbrett als String aus.
public String
spielzug (TTTSpieler spieler, boolean erster, String zahl)
Prüft ob der Zug ein gültiger Zug ist. Der gültige Zug wird gesetzt.
Prüft ob Sieg, unentschieden oder ob einfach noch weiter gespielt werden
muss.
private boolean
istunentschieden ()
Prüft ob der Spielstand unentschieden ist, wenn ja true, sonst false
private void
zugsetzen (String zahl, char chip)
Setzt einen Zug, Chip kann 'o' oder 'x' sein.
private boolean
zugpruefen (String zahl)
Prüft ob der Zug gültig ist
private boolean
istgewonnen (char chip)
Prüft ob das Spiel gewonnen ist
public String
getweiterspielensatz ()
Gibt die Frage für den Spieler ob er weiterspielen möchte aus.
public void
clear ()
Setzt den Spielstatus auf den Anfangszustand.
Get-
Set- Methoden:
public TTTSpieler
getspieler1 ()
public TTTSpieler
getspieler2 ()
public int
getspielstatus ()
Klasse:
class TTTSpieler
Speichert Daten zum Spieler, insbesonders seinen Namen und einen Link des
Spielers zu seinem TTTProtokoll.
Instanzvariablen:
private String
name;
private TTTProtokoll sprotokoll; //Variable damit man sich merkt welches Protokoll zu welchem Spieler gehört.
Konstruktor:
public TTTSpieler
()
Get-
Set- Methoden:
public void
setname (String n)
public void
setprotokoll (TTTProtokoll sp)
public
String getname ()
public
TTTProtokoll getprotokoll ()
Klassen fürs
GUI:
Klasse:
public
class TTTClient
Der Client implementiert das Gui der mit dem TTTServer kommuniziert.
Beim Start meldet sich der Client via Socket beim Server an, während des
Spiels bekommt er vom Server Messages über den Socket, die der Client
darstellt. Usereingaben meldet der Client über die Socketverbindung an den
Server.
Konstante: Die IP-Adresse ist hier änderbar und muss
die IP-Adresse des TTTServers sein damit der Client funktioniert
final
private static String IP = "localhost"; //IP Adresse vom server
final
private static int PORT = 4444; //Port
vom server
final public static String MARKER = “!”;
//der Marker wird auch von den TTTKlassen benützt um das Ende einer Message zu
markieren. Der Marker darf daher nur am Ende der Message vorkommen und nicht
innerhalb eines Textes.
Konstanten der
Stati:
final
private static int START = 0;
final private
static int VERBINDUNG = 1;
final
private static int BYE = 2;
Instanzvariable:
private
static int status;
private
static String eigenerName;
private
static JFrame mainFrame = null;
private
static JTextField eingabeFeld = null;
private
static JTextArea ausgabeText = null;
private
static JTextField nameFeld = null;
private
static Button[] tButton;
private
static int debug = 0;
private
static JRadioButton jaButton;
private
static JRadioButton neinButton;
private
static Socket clientSocket;
private
static BufferedReader in;
private
static PrintWriter out;
Methoden:
public
static void main (String[] args) throws IOException
Main Methode erzeugt Gui baut Socketverbindung zum Server auf, liest
Messages des Servers ein.
public
static void sendmessage (String sm)
Schickt dem Server eine Message via Sockets.
public static
void inputverarbeiten (String message)
Verarbeitet Messages vom Server.
Die verschiedenen Messages werden erkannt und für bestimmt Messages können
sie geändert werden und spezielle Reaktionen des Guis werden hier gesteuert.
public
static void initGUI ()
Hier wird das Gui erzeugt.
Auch der Eventhandler für den Window CloseEvent wird hier implementiert.
Wenn der User das Window schliesst wird eine Botschaft an den Gui geschickt,
der wird vom Eventhandler aufgefangen der dann eine Message (Socket) an den
Server schickt (damit der Server den Client aus der Spielerliste austragen
kann, bzw. einen Mitspieler informiert).
public
static JPanel initpane1 ()
Wird vom initGui verwendet und definiert das Erscheinungsbild des Guis.
Auch die Methoden für die Eventhandler (Buttons, Das Eingabefeld,
RadioButton) werden hier implementiert. Das Spielfeld wird mit 9 Buttons
implementiert, ein Button ist ein Spielfeld.
public
static void enableRadioButtons (boolean r)
Radiobuttons können ein und ausgeschalten werden
public
static void enableButtons (boolean b)
Buttons können benutzbar und unbenutzbar gemacht werden
public
static void setzeButton(int i, char chip)
Chip x oder o, wird auf den Button[i] gesetzt.
public
static String willkommenText ()
Willkommenstext wird angezeigt.
public
static String hilfeText ()
Hilfetext wird angezeigt.
public static
void spielfeldloeschen (String message)
Das Gui wird auf einen Anfangszustand gesetzt.
public
static void clearSpiel()
Nur das Spielfeld wird gelöscht.
Klasse:
class ActionAdapter implements ActionListener
Das Interface ActionListener wird als Eventhandler für Buttons in der
Methode initPanel implementiert.
Methode:
public void
actionPerformed (ActionEvent e)
Klasse:
class WindowClosingAdapter implements WindowListener
Eventhandler zum Schließen des Windows.
Server wird mit bye Message informiert, damit User aus der Liste
ausgetragen werden kann.
Methoden:
public void
windowClosing(WindowEvent e)
Die Methode windowClosing wird in der Methode
initGui implementiert
Diese Methoden
werden zwar vom Interface zur Verfügung gestellt aber nicht implementiert:
public void
windowActivated(WindowEvent e)
public void
windowClosed(WindowEvent e)
public void
windowDeactivated(WindowEvent e)
public void
windowDeiconified(WindowEvent e)
public void
windowIconified(WindowEvent e)
public void
windowOpened(WindowEvent e)
Implementierung:
Das Spiel hat
eine Server-Client Architektur. Im Server sind das Protokoll (die
Kommunikationsregel zwischen Server und Client und damit auch zwischen 2
Clients die über den Server kommunizieren), das Spiel und Daten zum Spieler als
Klassen implementiert. Der Server führt eine Liste aller Spieler. Der gesamt
Spielzustand ist in der Klasse Spiel implementiert. Im Protokoll wird nicht der
Spielzustand(Spielfeld, Spielzüge, ...) sondern der Kommunikationsprozess
zwischen Client und Server implementiert.
Die Kommunikation
zwischen Client und Server erfolgt mittels Sockets. Im Client sind keine
Spielzustände gespeichert, der Spielzustand der im Client angezeigt wird ist
jener den er vom Server geschickt bekommt. Alle Messages, die der Server einem
Client schickt werden durch einen eindeutigen Marker am Ende der Message
abgeschlossen. Diesen Marker benutzt der Client um alle Messages einzeln zu
verarbeiten wenn mehrere Messages gleichzeitig geschickt wurden.
(While-Schleife in der Main-Methode von TTTClient.java)
Wenn sich ein
Client (Telnet oder Gui) beim Server anmeldet dann startet der Server einen
Thread für den Client. Wenn sich ein Client abmeldet wird er aus der Spielerliste
ausgetragen.
Aussehen
des Guis:

Das Gui wurde mit Swing-Klassen implementiert. Die Spielfläche wurde mit 9 großen Buttons dargestellt. Die Useraktionen (drücken der Buttons) werden von Eventhandlern (Klasse ActionAdapter) aufgefangen. Die Eventhandler schicken Messages an den Server.
Die Erstellung des Quellcodes erfolgte auf einen Windows 2000 Rechner mittels Java-Compiler 1.4.2_03.
Der Client hat eine fix eingestellte IP-Adresse, da davon ausgegangen wird das nur ein Server existiert. Wenn sie Localhost hat dann kann nur auf einen Rechner mit verschiedenen Clients gespielt werden. Wenn die IP-Adresse geändert wird muss der Client neu kompiliert werden.
Wartung:
Sollte ein Fehler auftreten, bitte eine Mail mit Beschreibung an mich senden:
h0025447@wu-wien.ac.at
Bibliographie:
http://java.sun.com/j2se/1.4.2/docs/api/
Thinking in
Java – Bruce Eckel
Sprechen sie Java – Hanspeter Mössenböck
Spielbeschreibung:
Das Spielfeld von Tic Tac Toe besteht aus 9 Feldern, die in drei Reihen und Spalten angeordnet sind. Ziel des Spieles ist es 3 gleiche entweder waagrecht, senkrecht oder diagonal nebeneinander zu setzen.
Am Anfang sind alle Felder frei. Zwei Spieler ziehen abwechselnd. Ein Zug eines Spieler besteht aus dem Besetzen eines beliebigen bis dahin freien Felds. Wenn ein Spieler 3 Felder waagrecht, senkrecht oder diagonal besetzen kann, hat er sofort gewonnen. Sind alle Felder belegt und kein Spieler konnte 3 Felder waagrecht, senkrecht oder diagonal besetzen so endet das Spiel unentschieden.
Hilfe:
Es gibt 2 Hilfetexte, wird über Telnet gespielt so gibt es eine Hilfe übers Telnet vom Server:
Hilfe zum Tic Tac Toe Spiel:
Um das Spiel zu starten, musst du 'start' eingeben.
Danach gibst du deinen Namen ein, suchst einen Spielpartner aus und das Spiel kann beginnen.
Das Tic Tac Toe Spielfeld ist von links oben nach rechts unten durchnummeriert.
Das Spielfeld sieht so aus:
0 | 1 | 2
3 | 4 | 5
6 | 7 | 8
Damit du setzen kannst, tippe einfach die Zahl ein (0-8) wo du deinen Zug machen willst. Nach jedem Zug erscheint das Spielfeld neu und du siehst "die gesetzten Züge.
Wenn das Spiel beendet ist, kannst du mit deinem Partner noch eine Runde spielen. Bei jeder neuen Runde (mit dem selben Partner) beginnt ein anderer.
Falls du Partner wechseln möchtest, ist das nach dem Spiel möglich.
Wenn du das Spiel beenden möchtest gibt 'bye' ein.
Das Gui gibt eine etwas andere Hilfe aus, weil die Benutzeroberfläche Buttons, ... benützt:
Hilfe zum Tic Tac Toe Spiel:
Um das Spiel zu starten, musst du zuerst den Button 'Start' drücken.
Danach gibst du deinen Namen ein, suchst einen Spielpartner aus und das Spiel kann beginnen.
Um einen Zug zu setzen, drücke wenn du dran bist auf ein Feld.
Im Ausgabefeld werden Meldungen angezeigt, die dir sagen was du als nächstes tun sollst.
Werden im Ausgabefeld Fragen gestellt dann gib die Antworten in das Eingabefeld ein.
Wenn eine Frage kommt, die mit Ja oder Nein zu beantworten ist, so drücke auf den JA oder NEIN Button.
Wenn das Spiel beendet ist, kannst du mit deinem Partner noch eine Runde spielen. Bei jeder neuen Runde (mit dem selben Partner) beginnt ein anderer.
Falls du Partner wechseln möchtest, ist das nach dem Spiel möglich.
Wenn du das Spiel beenden möchtest drücke den Button 'Bye'. Das kannst du auch mitten im Spiel tun. In diesem Fall bekommt dein Partner dann die Verständigung dass du aufgehört hast mit ihm zu spielen.
Grafische
Oberfläche:

Installation:
Der TTTClient muss am PC gespeichert sein. Er lässt sich mittels einer Konsole mit dem Befehl: „java TTTClient“ starten.
Wenn die IP-Adresse des Servers geändert wird, muss der Client neu kompiliert werden mittels „javac TTTClient.java“, danach wieder mit „java TTTClient“ starten.