ProPra SS95 Endbericht
Contents
Anwendungsbereich und Zielgruppe
Soft- und Hardwareanforderungen
Elemente der Programmoberfläche
Kurze Beschreibung der Strategien
Menü: Nochmal spielen, Neues Spiel, Zug zurücknehmen
Die Klassen Computer1, Computer2, Computer3
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 Anderungen in Grenzen. Auf Anderungen 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.
Anderung: 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.
Anderung: 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.
Anderung: 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.
Anderung: 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''AddTimeOut() gestartet wurde.
Wegen Streß mit XtApp''AddInput und XtApp''AddTimeOut 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
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
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 OBENt`
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 Argernisse 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
Haupt | Fügen Sie Referat | Kontakt | Impressum | Nutzungsbedingungen