Sie sind auf Seite 1von 20

ProPra SS95 Endbericht

Leiter: Manfred Poepping, poepping@uni-paderborn.de

Mitglieder der Gruppe Manfred 2

Ilaender, Stephan, corona@uni-paderborn.de


Scheel, Olaf, olasch@uni-paderborn.de
Christoffers, Gerrit, eisensak@uni-paderborn.de
Al-Tikriti, Maysoun, mayada@uni-paderborn.de
Swoboda, Stephan, wasa@uni-paderborn.de
Dostal, Marc, dostal@uni-paderborn.de
Lochstampfer, Arthur, arthur@uni-paderborn.de
Visarius, Markus, visi@uni-paderborn.de
Frewert, Carsten, frewi@uni-paderborn.de
Eisbein, Thomas, eisbein@uni-paderborn.de
Pokolm, Marco, pokolm@uni-paderborn.de
Thurau, Carsten, ctproud@uni-paderborn.de
Strebin, Christoph, strebin@uni-paderborn.de
Borowski, Christoph, borowski@uni-paderborn.de
Kaiser, Thomas, tk@uni-paderborn.de

Tue Jan 30 13:50:07 MET 1996

Contents
• Einführung

• Einsatzbereich

• Anwendungsbereich und Zielgruppe

• Soft- und Hardwareanforderungen

• Geschwindigkeit

• Funktionalität

• Legende

• Start des Programms

• Neues Spiel

• Spielbeginn

• Nochmal Spielen

• Hilfe

• Spielende

• Spielstrategien - Spielregeln

• Spielstrategien

• Spielregeln

• Benutzeroberfläche
• Elemente der Programmoberfläche

• Spielfeld

• Spielsteine

• Informationsfenster

• Bedienelemente

• Menüleiste

• Buttons

• Kommunikation

• Spielmodi

• Beginner einer Partie

• Realisierung

• Kurze Beschreibung der Strategien

• Callbacks

• Anmerkung

• Start des Programms

• Fenster--Ereignisse

• Fenster--Verdeckung

• Fenster--schließen

• Fenster--zerstören

• Maus--Ereignisse

• Mausbewegung

• Mausklick

• Button--Ereignisse

• Button: Neues Spiel

• Button: Nochmal Spielen

• Button: Zug zurück

• Menü--Ereignisse

• Menü: Nochmal spielen, Neues Spiel, Zug zurücknehmen

• Menü: Spielende

• Menü: Tip

• Menü: Hilfe

• Die Klasse dD4G


• Atribute

• Methoden

• Die Klasse Spieler

• Methoden

• Die Klasse Mensch

• Die Klassen Computer1, Computer2, Computer3

• Methoden

• Klasse Kommunikation

• Attribute

• Methoden

• Die Klasse Brett

• Attribute

• Datenstruktur

• letzterZug

• letzteZugNummer

• Anz_Steine

• Methoden

• Die Klasse Spielbrett

• Attribute

• private:

• Methoden

• private:

• public:

• Kommentare:

• About this document ...

Einführung
Die Aufgabe des Programmierpraktikums SS '95 bestand darin, ein Spiel zu programmieren, daß man allgemein
unter dem Namen dreidimensionales 4-Gewinnt kennt.

So machten sich also 16 Studenten unter der Leitung von Manfred Poepping an die Arbeit diese Aufgabe zu lösen.
Probleme enstanden, wenn es um die Koordination der Arbeitsabläufe ging und den Kommunikationsteil des
Programmes zum Laufen zu bringen.

Nach mehreren Monaten harter Arbeit enstand nun diese lauffähige Version von 3D - 4 Gewinnt.

Mehr ist nicht mehr über dieses Spiel zu sagen, außer sich davor zu setzen und es auszuprobieren.
Einsatzbereich
Das dreidimensionale Strategiespiel Vier--Gewinnt ermöglicht es dem Spieler zum einem gegen den Computer zu
spielen, zum anderen besteht die Möglichkeit, gegen ein anderes Vier--Gewinnt--Programm, sofern es die unter
''Kommunikation'' Kapitel kommunikation beschriebenen Voraussetzungen erfüllt, zu spielen.

Anwendungsbereich und Zielgruppe


Das Programm dient dem Vergnügen. Es wendet sich an Anfänger, die dieses Spiel noch nicht kennen, aber auch an
Fortgeschrittene, die ihre Spielkenntnisse vertiefen wollen.

Soft- und Hardwareanforderungen


Benötigt wird ein Unix--Rechner mit der Benutzeroberfläche X--Windows und OSF--Motif.

Geschwindigkeit
Das Programm reagiert im allgemeinen in vernachlässigbar kurzer Zeit, außer beim Menüpunkt ,,Hilfe``, da dann
der html-Viewer Mosaic gestartet wird.

Funktionalität
Legende
Start des Programms
Neues Spiel
Wird der Menüpunkt ''Neues Spiel'' ausgewählt, so geschieht folgendes:

• Läuft bereits ein Spiel, so erscheint eine Abfrage, die sicherstellt, daß das aktuell laufende Spiel nicht
versehentlich abgebrochen wird.

• Soll kein neues Spiel gestartet werden, so erfolgt der Rücksprung an die Stelle, von der der Aufruf ''Neues
Spiel'' kam. Im anderen Fall muß nun der Spielmodus und die Spielstärke gewählt sowie die Namen der
Spieler eingegeben werden.

• Folgende Spielmodi sind verfügbar: siehe auch Kapitel kommunikation

• Mensch---Mensch

• Mensch---Programm

• Programm---Programm

• Es gibt 3 Spielstärken: leicht, mittel und schwer

• Falls ein neues Spiel beginnt, wird der, der anfängt, per Zufall bestimmt.

• Daraufhin werden die Farben der Spieler entsprechend gesetzt und es folgt der Beginn des Spieles.

• Die Funktionen ''Hilfe'' und ''Programmende'' sind hier immer verfügbar.


Spielbeginn
Wird ein neues Spiel begonnen, so gilt:

• Fängt der Gegner an, dann erfolgt ein Gegenzug. Falls dies nicht der Fall ist, so kann der Spieler nun einen
Zug machen.

• Nach jedem Zug erfolgt ein Gegenzug und umgekehrt, sofern nicht die UNDO--Funktion gewählt wurde
und das Spiel noch nicht gewonnen ist. Nach jedem Zug wird geprüft, ob das Spiel von einer Partei
gewonnen ist bzw. ein Remis besteht.

