Java Projekt: Spieleserver TicTacToe

Rechnerpraktikum aus Programmierung

2004-01-13

 

 

 

 

Susanne Hafner – 0152203

susanne.hafner@wu-wien.ac.at

 

Rechnerpraktikum aus Programmierung im WS 2003/04

unter der Leitung von Dr. Michael Hahsler

 Code

Problemdefinition

Das Projekt in der Lehrveranstaltung Rechnerpraktikum aus Programmierung hat den Umfang von etwa der halben Lehrveranstaltung und umfasst Analyse, Design, Implementierung und Dokumentation eines Softwaresystems. Ziel dieses Projekts ist es, einen Spieleserver für ein einfaches Spiel mit zwei Mitspielern zu implementieren. Ich habe mich für TicTacToe entschieden. Um das Projekt trotz geringer Programmiererfahrung in dieser kurzen Zeit umsetzen zu können, habe ich auf eine grafische Benutzeroberfläche vorerst verzichtet und den Schwerpunkt des Projekts auf die Lauffähigkeit des Programms in einem Netzwerk gerichtet. Es soll möglich sein einen Mitspieler zu einem Spiel einzuladen und mit diesem eine beliebige Anzahl an Runden zu spielen. Das Spiel läuft nach den üblichen Regeln eines TicTacToe – Spiels ab: Ein Spieler setzt X, der andere O, die Spieler sind abwechselnd an der Reihe. Gewonnen hat, wer als erster drei seiner Symbole waagrecht, senkrecht oder diagonal in einer Reihe hat. Das Spiel endet unentschieden, wenn alle Felder besetzt sind, aber kein Spieler drei Symbole in einer Reihe hat. Um eine Art Spielestatistik zu implementieren, soll für jeden Spieler die Anzahl der gewonnenen, verlorenen und unentschiedenen Spiele gespeichert werden, so lange dieser online ist. 

 

Analyse

Dieses Use-Case-Diagramm beschreibt die wichtigsten Anwendungsfälle die während einer Verbindung mit dem Spieleserver auftreten können. Dabei beinhaltet der Use-Case „einloggen“ die Verbindung mit dem Server und die Eingabe des eigenen Spielernamens. Ist dies geschehen und hat der Spieler einen gültigen Spielernamen eingegeben, kann er einen Mitspieler zu einem gemeinsamen Spiel einladen. Ansonsten bleibt die Verbindung zum Server aufrecht, der Spieler muss jedoch einen anderen – gültigen – Namen eingeben.

Mitspieler einladen setzt neben einem gültigen Login auch eine gültige Eingabe für einen Mitspieler voraus: Eine Einladung ist nur möglich, wenn der gewünschte Spielpartner am Server eingeloggt ist und zur Zeit nicht mit einem anderen Spieler spielt. Es soll weiters verhindert werden, dass der Spieler sich selbst einlädt.

Nach erfolgreicher Einladung startet ein neues Spiel. Der Spieler kann einen Stein setzen. Dazu muss er jedoch an der Reihe sein (ist er dies nicht, soll eine Meldung erscheinen, dass der Spieler warten muss) und in ein mögliches Feld setzen: Möglich sind alle freien Felder am Spielfeld, welches von eins bis neun durchnummeriert ist. Im Fall einer ungültigen Eingabe soll eine Fehlermeldung erscheinen.

 

 

 

Design

UML-Klassendiagramm

Folgende grundlegende Struktur wurde für die Problemlösung entworfen.

 

Der Server horcht an einem Port und wartet bis ein Benutzer eine Verbindung herstellen will. Der Server erzeugt für jeden Benutzer einen Thread.  Der Thread wiederum erzeugt für seinen Benutzer ein Protokoll, über das alle Eingaben verarbeitet werden können. Weiters soll das Protokoll eine Spielerinstanz erzeugen. Für ein Spiel werden zwei Spieler benötigt.

 

Klassen im Detail

Im Folgenden werden die einzelnen Klassen im Detail dargestellt:

 

 

Importiert werden:

import java.net.*;

import java.io.*;

import java.util.*;

 

 

Variablen

 

ServerSocket serverSocket

Die Variable serverSocket speichert ein Socket und hat Initialwert null.

 

boolean listening = true

listening ermöglicht das Öffnen eines neuen Threads, wenn Verbindung zum Server hergestellt wird

 

