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
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.
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.
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.
Benötigt wird ein Unix--Rechner mit der Benutzeroberfläche X--Windows und OSF--Motif.
Das Programm reagiert im allgemeinen in vernachlässigbar kurzer Zeit, außer beim Menüpunkt ,,Hilfe``, da dann der html-Viewer Mosaic gestartet wird.



Wird der Menüpunkt ''Neues Spiel'' ausgewählt, so geschieht folgendes:

Wird ein neues Spiel begonnen, so gilt:

Bei ''Nochmal Spielen'' gilt folgendes:


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.
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.

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.
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.

Das Spielfeld wird durch vier übereinanderliegende, 4 x 4 Felder große Gitter in dreidimensionaler Ansicht ohne Perspektive dargestellt.
Die Spielsteine werden als Kugeldarstellung realisiert, wobei die Spielsteine zur Unterscheidung der Spieler verschiedenartig gefärbt sind.
Hier werden die folgenden Informationen angezeigt:
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.
Eine Menüleiste am oberen Fensterrand enthält die folgenden Menüpunkte.
Die folgenden Buttons, deren Funktion bereits unter
erläutert wurde, stehen auf der Programmoberfläche zur Verfügung.
Es ergeben sich - neben dem Modus , Mensch gegen Programm` - folgende Möglichkeiten:
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.)
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
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.
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.
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.
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.
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.
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.
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
Position
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.
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.
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.Ä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.
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.
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.
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.
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.
Diese Menüpunkte rufen die gleichen Callbacks wie die entsprechenden Buttons auf.
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.
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.
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.
Diese Klasse ist der Mittler zwischen den Fronten.
Sie weiß im allgemeinen was Sache ist.
Die Klasse Spieler ist eine virtuelle Basisklasse
für die Klassen Mensch, Computer1, Computer2, Computer3 und Kommunikation.
Sie legt die allgemeinen Schnittstellen fest.
Die Klasse Mensch ist eine Dummy-Klasse.
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.)
Kommunikation():
Diese Klasse repräsentiert die Daten des Spielbretts, auf die mit speziellen Methoden zugegriffen werden kann.
Die Daten werden in einem 4 x 4 x 4 Array (Würfel) mit dem Inhalt schwarz, weiss und frei vom Typ tFarben verwaltet.
Das Attribut letzterZug ist ein eindimensionales Feld vom Typ tZug. In ihm werden die gemachten Züge gespeichert.
Das Attribut letzteZugNummer ist ein Index (vom Typ integer), welcher immer auf den letzten Eintrag in dem Feld letzterZug zeigt.
Das Attribut Anz_Steine speichert die Anzahl der schon gesetzten Steine und ist vom Typ integer.
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.
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.
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.
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.
Diese Methode liefert den letzten gültigen Zug. Falls im Spielverlauf noch kein Zug gemacht wurde, wird ein dummyZug vom Typ tZug zurückgegeben.
,
wobei
. D.h. die Spielwiese besteht aus 4 Gittern,
wobei nur das oberste Gitter Zugeingaben zuläßt.

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.


von Punkten, welche die globale Struktur tPunkt haben.




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

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