• Ist bereits ein eigener Zug erfolgt, so läßt sich mit der UNDO--Funktion genau dieser Spielzug
zurücknehmen. Ist bereits der Zug des Gegners erfolgt, so wird auch dieser zurückgenommen. Bei der
Spielvariante ''Programm gegen Programm'' ist diese Funktion nicht ausführbar.

• Die Funktionen ''Hilfe'', ''Programmende'' und ''Neues Spiel'' sind jederzeit möglich.

Nochmal Spielen
Bei ''Nochmal Spielen'' gilt folgendes:

• Die Abfrage ''Wirklich'' erscheint nur bei bereits laufendem Spiel.

• Soll kein neues Spiel gestartet werden, so erfolgt der Rücksprung an die Stelle, von der der Aufruf
''Nochmal Spielen'' kam. Andernfalls werden die Optionen des letzten Spieles übernommen.

• Optionen des letzten Spieles bedeuten hier die Übernahme von Spielmodus, -stärke und Spielernamen; sie
können hier nicht mehr verändert werden.

• Bei ''Wer beginnt'' wird der Beginner der neuen Partie festgelegt. Es fängt derjenige an, der das vorherige
Spiel verloren hat.

• Die Funktionen ''Hilfe'', ''Neues Spiel'', ''Nochmal Spielen'' und ''Programmende'' sind jederzeit verfügbar.

Hilfe
• Der Aufruf der Hilfe ist jederzeit möglich.

• Die ''Spielregeln'' von 3D--Vier--Gewinnt werden erläutert.

• Die Funktion ''Tip'' gibt dem Spieler einen möglichen Spielzug an. Sie kann nur bei laufendem Spiel und
bei eigenem Zug aufgerufen werden.

• Unter ''Bedienung'' wird die Programmbedienung des Spieles erklärt.

• Bei ''Info'' wird eine Information über das Programm und seine Autoren ausgegeben.

Spielende
• Es wird geprüft, ob einer der Spieler gewonnen hat, oder ob das Spiel mit Remis endet. (siehe auch Kapitel
spiel gewonnen)

Spielstrategien - Spielregeln
Spielstrategien
Es existieren 3 Spielstrategien mit unterschiedlicher Spielstärke, von denen die erste Spielstrategie (Random
Placement) die schwächste Spielstärke ist, und die zweite Spielstrategie stärker ist als Randomplacement. Die dritte
Spielstrategie ist die stärkste Strategie von allen.

Spielregeln
Das Spiel beginnt mit einem leeren Spielfeld, das man sich als einen Würfel vorstellen kann, welcher 4 Felder breit,
4 Felder hoch und 4 Felder tief ist. Abwechselnd wird jeweils von einem Spieler jeweils eine Kugel seiner Farbe auf
ein Feld gelegt. Dabei wird die Kugel von oben auf den Würfel gelegt und fällt anschließend auf die oberste
darunterliegende Kugel, so daß die Schwerkraft berücksichtigt wird und daher keine Kugel frei schweben kann.
Demjenigen, dem es als erstes gelingt, eine gerade Reihe von 4 eigenen Kugeln zu bilden hat gewonnen, und das
Spiel wird damit beendet. Eine gültige 4er Reihe ist horizontal, vertikal und diagonal möglich. Wenn alle Felder
belegt sind, ohne daß eine 4er Reihe gebildet wurde, so endet das Spiel unentschieden.

Die Ebene 4 soll die höchste Ebene sein.

• Horizontal lassen sich pro Ebene 4 Viererreihen (= 16 Möglichkeiten) bilden (Eine Möglichkeit zeigt
Beispiel A)

• Vertikal lassen sich pro Ebene 4 Viererreihen (= 16 Möglichkeiten) bilden (illustriert Beispiel B)

• Diagonal lassen sich pro Ebene 2 Viererreihen (= 8 Möglichkeiten) bilden (die zwei Möglichkeiten zeigen
Beispiel C und D)

Die Ebene 4 soll die höchste Ebene sein.


Es ist auch möglich, die Viererreihe durch die Ebenen zu bilden, was durch Beispiel E veranschaulicht wird.
Dadurch ergeben sich 16 Möglichkeiten, auf diese Weise eine senkrechte Viererreihe zu bilden.

Die Ebene 4 soll die höchste Ebene sein.


Viererreihen lassen sich außerdem senkrecht diagonal im Raum bilden. Hier sind 8 Möglichkeiten denkbar. Eine
Möglichkeit zeigt Beispiel F.

Die Ebene 4 soll die höchste Ebene sein.


Es lassen sich außerdem Diagonalen auch waagerecht im Raum bilden. Hier existieren auch wiederum, ähnlich
Beispiel F, 8 mögliche Viererreihen. Eine Möglichkeit illustriert Beispiel G.

Die Ebene 4 soll die höchste Ebene sein.


Noch eine weitere Art der Viererreihe zeigt Beispiel H. Es gibt hier 4 mögliche Viererreihen, die gebildet werden
können.

Benutzeroberfläche
Die Benutzeroberfläche ist X--Windows und OSF--Motif unter einem UNIX-Betriebssystem. Die
Benutzungsführung erfolgt in Deutsch; die Bedienung erfolgt mittels Maussteuerung.
Der Aufbau sämtlicher Bildschirmdialoge, d.h. Menüs, Dialogboxen und das Hauptfenster (siehe Skizze unten) ist
konform mit den Motif Style Guides; dies ergibt sich u.a. durch den Einsatz des Interface Builders.
Das Programm arbeitet vollständig in einem eigenen Fenster, das als Icon verkleinert werden kann.

Elemente der Programmoberfläche


Spielfeld
Das Spielfeld wird durch vier übereinanderliegende, 4 x 4 Felder große Gitter in dreidimensionaler Ansicht ohne
Perspektive dargestellt.
Spielsteine
Die Spielsteine werden als Kugeldarstellung realisiert, wobei die Spielsteine zur Unterscheidung der Spieler
verschiedenartig gefärbt sind.

Informationsfenster
Hier werden die folgenden Informationen angezeigt:

• Spielermodus; z.B. Mensch gegen Programm

• Farbe der Spielsteine der Spieler; z.B. Spieler ''Name'' spielt mit den schwarzen Steinen

• Wer beginnt