LinkedList liste = new LinkedList()

Die Variable liste wird als leere Liste initialisiert.

In der Liste wird jeder erzeugte Thread gespeichert. So ist es später einfach möglich Namen oder auch andere Daten der Online-Spieler zu suchen, da über den gespeicherten Thread auf all diese Daten zugegriffen werden kann.

 

 

Methoden

 

public void run ()

Horcht am Port 2708. Kann eine Verbindung aufgebaut werden, erzeugt diese Methode den Thread, fügt ihn der Liste hinzu und startet ihn.

 

public void addThread (TTTThread t)

Diese Methode fügt einen Thread der LinkedList liste hinzu. Dies ist notwendig, damit Online-Spielerlisten erzeugt werden können.

 

public void removeThread (TTTThread t)

Diese Methode löscht einen Thread aus liste

 

public String zeigeSpieler (Spieler spieler)

Diese Methode sucht in der Threadliste nach inaktiven Spielern und gibt diese als String zurück bzw. eine Meldung, dass keine inaktiven Spieler verfügbar sind.

 

public String zeigeStatistik ()

Sucht aus liste alle Spielerinstanzen der jeweiligen Threads und gibt diese zurück.

 

public boolean pruefeName (Spieler spieler, String spielername)

Auch diese Methode arbeitet mit der Threadliste. Sie prüft ob spielername ein existierender Spielername ist und nicht mit dem Namen von spieler übereinstimmt.

 

public boolean pruefeLogin (String spielername)

Diese Methode durchsucht die Threadliste nach spielername. Sie liefert true, wenn noch kein entsprechender Spielername existiert.

 

public TTTProtocol getProtocolGegner (String spielername)

Diese Methode liefert das Protokoll zu spielername aus der Liste.

 

 

 

 

Importiert wird :

import java.io.*;

 

 

Methode

 

public static void main (String[ ] args)

Das ist die Mainmethode um den Server zu starten. Sie erzeugt eine Instanz von TTTServer und startet von diesem die Methode run().

 

 

 

Die Klasse TTThread ist abgeleitet von  Thread.

 

 

Importiert werden:

import java.net.*;

import java.io.*;

import java.util.*;

 

 

Variablen

 

private Socket socket

Wert wird von Server mitgeliefert.

private TTTProtocol protokoll

Speichert das Protokoll, das von TTTThread erzeugt wird.

private TTTServer server

Speichert den Server, durch den TTTThread erzeugt wird.

PrintWriter out

BufferedReader in

Speichern Buffer zur Input/Output-Verarbeitung.

 

 

Methoden

 

public TTTThread (Socket socket, TTTServer server)

Der Konstruktor weist den Variablen socket und server die mitgelieferten Werte zu und erzeugt eine neues Protokoll, das unter der Variable protokoll abgespeichert wird.

 

public void run()

Die Run-Methode von Thread wird überschrieben. Es werden die Buffer erzeugt und Eingaben verarbeitet. Weiters werden hier wieder Sockets geschlossen, der Thread aus Threadliste entfernt und dann geschlossen.

 

public void ausgeben (String s)

Ermöglicht die Ausgabe von einem anderen Protokoll aus.

 

public Spieler getSpieler ()

public int getOnlinestatus ()

public String getSpielername ()

public TTTProtocol getSpielerprotokoll ()

public TTTProtocol getProtocol2 ()

get-Methoden liefern jeweilige Variablen und sind unter anderem für die LinkedList am Server wichtig.

 

 

 

Variablen

 

private TicTacToe spiel

speichert das Spiel, wenn Spieler eines startet.

 

private Ratespiel allein

speichert Spiel zum Zeitvertreib.

 

private Spieler spieler

speichert Spielerinstanz.

 

private Spieler spieler2

Speichert Spielpartner, wenn gespielt bzw. eingeladen wird.

 

private TTTProtocol protocol2

Speichert das Protokoll des Spielpartners.

 

private TTTServer server

Speichert den Server.

 

private TTTThread thread

Speichert den Thread

 

 

Methoden

 

public TTTProtocol (TTTServer server, TTTThread thread)

Konstruktor. Server und Thread werden gespeichert. Neue Spielerinstanz wird erzeugt und als spieler gespeichert.

 

public String processInput (String inputLine)