• Spielstand, d.h. Spieler 1 oder Spieler 2 hat gewonnen oder Remis

Bedienelemente
Bei der Spielvariante ''Mensch---Programm'' werden die Züge des Menschen mit der Maus realisiert, wobei nur das
oberste Gitter des Spielfeldes als Eingabefeld bestimmt ist. Dort ändert sich der Mauscursor, so daß der Spieler
erkennt, wo er eine Kugel setzen darf. Die linke Maustaste bewirkt das Setzen der Kugel auf das Spielfeld. Die
Kugel landet danach in der untersten Ebene, die noch frei ist. Fehlerhafte Eingaben werden nicht akzeptiert.

Menüleiste
Eine Menüleiste am oberen Fensterrand enthält die folgenden Menüpunkte.

• Datei

Spielende:

In einer Dialogbox wird nach einer Sicherheitsabfrage der Art ''Programm wirklich beenden'', das
Programm bei einer positiven Antwort beendet, ansonsten läuft das aktuelle Spiel weiter.

Neues Spiel:

Bei einem laufenden Spiel wird mittels einer Dialogbox nach einer Sicherheitsabfrage der Art ''Eine
neue Spielserie starten'' bei einer positiven Antwort eine neue Spielserie gestartet. Bei einer negativen
Antwort läuft das aktuelle Spiel weiter. Läuft aktuell kein Spiel, so wird eine neue Spielserie gestartet.
Über eine Dialogbox wird der Spielmodus, -stärke und Spielernamen ausgewählt.

Nochmal spielen:

Bei einem laufenden Spiel wird ebenfalls in einer Dialogbox nach einer Sicherheitsabfrage der Art
''Neues Spiel beginnen?'' bei einer positiven Antwort ein neues Spiel gestartet. Bei einer negativen
Antwort läuft das aktuelle Spiel weiter. Läuft aktuell kein Spiel, so wird ein neues gestartet. Dabei
werden die Optionen (wie Spielmodus etc.) des vorherigen Spieles übernommen.

Zug zurücknehmen:

Nur der jeweils zuletzt gesetzte eigene Zug des Spielers wird rückgängig gemacht.

• Hilfe

Bedienung:

Die Bedienung des Programmes wird erklärt.

Spielregeln:
Die Spielregeln von 3D-Vier-Gewinnt werden erläutert. Der Text erscheint in einem seperaten Fenster,
dessen Inhalt mit der Maus gescrollt werden kann.

Info:

In einem Textfenster werden Informationen über das Spiel und die Autoren ausgegeben.

Tip:

Dem Spieler wird optisch durch eine Kugel, die sich farblich von den Spielerkugeln unterscheidet, ein
möglicher nächster Zug angezeigt. Dieser Button kann nur angewählt werden, wenn man am Zug ist
und bereits ein Spiel läuft.

Buttons
Die folgenden Buttons, deren Funktion bereits unter Programmoberfläche zur Verfügung.

• Nochmal spielen

• Neues Spiel

• Zug zurücknehmen

Dabei können -- je nach Spielsituation -- einzelne Buttons nicht angewählt werden. Diese sind in dem Fall von den
wählbaren optisch unterscheidbar dargestellt.

Kommunikation

Spielmodi
Es ergeben sich - neben dem Modus , Mensch gegen Programm` - folgende Möglichkeiten:

a)

Ein Programm spielt automatisch gegen ein anderes Programm

Die Spielstärke des Programmes kann vor Beginn des Spieles ausgewählt werden. (Das Programm kann auch
zweimal gestartet werden und auf diese Weise gegen sich selbst spielen.)

b)

Zwei menschliche Spieler spielen gegeneinander, wobei jeder Spieler an einem 'eigenen` Programm spielt.
In diesem Fall entfällt die Möglichkeit der Spielstärkenwahl.

Beginner einer Partie


Sollen zwei Programme gegeneinander spielen, so beginnt diejenige Seite, bei der die Spielstärkenwahl zuerst
abgeschlossen wird. Genauer: Der Prozeß, in dem zuerst die Pipe `player_1' angelegt worden ist, beginnt. Siehe
Datenstrukturen

Realisierung
Kurze Beschreibung der Strategien
Die erste und in psychologischer Sicht auch wichtigste Strategie ist zweifellos die erste (Spielstärke: leicht).

Gemeinhin auch unter dem Pseudonym 'Random Placement' bekannt bietet es allen uns bisher bekannten Vertretern
der Klasse DAU zumindest die Hoffnung, einmal im Leben gegen einen Computer irgendetwas reißen zu können.
Diese unsere Lieblingsstrategie wählt aus den noch freien Positionen zufällig eine aus und wuchtet ihr Steinchen
hinein, ohne auf die Konsequenzen zu achten.

Alle anderen Strategien arbeiten mit einem Bewertungsystem für die jeweils möglichen Positionen. Die zweite
Strategie prüft zunächst, ob eine 4er-Reihe des Gegners verhindert werden muß oder ob sie selbst eine bilden, sprich
gewinnen kann. Dann wird jede mögliche Position daraufhin geprüft, ob ein Setzen an die jeweilige Stelle eine 3er-
Reihe des Gegners verhindern, bzw eine eigene ermöglichen kann; für beide Fälle sind Punkte festgesetzt, die dann
addiert bzw subtrahiert werden. Gesetz wird schließlich an die Stelle, die die meisten Punkte im Bewertungsystem
hat sammeln können, nachdem noch getestet wurde, ob ein Setzen an der Stelle vielleicht dem Gegner eine
Gewinnreihe in der Eben darüber möglich macht.

Die vierte Strategie arbeitet nach genau dem gleichen Prinzip, nur daß hier schon auf 2er-Reihen (eigene und die des
Gegners) getestet wird und geprüft wird, ob der Gegner vielleicht eine von diesen niedlich-tödlichen Doppelmühlen
im Sinn hat. Weiterhin besitzt diese Strategie eine Prioritätenliste für die einzelnen Positionen; so ist zB ein
Eckpunkt in der untersten Ebene strategisch günstiger als in Ebene 2 oder 3, in denen die mittleren Punkte die
besseren Chancen bieten. Diese Bewertung ist allerdings im Verhältnis zur obigen Zugbewertung klein gewählt, so
daß sie nur zum Tragen kommt, wenn zwei mögliche Züge die gleiche Punktzahl erreicht haben. Die fünfte Strategie
arbeitet nach genau demselben Prinzip, allerdings mit leicht veränderten Werten.

Die dritte Strategie bewertet nun die möglichen Positionen nicht nach einem Punktesystem, sondern danach,
wieviele Möglichkeiten diese Position noch für Gewinnreihen bildet, d.h. wieviel freie oder nur von eigenen Steinen
besetzte Reihen von diesem Punkt ausgehen. Vorher wird, wie bei den obigen Varianten, noch auf 4er-Reihen und
Doppelmühlen geprüft.

Callbacks
Anmerkung
Im Gegensatz zu anderen Teilen des Programms, wurden die Callbacks genau nach dem vorher erstellten Dokument
(und nicht umgekehrt) implementiert. Dadurch halten sich die Änderungen in Grenzen. Auf Änderungen wird im
folgenden explizit hingewiesen.

Start des Programms


Beim Start des Programms wird durch den von Motifation erzeugten Quellcode das Hauptfenster von 3D--Vier--
Gewinnt initialisiert und dargestellt. Nachdem das Fenster dargestellt wurde, wird dem Hauptprogramm über einen
Event signalisiert, daß mit spielspezifischen graphischen Darstellungen etc. begonnen werden kann. Dies wird über
den Callback XmPostInitialize realisiert, der global die Klasse 3D4G initialisiert. Diese erzeugt Brett und Spielbrett
und stellt mittels Spielbrett.Neuzeichnen() und Spielbrett.gebe_info_aus() das Spielraster und Infofenster dar. Bei
der Initialisierung von 3D4G wird ein globales Flag gesetzt, welches speichert, daß der Neues Spiel--Button noch
nicht gedrückt wurde. Solange dieses Flag gesetzt ist werden Mausbewegungen und Mausklicks auf der Stein--
setzen--Fläche von den entsprechenden Callbacks ignoriert. Dieses Flag wird beim Erzeugen der Spieler (also dem
tatsächlichen Spielbeginn) gekippt.

Ergänzung: Alle global verwalteten Daten werden in globalen structs vom Typ CallbackInfo bzw.
SpielModusInfo gespeichert.

Zu diesem Zeitpunkt ist das Spiel komplett initialisiert und bereit fuer weitere Benutzereingaben, allerdings werden
die Buttons Nochmal Spielen und Zug zurück für Eingaben gesperrt.

Ergänzung: Dies wird ebenfalls über interne Flags abgefangen.

Fenster--Ereignisse
Fenster--Verdeckung
Nach vollständiger oder teilweiser Verdeckung des Spielfensters wird durch einen entsprechenden Refresh--
Callback die Programmoberfläche neu gezeichnet. Dies wird über die Funktionen Spielbrett.Neuzeichnen() und
Spielbrett.gebe_info_aus() realisiert.

Fenster--schließen
Wird das Fenster über den WindowClose--Button geschlossen, wird der gleiche Callback wie bei dem Menüpunkt
Spiel beenden ausgelöst (d.h. es erscheint ein Requester, welcher erfragt, ob wirklich abgebrochen werden soll. Die
weitere Vorgehensweise kann beim Callback für Spielende nachgesehen werden.

Änderung: Die Spiel verlassen? Sicherheitsabfrage fiel einer GUI Überarbeitung zum Opfer...

Wird im Kommunikationsmodus gespielt, muß dem gegnerischen Programm über Kommunikation.Abbruch()


mitgeteilt werden, daß das Spiel beendet wurde.

Fenster--zerstören
Wird das Programm über destroy verlassen, werden vom Callback Destroy() die entsprechenden Destruktoren der
bereits erzeugten Klassen aufgerufen. Danach wird das Programm (ohne Requester--Abfrage) verlassen.
Wird im Kommunikationsmodus gespielt, muß dem gegnerischen Programm über Kommunikation.Abbruch()
mitgeteilt werden, daß das Spiel beendet wurde.

Maus--Ereignisse
Mausbewegung
Bei diesem Ereignis wird ein Callback namens Mausbewegt() ausgelöst.

Änderung: Alle Funktionen des Callbacks Mausbewegt sind in die Klasse 3D4G gewandert. Die
Mausbewegung wird dieser übergeben und an die Klasse Spielbrett weitergeleitet.

Dieser ermittelt und übergibt die Mauskoordinaten an die Funktion Spielbrett.Feldpositionberechnen(), welche die
zurückgibt (bzw. falls der Mauszeiger sich nicht über dem Zug-Eingabefeld befindet). Wurde eine gültige
Koordinate zurückgegeben, wird dann Brett.Zuglegal() aufgerufen. Ist der Zug legal, wird der Klasse Spielbrett über
die Methode Spielbrett.Markieren( a,b ) mitgeteilt, über welchem Eingabefeld sich der Mauszeiger befindet. Von
der Klasse Spielbrett wird die Farbe des entsprechenden Eingabefeldes geändert und gespeichert, welches Feld
verändert wurde. Zuvor wird diese Variable ausgelesen um ein evtl. vorher markiertes Feld wieder zu de--
markieren. Sollte nach dem Start des Programms noch kein Spielmodus gewählt sein, so werden die
Mausbewegungs--Ereignisse nicht bearbeitet, da das Setzen eines Spielsteines noch nicht möglich ist. Gleiches gilt
falls die Kommunikation das Spiel übernommen hat.

Mausklick
Bei diesem Event wird der Callback Kugelsetzen() mit den aktuellen Mauskoordinaten aufgerufen.

Änderung: Alle Funktionen dieses Callbacks sind in die Klasse 3D4G gewechselt. Bei einem Mausklick
wird nur noch die Methode Zug von 3D4G (mit den aktuellen Mauskoordinaten) aufgerufen.

Dieser testet zunächst, ob ein Mausklick im momentanen Spielmodus überhaupt beachtet werden soll. Im
Spielmodus Rechner--Rechner oder vor Auswahl eines Spielmodus sind Mausklicks nicht zugelassen.
Ist ein Mausklick erlaubt, ruft er (wie bereits oben beschrieben) die Funktionen Spielbrett.Feldpositionberechnen()
und Brett.Zuglegal() auf, um zu ermitteln, ob es sich um einen gültigen Zug handelt. Ist es ein gültiger Zug, wird die
gültige Position über 3D4G der Klasse des menschlichen Spielers über die Methode Zug mitgeteilt. Innerhalb dieser
wird dann Brett.Zugsetzen() aufgerufen. Durch Brett.Zugsetzen() wird auch der momentane Spielstatus ermittelt
(gewonnen/remis/gültiger Zug). Dieser Rückgabewert wird von der Methode Zug als Rückgabewert an die
aufrufende Callback--Funktion zurückgegeben. Mittels Spielbrett.Neuzeichnen() wird dann die Bildschirmausgabe
aktualisiert.
Wechselt der Spielstatus in den Zustand ,,gewonnen``, wird die Funktion Spielbrett.Spielbeendet( Spieler, Grund )
aufgerufen, welche einen Requester erzeugt, der diese Tatsache kundtut.

Änderung: Die hier erwähnten Requester sind einer Ausgabe im Statusfenster gewichen.
Nach Bestätigen des Requesters wird das Brett mittels Brett.Brettleeren() neu initialisiert und die Variable für den
letzten Zug gelöscht. Desweiteren werden die Spielerklassen über SpielerKlasse.init() neu initialisiert.
Wird im Kommunikationsmodus gespielt, muß die Kommunikation dem gegnerischen Programm mittels der
Methode Zug das Spielergebnis mitteilen. Danach kann das Spiel erneut beginnen. Wechselt der Spielstatus in den
Zustand ,,remis``, wird ebenso verfahren.

Liegt der Zustand ,,gültiger Zug`` vor, wird über Spielbrett.Neuzeichnen() die Bildschirmausgabe aktualisiert.
Danach wird über 3D4G.AntwortZug() ein Gegenzug berechnet und im Brett gesetzt. Dazu ruft 3D4G die Methode
Zug der jeweiligen Gegnerklasse auf und gibt deren Rückgabewert an die aufrufende Callback--Funktion zurück.
Wechselt dadurch der Spielstatus, werden jeweils die gleichen Funktionen wie nach einem per Mausklick
ausgelösten Zug ausgelöst. Ist der Gegenzug getätigt, wird mittels Spielbrett.Neuzeichnen() die Bildschirmausgabe
aktualisiert.

Button--Ereignisse
Button: Neues Spiel
Dieser Event ruft den Callback NeuesSpiel() auf, welcher den Requester für die Spieleinstellungen beinhaltet. Die
im Spieleinstellungsfenster enthaltenen Bedienelemente rufen jeweils Callbacks auf, die die jeweils betroffene
Variable im (zu den Callbacks gehörenden) struct SpielModusInfo verändern.
SpielModusInfo wird bei der Erzeugung der Spieler von der Klasse 3D4G ausgewertet.

Auswahl des Spielmodus

Für jeden wählbaren Spielmodus (Mensch--Mensch, Mensch--Computer, Computer--Computer) existiert ein


Callback, der im entsprechenden Feld des SpielModusInfo--structs den gewünschten Spielmodus einträgt.

Auswahl der Spielstärke

Für jede auswählbare Spielstärke existiert ein Callback, der in SpielModusInfo speichert, welche Spielstärke gewählt
wurde. Diese Information wird bei der Erzeugung der Spieler von 3D4G berücksichtigt.

Namensvergabe

Eintrag des Namens im Texteingabefeld und Bestätigen mittels Return führt zu einem Callback, der den
eingegebenen Text in SpielModusInfo speichert.

Wer fängt an?

Beim Aufruf des Spieleinstellungsrequesters wird der Zufallsgenerator gestartet und ermittelt, wer beginnen soll.
Auch diese Information wird in SpielModusInfo gespeichert.

Weiter

Betätigen des Weiter--Buttons führt dazu, daß der struct SpielModusInfo mittels der Methode 3D4G.Spielersetzen()
an die Klasse 3D4G übermittelt wird. Mit den so erhaltenen Daten erzeugt 3D4G die entsprechenden Spieler und
initialisert die spielbezogenen Daten neu. Danach wird das Neues Spiel--Fenster geschlossen und über
Spielbrett.gebe_info_aus() das Info--Fenster aktualisiert.
Wird ein Spielmodus gewählt, welcher Einschränkungen für die Oberfläche beinhaltet, müssen diese vorgenommen
werden (z.B. Buttons sperren etc.).

Abbruch

Dieser Button führt dazu, daß ohne eine Veränderung zum Hauptfenster zurückgekehrt wird.

Button: Nochmal Spielen


Dieser Event initialisiert das Brett mittels Brett.Brettleeren() und Spielbrett.Neuzeichnen(), und setzt die Variable
für den letzten Zug zurück. Desweiteren werden die Spielerklassen von 3D4G über SpielerKlasse.init() neu
initialisiert. Danach kann weitergespielt werden.
Diese Funktion ist nicht verfügbar, falls im Kommunikationsmodus gespielt wird.
Button: Zug zurück
Dieser Event löst einen Callback namens ZZurueck() aus, welcher Brett.Zugzurueck und dann
Spielbrett.Neuzeichnen() aufruft.
Diese Funktion ist nicht verfügbar falls im Kommunikationsmodus gespielt wird.

Menü--Ereignisse
Menü: Nochmal spielen, Neues Spiel, Zug zurücknehmen
Diese Menüpunkte rufen die gleichen Callbacks wie die entsprechenden Buttons auf.

Menü: Spielende
Löst einen Callback auf die Funktion Spielbeenden() und einen Requester aus. Wird in diesem Ja (=Ja, es soll
abgebrochen werden) gewählt, wird der Destruktor der Klasse 3D4G aufgerufen. Dadurch wird das Spiel beendet.
Wird im Kommunikationsmodus gespielt wird dem gegnerischen Programm von der Klasse 3D4G über die
Methode Kommunikation.Abbruch() mitgeteilt, daß das Spiel beendet wurde. Danach wird wie oben bereits
beschrieben verfahren.
Wird Nein angewählt, wird ohne eine Aktion auszuführen zum Hauptfenster zurückgekehrt.

Menü: Tip
Bei der Anwahl dieses Menüpunktes wird über 3D4G.AntwortZug() ein Zug von der jeweiligen Gegnerstrategie
angefordert (diese berechnet natürlich einen für den Menschen günstigen Zug). Die erhaltene Position wird über
Spielbrett.gebe_info_aus() im Infofenster dargestellt.
Diese Funktion ist nicht verfügbar falls im Kommunikationsmodus gespielt wird.

Menü: Hilfe
Die Menüpunkte 1--3 lösen jeweils einen Callback auf eine Funktion aus, die einen externen Textanzeiger (z.B.
MOSAIC oder hilfsweise ein XTERM--Fenster) mit dem jeweiligen Text asynchron startet.

Die Klasse dD4G


Diese Klasse ist der Mittler zwischen den Fronten.

Sie weiß im allgemeinen was Sache ist.

Atribute
SpielerxPtr

zeigt auf Spieler x

KommunikationPtr

zeigt auf Kommunikation

BrettPtr

zeigt auf Brett

SpielbrettPtr

zeigt aufs Spielbrett

tFarben

enthält die Farbe des Spielers, der an der Reihe ist


Info

enthält die Informationen zum Spiel

ComputerNamen

Array das die schönen von uns erfundenen Namen für die 3 Strategien enthält

kommdestruct

weiß ob Pipes gelöscht werden müssen

Methoden
Constructor:

Brett und Spielbrett werden erzeugt

Neues Spiel

Spieler1 und -2 werden erzeugt

NochmalSpielen

SetStarter

ZugSetzen

Der Zug wird im Brett gesetzt

AntwortZug

Ein Antwortzug wird angefordert

Tip

Ein Tip wird angefordert

ZugZurueck

Der letzte Zug wird zurückgenommen

Spielersetzen

Spielerklassen initialisieren

Die Klasse Spieler


Die Klasse Spieler ist eine virtuelle Basisklasse für die Klassen Mensch, Computer1, Computer2, Computer3 und
Kommunikation. Sie legt die allgemeinen Schnittstellen fest.

Methoden
• Zug
Alle Klassen stellen die Methode Zug zur Verfügung. Diese holt oder berechnet einen Zug und setzt diesen
im Brett.
Der Rückgabewert dieser Methode gibt an, ob durch diesen Zug das Spiel gewonnen wurde, unentschieden
endete oder ob und wie während des Zugs das Spiel abgebrochen wurde.

• init(int i)
Beim Aufruf dieser Funktion werden die internen Daten neu initalisiert. Der Parameter i gibt an ob diese
Klasse beginnt (1) oder nicht (2).
Die Klasse Mensch
Die Klasse Mensch ist eine Dummy-Klasse.

Die Klassen Computer1, Computer2, Computer3


Methoden
• Zug
Ein Zug wird berechnet und im Brett gesetzt. Es wird die Methode ``letzter Zug'' der Klasse Brett benötigt.

Klasse Kommunikation
Die Klasse Kommunikation stellt die Verbindung zu einem anderen entsprechend vorbereiteten 3D--Vier--Gewinnt-
-Programm zur Verfügung.

(Diese Klasse repräsentiert dann sozusagen den Gegenspieler.)

Attribute
• private

tFarben GegnerFarbe:

Die Farbe, mit der die Steine der gegnerischen Seite dargestellt werden.

bool fAbbruchEmpfangen

ist ein Flag als Merker für den Fall, daß die andere Seite das Spiel beendet.

bool angefangen

ist gleich true, genau wenn 'unser' Programm das Spiel beginnt.

• public

XtInputId InputId

enthält den Rückgabewert von XtAppAddInput(...). Dieses Attribut wird in LegePipesAn() und
LoeschePipes() benötigt.
Wegen Streß mit XtAppAddInput und XtAppAddTimeOut ist dieses Attribut public.

XtIntervalId

ist die Id eines Aktiven TimeOut-Timers, der mit XtApp''ÄddTimeOut(...) gestartet wurde.
Wegen Streß mit XtApp''ÄddInput und XtApp''ÄddTimeOut ist dieses Attribut public.

int PipeHandle:

Handle der Pipe, in die die Daten von Gegenseite geschrieben werden. Muß für Aussen_Lese_Zug() (in
comsupport.c) leider public sein.

Methoden
• private

void LegePipesAn()

legt zwei Pipes ``player_1'' und ``player_2'' an, über die die Züge zwischen beiden Programmen
ausgetauscht werden. Sind die Pipes bereits vorhanden, so fängt die Gegenseite das Spiel an; im
anderen Fall beginnt dieses Programm. Diejenige Pipe, aus der Daten des Gegners gelesen werden
sollen, wird bei Motif über XtAppAddInput als Eingabemöglichkeit angemeldet.

void LoeschePipes()

meldet ReadPipe bzw. InputId als Eingabemöglichkeit bei Motif ab und löscht die Pipes ``player_1''
und ``player_2''.

void SignalHandler(int)

wird im Konstruktor als Funktion angegeben, die bei Signalen wie SIGSEGV, SIGABRT etc. noch
abgearbeitet werden soll.
Es wird die Methode LoeschePipes() aufgerufen, eine Meldung über stderr ausgegeben und das
Programm beendet.

• public

Kommunikation(tFarben farbe, dD4G *pointer):

Der Konstruktor erhält als Parameter die Farbe, mit der gegnerische Steine in diesem Programm
dargestellt werden. Der Wert wird im Attribut GegnerFarbe gespeichert. [Vergleiche auch
init(tFarben).]
Der Zeiger dD4G *pointer zeigt auf die Instanz der Klasse dD4G, die den Konstruktor aufgerufen hat.
Über diesen Zeiger wird auf Methoden der Klassen dD4G und Brett zugegriffen.
Das Flag fAbbruchEmpfangen wird initialisiert (auf false gesetzt).
Die Methode SignalHandler wird als Handler für Signale wie SIGSEGV etc. angemeldet.
Weiterhin wird LegePipesAn() aufgerufen. Innerhalb dieser Methode wird das Flag angefangen auf
den korrekten Wert gesetzt.
Danach wird der Instanz von dD4G mitgeteilt, welche Seite das Spiel beginnt. (Auf die Methode
dD4G::SetStarter kann über den von der Klasse Spieler geerbten Zeiger auf dD4G zugegriffen
werden.)

Kommunikation():

Im Destruktor wird ein eventuell noch laufender TimeOut-Zähler entfernt.


Wenn dieses Programm gewonnen hat, werden die Pipes durch
LoeschePipes() gelöscht. (Die Gegenseite muß genauso verfahren, damit die Pipes nach einem Spiel
auf jeden Fall entfernt werden.)

void Abbruch():

Wird diese Methode aus LeseZug heraus aufgerufen bzw. gilt fAbbruchEmpfangen==true, so wird
LoeschePipes() aufgerufen.

Rueckgabewert Zug(tFarben farbe):

Entspricht farbe der Farbe 'unserer' Spielsteine, so wird über Brett::Letzter_Zug() der letzte Zug dieses
Programms geholt und an das gegnerische Programm übermittelt.
Ist farbe gleich GegnerFarbe, so wird die Antwort des gegnerischen Programmes aus der
entsprechenden Pipe gelesen und ausgewertet. Ein vom Gegner gesendeter Spielzug wird über
Brett::''ßug_setzen(tZug) gesetzt.
Sind in der Pipe keine neuen Daten vorhanden, so ist der Rückgabewert 'NixDa'; ansonsten wird der
Rückgabewert von
Brett::''ßug_setzen(tZug) oder ein entsprechender Wert für eine Meldung der Gegenseite (wie
``Abbruch'') zurückgegeben.

void init(tFarben farbe)

setzt GegnerFarbe auf farbe, also die Farbe, mit der gegnerische Steine in diesem Programm
dargestellt werden.

• Funktionen außerhalb der Klasse:


Die folgenden Funktionen werden von der Klasse Kommunikation benötigt und sind in der Datei
comsupport.c implementiert.

void Aussen_Lese_Zug()

wird über XtAppAddInput aufgerufen, wenn neue Daten in der zu ReadPipe gehörenden Pipe anliegen
und ruft ihrerseits eine Methode der Klasse dD4G auf. Aus dD4G heraus wird
Kommunikation::Zug(tFarben) aufgerufen, um die neuen Daten zu bearbeiten.

void Aussen_DoTimeout()

wird über XtAppAddTimeOut aufgerufen, wenn eine festgelegte Zeitspanne abgelaufen ist und ruft
ihrerseits void Kommunikation::DoTimeOut() auf.

Die Klasse Brett


Diese Klasse repräsentiert die Daten des Spielbretts, auf die mit speziellen Methoden zugegriffen werden kann.

Attribute
Datenstruktur
Die Daten werden in einem 4 x 4 x 4 Array (Würfel) mit dem Inhalt schwarz, weiss und frei vom Typ tFarben
verwaltet.

letzterZug
Das Attribut letzterZug ist ein eindimensionales Feld vom Typ tZug. In ihm werden die gemachten Züge
gespeichert.

letzteZugNummer
Das Attribut letzteZugNummer ist ein Index (vom Typ integer), welcher immer auf den letzten Eintrag in dem Feld
letzterZug zeigt.

Anz_Steine
Das Attribut Anz_Steine speichert die Anzahl der schon gesetzten Steine und ist vom Typ integer.

Methoden
• Methode Zug-setzen
Parameter: tZug, wobei der z-Wert nicht relevant ist und Null sein kann
Rückgabewert:

0 : ungültiger Zug

1 : gültiger Zug

2 : Spiel gewonnen

3 : Remis

Diese Methode bekommt die 2D-Koordinaten(x,y) und die Farbe des Spielers übergeben, der gerade am
Zug ist und liefert oben genannte Rückgabewerte zurück. Dabei wird intern nur dann ein Stein (d.h. 1 oder
2) gesetzt, falls der Zug ein gültiger Zug war (wird mit Hilfe von Zug_legal abgeprüft). In dem Fall wird
dann zuerst das Attribut Anz_Steine und das Attribut letzterZug aktualisiert. Dann wird überprüft, ob das
Spiel mit diesem Zug gewonnen wurde oder ein Remis vorliegt. Dementsprechend ist der Rückgabewert.
Wenn ein Stein gesetzt wurde, wird Neuzeichnen(Ebene) des Spielbretts aufgerufen.
• Methode Zug-zurueck
Parameter: tFarben
Rückgabewert: FALSE: Zug wurde nicht zurückgenohmen
TRUE: Zug oder Züge wurden zurückgenohmen

Diese Methode löscht den letzten oder die beiden letzten Züge (falls der Gegner schon am Zug ist) und gibt
TRUE als Rückgabewert zurück. Falls der aktuelle Zug schon zurückgenommen oder im bisherigen
Spielverlauf noch kein Zug gemacht wurde (Attribut letzteZugNummer = -1), wird FALSE als
Rückgabewert zurückgegeben.

• Methode Daten-auslesen
Parameter: tPosition
Rückgabewert: tFarben, Farbe des Spielsteins an der Position

• Methode Brett-leeren
Parameter: keine
Rückgabewert: keiner

Diese Methode initialisiert (löscht) das komplette Brett. D.h. jedes Feldelement des Attributes wuerfel[ ][ ][
] wird auf 'frei', das Attribut Anz_Steine auf 0 und das Attribut letzteZugNummer auf -1 gesetzt.

• Methode Zug-legal
Parameter: tPosition, wobei der z-wert nicht relevant ist
Rückgabewert: tPosition, wobei der z-Wert angibt, wo der Stein reinfallen würde
Ist der z-Wert gleich -1, dann ist die senkrechte Reihe schon voll

Diese Methode prüft anhand der ihr übergebenen Parameter, an welche z-Position der Stein fallen würde,
oder ob der Zug illegal ist. Je nachdem ist der Rückgabewert. Diese Methode prüft nur UND setzt nicht den
Zug.

• Methode Letzter-Zug
Parameter: keine
Rückgabewert: tZug, d.h. den letzten gemachten Zug
tZug = -1,-1,-1,frei falls noch kein Zug gemacht wurde

Diese Methode liefert den letzten gültigen Zug. Falls im Spielverlauf noch kein Zug gemacht wurde, wird
ein dummyZug vom Typ tZug zurückgegeben.

Die Klasse Spielbrett


Attribute
private:
• Spielwiese: technisch: struct polygon Spielwiese wobei D.h. die Spielwiese besteht aus 4 Gittern, wobei nur
das oberste Gitter Zugeingaben zuläßt.

• Ein Gitter besteht aus 16 Polygonen bzw. Parallelogrammen.

wobei die c-te Ebene

Das private Linienarray Gitter beinhaltet die vier Außenlinien des obersten Gitters. Diese werden im
Konstruktor berechnet und bei der Methode Feldpositionberechnen bzw. ist_in_Gitter benötigt.

• Die Struktur polygon besteht aus den Punkten A,B,C,D und einem Mittelpunkt M; der Schnittpunkt der
beiden Diagonalen.

• Die Struktur linie besteht aus den zwei Punkten A und B, welche jeweils die globale Struktur tPunkt haben.
• Ein Punkt besteht aus einem globalen Tupel der Form tPunkt(int x; int y;).

• Integerkonstante anzahl_meldungen legt fest, wieviel Meldungen im Info--Fenster angezeigt werden sollen;
sie wird für die Methode gebe_Infos_aus gebraucht.

• Integerkonstante anzahl_buchstaben legt fest, wie lang ein Textstring sein soll und wird für die Methode
gebe_Infos_aus benötigt.

• Sonstige:

Methoden
private:
• Konstruktor: Berechnet die Linien des Spielbretts und initialisiert die Attribute.

• ccw

• Parameter: Tripel von Punkten, welche die globale Struktur tPunkt haben.

• Rückgabe:

Teste, ob man für die drei Punkte, wenn man vom ersten zum zweiten und dann zum dritten geht, sich im
oder gegen den Uhrzeiger dreht.

• schneidet

• Parameter: Tupel von Linien, wobei eine Linie die Struktur linie hat.

• Rückgabewert:

Prüft, ob sich zwei Linien schneiden.

• ist_in_Gitter

• Parameter: Koordinate des Mauscursors mit der Struktur tPunkt

• Rückgabewert:

Prüft, ob Mauscursor sich innerhalb des obersten Gitters befindet.

public:
• Neuzeichnen

1. Stellt gesamte Spielwiese graphisch dar. Dabei wird auf die Methode
Brett.Daten_auslesen(tPosition) zugegriffen. Das Info--Fenster bleibt dabei unverändert.

• Parameter: keiner

• Rückgabewert: keiner

2. aktualisiert nur die c-te Ebene von unten und verändert das Info--Fenster nicht. Es erfolgt ein
mehrmaliger Aufruf von Brett.Daten_auslesen.

• Parameter:

• Rückgabewert: keiner
• gebe_Info_aus

• Parameter: Struktur SpielModusInfo und Integer was_noch mit:

Das Info--Fenster hat den Widgetnamen infoT. Eine Textzeile hat Platz für anzahl_buchstaben. Es
ist Platz für anzahl_meldungen vorgesehen.

• Rückgabewert: keiner

Die Methode gibt Informationen über den Spielmodus, Spielstärke, wer beginnt und wer gewonnen hat aus.

• Feldpositionberechnen

• Parameter: Mauskoordinaten (bezogen auf das Programmfenster) in der Struktur tPunkt

• Rückgabewert: Koordinate mit der Struktur tPunkt

Versuch, die Mauskoordinate auf Koordinaten des oberen Gitters abzubilden.

• FeldMarkieren

• Parameter: Mauskoordinaten (bezogen auf das Programmfenster) in der Struktur tPunkt

• Rückgabewert: keiner

Die Methode verändert den Mauscursor, falls dieser sich über einem zulässigen Eingabefeld befindet.

Kommentare:
• Der von keinem erwartete Abschnitt mit Carstens [Anm.: Carsten Thurau] Kommentar...
Mein Kommentar zum Propra:
Wieder einmal eine Pflichtveranstaltung hinter mir...

Nein, so schnell und abwertend kann man das Thema nicht abhaken. Entgegen anders lautenden Gerüchten
hat es mir Spaß gemacht, und sogar einen Sinn habe ich entdeckt.

Was hat es gebracht:

• es war für die meisten wahrscheinlich der erste Versuch, in einer größeren Gruppe zu arbeiten.
Sicherlich hätte das Problem auch zu zweit lösen können ... aber darum ging es nicht. Hier konnte
man einfach lernen, was man bei Gruppenarbeit zu beachten hat, welche Fehler auftreten können
... und wir nutzten die Gelegenheit, aus Fehlern zu lernen, sehr gut aus. Vor allen Dingen dürften
wir gelernt haben, dass genaue Absprache selbst der Details, die selbstverständlich erscheinen,
selbstverständlich sein sollte.
,,Die Ebene 0 ist bei uns unten.``
,,BEI UNS IST SIE ABER OBEN¡`