Hier werden alle Eingaben des Benutzers verarbeitet (Name eingeben, Spieler einladen, Stein setzen, etc.)

 

public String welcome ()

Gibt Begrüßung zurück.

 

public String help ()

Es werden alle möglichen Eingaben und der Spielablauf erklärt.

 

public String rateZahlen ()

Gibt den Begrüßungstext des Ratespiels zurück.

 

public Spieler getSpieler ()

public int getOnlinestatus ()

public String getSpielername ()

public TTTProtocol getSpielerprotokoll ()

public TTTProtocol getProtocol2 ()

Get-Methoden für Spieler und Spielpartner.

 

public void setSpiel (TicTacToe spiel2)

public void setProtocol2 (TTTProtocol pr)

public void setSpieler2 (Spieler sp)

public void setOnlinestatus (int i)

Set-Methoden für Spielpartner (notwendig wenn eingeladen wird bzw. Spiel gestartet wird).

 

public void ausgeben (String x)

Ermöglicht die Ausgabe von x an den Mitspieler-Thread.

 

 

 

Variablen

 

private String spielername

Speichert den Spielernamen.

 

private TTTProtocol spielerprotokoll

Speichert das Protokoll des Spielers.

 

private int onlinestatus   

Speichert den Onlinestatus des Spielers:

0          online, noch kein Name eingegeben

1          Name bereits eingegeben, inaktiv

10       Spieler hat eingeladen (muss noch auf Antwort warten)

11       Spieler wurde eingeladen (muss noch antworten)

12       Spieler spielt Ratespiel

2          Spieler spielt TicTacToe

 

private int gewonnen                                                     

private int verloren                                                          

private int unentschieden          

Speichert die Anzahl der gewonnenen, verlorenen und unentschiedenen Spiele.                                       

private int xo

Speichert den Wert für X oder O oder hat Wert 0, wenn kein Spiel läuft.

 

 

Methoden

 

public String toString ()               
ToString-Methode von Spieler. Gibt Spielername und gewonnen/ unentschieden/ verloren zurück
 
public String getSpielername ()
public TTTProtocol getSpielerprotokoll ()

public int getOnlinestatus ()

public int getXo ()
Get-Methoden
 
public void setSpielername (String newSpieler)
public void setSpielerprotokoll (TTTProtocol newOne)
public void setOnlinestatus (int i)
public void setXo (int i)
Set-Methoden
 
public void zaehleGewonnen ()
public void zaehleVerloren ()
public void zaehleUnentschieden ()
Zählt zur entsprechenden Variable eins dazu.

 

  

 

Variablen

 

Spieler spielerX

Spieler spielerO

Speichert die Spielerinstanzen

 

int amZug

Wird mit xo des jeweiligen Spielers belegt, der am Zug ist

 

int gespielteZuege

Speichert die Anzahl der gespielten Züge.

 

public int[ ] spielfeld        

Stellt das TicTacToe – Spielfeld als Integerarray dar.

 

final int FREE = 0;            

final int X = 1                      

final int O = 5

Konstanten, die denen einzelnen spielfeld-Elemente zugewiesen werden können. Durch die Werte eins bzw. fünf für X und O können Gewinnsituationen leicht berechnet werden.

 

public int status

Speichert eine der weiter unten genannten Konstanten, um darzustellen, ob Spiel zu Ende/unentschieden ist oder noch weiter geht.

 

final int WEITER = 0

final int FERTIG = 1

final int UNENTSCHIEDEN = -1

Konstanten, die status zugewiesen werden können.

 

 

Methoden

 

TicTacToe()

Konstruktor.

 

public int getAmZug()

Gibt  xo des Spielers zurück, der gerade am Zug ist.

 

public int getGegner(int amZug)

Gibt xo des Spielers zurück, der gerade nicht am Zug ist.

 

public int gibtsGewinner()

Sucht nach drei gleichen Symbolen waagrecht, senkrecht oder diagonal und gibt – wenn vorhanden – Gewinner durch X oder O zurück, UNENTSCHIEDEN, wenn alle Felder besetzt sind oder WEITER, wenn ein nächster Zug möglich ist.

 

public String setzeStein(int i)