• Während in INFO A nur einige lückenhafte Grundlagen der Sprache C++ vermittelt wurden, in B
diese ignoriert wurde, kam INFO C. Meine Hoffnungen, dort etwas mehr über C++ zu erfahren
waren genauso groß wie falsch. Durchaus konnte man etwas dazulernen - jedoch nur auf wenig
vernünftige Weise: es kamen neue Elemente in den Programmen der Vorlesung vor, die man dann
zu Hause verstehen sollte.
Im Propra lernte ich zwangsläufig mehr über mir vorher verborgen gebliebene Elemente wie
virtuelle Funktionen und abstrakte Basisklassen, auch einiges über Konzepte, die hinter C++
stehen - jedoch nur in Eigenarbeit.
Vielleicht sollte man mal darüber nachdenken, eine Vorlesung zum Thema C++ nicht erst im
Hauptstudium anzubieten, wenn sich fast jeder schon selbst die Kenntnisse angeeignet hat...
Aber immerhin: nun ist mir C++ kein Fremdwort mehr, nur ein Reizwort...
• Gut, dass man sich Kenntnisse mit diversen Hilfsprogrammen selbst aneignen musste; dass wird
den Informatiker auch später noch belasten. Interessant die Ärgernisse mit diesen ,,Hilfen`` -
manchmal scheinen sie Eigenleben zu entwickeln.

• ,,Manfred hat Humor -- man sieht es an Motifation.``

• Das wichtigste jedoch war der Bericht über Computer-Psychologen in Norwegen ...

• Stephan I.:
Es waere ,,günstig`` gewesen, wenn alle nicht nur Dokumente produziert hätten, sondern diese auch zur
Impelementation nochmal gelesen (!!!) hätten...
Ansonsten fand ich, daß wir Eine starke Truppe waren!! :-)
Brain over --- Insert coin

About this document ...


ProPra SS95 Endbericht

This document was generated using the LaTeX2HTML translator Version 95 (Thu Jan 19 1995) Copyright © 1993,
1994, Nikos Drakos, Computer Based Learning Unit, University of Leeds.

The command line arguments were:


latex2html -split 0 Endbericht.tex.

The translation was initiated by Stephan Ilaender on Tue Jan 30 13:49:20 MET 1996

Stephan Ilaender
Tue Jan 30 13:49:20 MET 1996