Überprüft ob ein Setzen auf spielfeld [i] möglich ist, führt den Zug – wenn möglich durch – wechselt, wenn es noch keinen Gewinner gibt, den Spieler, der einen Zug machen darf (amZug wechselt von X auf O oder umgekehrt) und gibt einen String über das Ergebnis zurück.

 

public String printSpielfeld()

Gibt das Spielfeld als 3x3-Feld aus. Unbesetzte Felder zeigen einen Bindestrich, besetzte Felder zeigen X oder O.

 

 

           

Diese Klasse war im ursprünglichen Design nicht vorgesehen. Sie wurde jedoch implementiert, um die Wartezeit auf einen Mitspieler zu verkürzen. Es wird dadurch ein Spiel für einen Spieler implementiert. Der Spieler muss versuchen eine Zahl zwischen null und 100 mit möglichst wenigen Versuchen zu erraten.

 

 

Variablen

 

int zahl

Speichert die Zufallszahl, die erraten werden muss.

 

int anzahlZuege

Speichert die Anzahl der abgegebenen Tipps.

 

Spieler spieler

Speichert die Spielerinstanz, die dieses Spiel startet.

 

TTTProtocol protocol

Speichert das Protokoll der Spielerinstanz

 

 

Methoden

 

Ratespiel(Spieler spieler)

Konstruktor. Spieler wird der Variable spieler zugewiesen, anzahlZuege wird auf 0 gesetzt, Zufallszahl wird erzeugt.

 

public String raten(int i)

i wird mit zahl verglichen. Es wird ein String mit einem Lösungshinweis zurückgegeben oder einer Gratulation, wenn zahl erraten wurde.

 

 

Das Protokoll

 

 

 

 

Sequenzdiagramm

Dieses Sequenzdiagramm zeigt die Vorgänge, die beim Einloggen am Server passieren.

 

 

Hier werden die Abläufe während eines Spiels dargestellt

 

 

 

Implementierung

Die Implementierung erfolgte mit Hilfe des Editors JOE (http://www.javaeditor.de/) sowie des JDK 1.4.1 auf Rechnern mit den Betriebssystemen Windows 2000 bzw. Windows XP.  Getestet wurde das Programm auf mehreren Rechnern (Windows 98, Windows ME, Windows 2000, Windows XP) isoliert sowie über ein Netzwerk.

 

Dokumentation für den Anwender

Der Anwender muss sich über telnet (entweder über die Eingabeaufforderung oder einen telnet-Client) am Server einloggen. Dazu sind IP und Host des Servers anzugeben. Da der Server von einem Benutzer in der derzeitigen Form selbst gestartet werden muss, kann eine allgemeingültige IP an dieser Stelle nicht angegeben werden. Läuft der Server am eigenen Rechner, kann localhost als IP eingegeben werden. Als Port ist 2708 anzugeben.

Hat die Verbindung mit dem Host geklappt, so muss als erstes der Spielername mit dem Kommando „#name“ plus dem gewünschten Spielernamen eingegeben werden.

Mögliche Kommandos sind mit „#help“ abrufbar. Hier eine Zusammenfassung:

#help              Anzeige der Hilfe

#ww                Anzeige aller Spieler, die eingeladen werden können

#who              Anzeige aller Online-Spieler in der Form

                        Spieler           (gewonnen/unentschieden/verloren)

#allein            startet das Ratespiel zum Zeitvertreib (der Spieler kann während eines

Spiels zu einem TicTacToe eingeladen werden!)

#ask +name  Einladung des Spielers mit dem Namen name zu einem TicTacToe

#show            Abruf des aktuellen Spielfelds während einer TicTacToe-Runde

#reset             Abbruch eines Spiels bzw. Zurücksetzen des Status

#quit               vom Server ausloggen

 

TicTacToe wird auf einem 3x3-Spielfeld gespielt. Ein Spieler setzt X, der andere O, die Spieler sind abwechselnd an der Reihe. Das TicTacToe-Spielfeld ist von links oben nach rechts unten durchnummeriert (siehe unten). Um auf ein Feld zu setzen, muss # gefolgt vom gewünschten Feld eingegeben werden. Gewonnen hat, wer als erster drei seiner Symbole waagrecht, senkrecht oder diagonal in einer Reihe hat. Das Spiel endet unentschieden, wenn alle Felder besetzt sind, aber kein Spieler drei Symbole in einer Reihe hat.  

 

1  2  3

4  5  6

7  8  9