Im Verzeichnis /home/pdv/info4/info4/ gibt es 9 Ordner aufgabe[1a,1c,2,3,4a,4b,5,6,7], in denen je nach Aufgabe Vorgaben und Dokumentationen enthalten sind. Es ist immer ratsam, zunächst alle Dateien, die zur Verfügung gestellt werden, genau anzuschauen. Auch das Lesen der Dokumentation hat immer wieder geholfen, die Aufgaben noch leichter bearbeiten zu können.
Informatik 4 |
|
TU Berlin |
Die Computer im Terminalraum sind mit zwei LED-Anzeigen ausgestattet, die direkt nebeneinander an der Vorderseite des kleinen silbernen Kastens neben dem Computer zu finden sind. Jede LED-Anzeige besteht aus 8 Segmenten (inklusive dem Punkt) und wird über je einen 8 bit breiten parallelen Port angesprochen. Die beiden Parallelports haben die Namen cTTLPort1 (für die linke LED-Anzeige) und cTTLPort2 (für die rechte LED-Anzeige) und sind an einer bestimmten Adresse im Hauptspeicher eingeblendet (siehe Datei LED.h).
Beim Schreiben eines Bytes an die Adresse eines Parallelports im Hauptspeicher, wird der Wert automatisch über den Parallelport zur zugehörigen LED-Anzeige geschickt. Dabei ist jedes der 8 Segmente der LED-Anzeige genau einem Bit zugeordnet, sodass alle Segmente gelöscht werden, wenn man 0x00 überträgt und alle Segmente eingeschaltet werden, wenn man 0xFF überträgt. Die genaue Zuordnung der Bits zu den einzelnen Segmenten ist ebenfalls in der Header-Datei LED.h beschrieben.
Ihr sollt eine kleine Assemblerfunktion schreiben, die alle Segmente beider LED-Anzeigen hintereinander ein- und wieder ausschaltet, sodass eine visuell schöne Figur entsteht (z.B. eine laufende 8). Dabei sollen möglichst viele Segmente benutzt werden und zwischen dem Einschalten eines Segments und dem Einschalten eines anderen Segments soll eine kleine Pause gemacht werden, damit die dargestellten Figuren gut zu erkennen sind. Die Pause kann z.B. durch aktives Warten mit einer Zählschleife programmiert werden.
Eure Assemblerfunktion hat die Syntax:
void AnimiereLED( );
und ist rudimentär in der Datei LED.s vorgegeben.
Alle Dateien, die Ihr benötigt, findet Ihr unter /home/pdv/info4/info4/aufgabe1a. Kopieren müsst Ihr Euch nur LED.S (zum Ergänzen) und die Datei Makefile, die den Übersetzungsvorgang steuert (siehe auch die Dokumentation zur "Assembler-Entwicklungsumgebung").
Die Lösung dieser Aufgabe ist spätestens zu Beginn der Tutorien in der 5. Vorlesungswoche bei der Tutorin / dem Tutor abzugeben.
Die Collatz-Funktion sei wie folgt definiert:
In der Collatz-Folge wird die Collatz-Funktion so lange auf einen gegebenen Eingabewert angewandt, bis das Ergebnis 1 herauskommt.
Beispiel:
Die Länge dieser Collatz-Folge ist 8.
Schreiben Sie ein Programm in MC68000-Assembler, das zu einer Zahl die Länge ihrer Collatz-Folge berechnet. Das Programm wird wie folgt aufgerufen:
int collatz(int *n)
Achtung: Programme ohne aussagekräftige Kommentare werden nicht gewertet!
Zeichnen Sie den Kellerspeicher auf, wie er nach Aufruf des Unterprogramms und nach Rettung der Register aussieht. Zeichnen Sie ferner ein, wo der Kellerzeiger (stack pointer) dann steht.
Programme in Assembler sind heutzutage sehr selten und auch nur in wenigen Ausnahmesituationen sinnvoll. Eine Anwendung könnte eine rechenintensive Berechnung sein, die ohne die üblichen Sicherheitsabfragen, die eventuell der Compiler hinzufügt, schneller laufen könnte. Allerdings fehlen diese Sicherheitsabfragen auch bei der Entwicklung von neuen Programmen.
Ihr sollt eine Funktion implementieren, die die folgende Summe berechnet:
Von C aus soll die Funktion als
void Calculate(unsigned short n, unsigned int *AdresseDesErgebnisses)
aufgerufen werden.
In eurer Funktion müßt ihr zuerst die Aufrufparameter vom Stack lesen, dann die Berechnung durchführen und zum Schluß das Ergebnis an der übergebenen Speicherstelle ablegen.
Bitte beachtet, daß der verwendete Prozessor MC68000 bei der Multiplikation nur zwei Wort-Werte miteinander verrechnen soll (und kann) und als Ergebnis einen Long-Wert liefert.
Zusatzaufgabe: Überlegt euch, wie groß n maximal sein darf, damit die Berechnung ohne Fehler abläuft.
Alle Dateien, die Ihr benötigt, findet Ihr unter /home/pdv/info4/info4/aufgabe1c. Kopieren müsst Ihr Euch nur Aufgabe1.S (zum Ergänzen) und die Datei Makefile, die den Übersetzungsvorgang steuert (siehe auch die Dokumentation zur "Assembler-Entwicklungsumgebung").
Informatik 4 |
|
TU Berlin |
Der folgende Präzedenzgraph soll in ein Programm mit
nebenläufigen Prozessen umgewandelt werden.
Gebt ein Prozeßsystem mit den aus der Vorlesung bekannten fork/join-Anweisungen an, das dem Präzedenzgraphen entspricht.
Implementiert das Prozeßsystem aus dem
ersten Aufgabenteil in Java, indem Ihr die Klasse Thread
aus der Java-Bibliothek java.lang benutzt, die einen
leichtgewichtigen Prozeß abbildet. Die Anweisungen fork
und join sollen durch new Thread() und die Methoden
start() und join() ersetzt werden. Dabei dienen
new Thread() und start() zur Erzeugung und zum
Start eines Kindprozesses. Mit join() wartet der
Elternprozeß, bis der referenzierte Kindprozeß beendet
ist.
Es sollen alle 6 Prozesse Pi vom
Elternprozeß erzeugt und gestartet werden! Jeder Prozeß
soll mindestens eine Sekunde warten und dazu die sleep()
Methode aufrufen.
Die zeitliche Reihenfolge der Ausführung
soll durch Bildschirmausgaben deutlich gemacht werden. Hierzu sollen
Beginn und Ende der Ausführungen von P1
bis Pn durch
entsprechende Ausgaben gekennzeichnet werden. Zusätzlich dazu
soll der Name des aktuellen Prozesses sowie aller zu dieser Zeit
laufenden Java-Prozesse ausgegeben werden. Die Klasse Thread
stellt die Methoden enumerate() und isAlive() zur
Verfügung, mit Hilfe derer die laufenden Prozesse ermittelt
werden können. Der Prozeßname kann dann durch die Methode
getName() ermittelt werden. Dazu ist es aber erforderlich,
jedem neuen Prozeß den Namen entweder gleich als Argument beim
Erzeugen oder durch die Methode setName() mitzuteilen.
Alle benötigten Klassen und Methoden stehen in Java automatisch zur Verfügung, da alle Klassen der Bibliothek java.lang automatisch eingebunden werden.
Eine Dokumentation zum Erzeugen von Threads in Java findet ihr hier!
Informatik 4 |
|
TU Berlin |
An einem neuen Flughafen soll der Zugang der Passagiere zur Zugangsbrücke eines Flugzeugs mit Semaphoren geregelt werden. Es gibt Passagiere, die Erste Klasse fliegen, normale Passagiere und Personen, die Hilfe beim Zugang zum Flugzeug benötigen, z. B. Rollstuhlfahrer oder Kinder. Wir nennen die Personen ab sofort ErsteKlasse, Normalos und Hilfsbedürftige.
Die ErsteKlasse und Normalos bewegen sich frei im Flughafen, die Hilfsbedürftigen werden in einem Nebenraum betreut. Sobald die Fahrgäste zum Einsteigen aufgefordert werden, muß das Progamm sicherstellen, daß folgende Regeln eingehalten werden:
Vor der Zugangsbrücke gibt es einen kleinen Warteraum.
Der Warteraum darf maximal von 15 Passagieren gleichzeitig benutzt werden.
ErsteKlasse und Normalos und dürfen den Warteraum nicht gleichzeitig benutzen.
ErsteKlasse sollen vor wartenden Normalos den Warteraum betreten können.
Wenn ein ErsteKlasse wartet, dürfen keine Normalos mehr den Warteraum betreten. Sich bereits im Warteraum befindliche Normalos dürfen aber dort bleiben. Wenn diese den Warteraum verlassen haben, ist ein ErsteKlasse an der Reihe.
Die Brücke zum Flugzeug trägt nur ein begrenztes Gewicht von 10 Passagieren.
Auch Hilfsbedürftige sollen über die Brücke das Flugzeug betreten. Dabei darf die Brücke nicht überbelastet werden. Da Hilfsbedürftige von Helfern begleitet werden, zählt jeder Hilfsbedürftige wie 5 Personen.
Die Hilfsbedürftigen warten in einem separaten Raum und betreten die Brücke nicht durch den Warteraum für alle anderen Passagiere sondern direkt.
Implementiert diese Regelung mit den gegebenen Vorgaben. Dabei sollen ErsteKlasse, Normalos und Hilfsbedürftige als nebenläufige Prozesse (Java Threads) realisiert und mit Hilfe von Semaphoren synchronisiert werden.
Im Verzeichnis vorgabe befinden sich bereits mehrere Klassen, die zur Implementierung benutzt werden sollen. Die Klassen ErsteKlasse, Normalo und Hilfsbedürftig repräsentieren dabei die ErsteKlasse, Normalos und Hilfsbedürftigen (als nebenläufige Prozesse) und sollen ergänzt werden, um die o.g. Regeln zu erfüllen. Es können auch zusätzliche Klassen oder Interfaces implementiert werden.
Die Klasse WarteschlangenSemaphore implementiert Semaphoren, deren Zähler bei der Instanziierung mit einem Wert initialisiert werden können und die die beiden Methoden P() und V() zur Verfügung stellen.
Eine Testumgebung enthält die Klasse FlughafenTest, die automatisch nach dem Start der Java-Applikation (Klasse Main) ausgeführt wird und die Prozesse in einer zufälligen bzw. einer deterministischen Reihenfolge erzeugt. Hierzu ist es unbedingt notwendig, dass die drei nebenläufigen Prozesse in direkter oder indirekter Ableitung von der Klasse WarteraumThread sind.
Zur Simulation der Zeit, die ein ErsteKlasse, Normalo bzw. Hilfsbedürftiger zum Überqueren der Brücke benötigt, soll in den Implementierungen die Methode betrittBrücke() aus der Klasse WarteraumThread aufgerufen werden.
Die Dokumentation zur Testumgebung findet ihr hier!
Passagiere kommen an eine Bushaltestelle. Sobald der Bus ankommt, steigen alle wartenden Passagiere in den Bus, allerdings dürfen während des Einsteigens keine weiteren Passagiere hinzukommen. Das heißt, Passagiere die während des Einsteigens an die Bushaltestelle kommen, müssen auf den nächsten Bus warten.
Der Bus kommt immer leer an die Haltestelle und hat 50 Sitzplätze. Falls mehr Passagiere warten als Sitzplätze vorhanden sind, müssen einige Passagiere auf den nächsten Bus warten.
Sobald alle Passagiere den Bus bestiegen haben, soll der Bus abfahren. Falls keine Passagiere an der Haltestelle warten, soll der Bus unverzüglich abfahren.
Der Ablauf soll durch ein nebenläufiges Programm in der aus der Vorlesung bekannten Notation beschrieben werden. Die Synchronisation soll über Semaphore erfolgen. Es soll zwei Prozeßtypen für Passagiere und Busse geben:
type Passagier = process
type Bus = process
Schreiben Sie ein Programm mit obigen Prozessen und Regeln.
-- Globale Deklarationen:
|
|
type Bus = process -- Euer Programm
End process; |
type Passagiere = process -- Euer Programm
End process; |
Informatik 4 |
|
TU Berlin |
Bei der Einsichtnahme in die Informatik-B Klausur im FR2051 dürfen maximal 5 Studenten gleichzeitig ihre Klausur einsehen. Zwischendurch soll dieser Raum gereinigt werden. Das Reinigungspersonal darf den Raum aber erst dann betreten, wenn keine Studierenden mehr im Raum sind. Umgekehrt gilt, daß der Raum während der Reinigungsarbeit nicht benutzt werden darf. Das Reinigungspersonal soll bevorrechtigt sein.
Lösen Sie das folgende Synchronisationsproblem mit Hilfe eines Monitors in Java. Hierbei ist die Java-Anweisung synchronized zu verwenden!
Der Monitor soll die folgenden Prozeduren zur Verfügung stellen:
beginnEinsicht
endeEinsicht
beginnReinigung
endeReinigung
Im Verzeichnis vorgabe befinden sich bereits mehrere Klassen, die zur Implementierung benutzt werden sollen. Die Klasse FR2051 repräsentiert dabei den Raum, dessen Betreten synchronisiert werden soll und soll ergänzt werden, um die o.g. Regeln zu erfüllen. Es können auch zusätzliche Klassen oder Interfaces implementiert werden.
Zusätzlich zu der Java-Anweisung synchronized soll die Anweisung wait() verwendet werden, durch die der aktuelle Thread wartet, bis ein anderer Thread notify() bzw. notifyAll() auf dem zugehörigen Objekt aufgeruft. Beides wird in folgenden Kapiteln genau beschrieben: Thread-Synchronisation und Wait Sets and Notification.
Eine Testumgebung enthält die Klasse KlausureinsichtTest, die automatisch nach dem Start der Java-Applikation (Klasse Main) ausgeführt wird und die Studenten- und Reinigungspersonal-Prozesse in einer zufälligen bzw. einer deterministischen Reihenfolge erzeugt.
Die Dokumentation zur Testumgebung findet ihr hier!
Im Endspiel der
Fußballweltmeisterschaft steht es sowohl nach Ende der
regulären Spielzeit, als auch nach der Verlängerung
unentschieden. Es kommt zum Elfmeterschießen.
Implementieren
Sie das Elfmeterschießen in Java mit Monitoren unter
Berücksichtigung folgender Vorgaben:
Die Mannschaften sind mit "0" und "1" bezeichnet.
Fünf Spieler jeder Mannschaft haben einen Torschuss. (Gehen Sie davon aus, dass das Spiel danach entschieden ist.)
Die Mannschaften schießen abwechselnd. Mannschaft "0" beginnt.
Der Ablauf eines Schusses: Zuerst
geht der gegnerische Torwart zum Tor und wartet auf den Schuss,
danach geht der Spieler zum Elfmeterpunkt und schießt.
D.h.
es ergibt sich folgendes:
Torwart von Mannschaft 1 geht zum Tor
und wartet auf den Schuss.
Spieler von Mannschaft 0 geht zum
Elfmeterpunkt und schießt.
Torwart von Mannschaft 0 geht
zum Tor und wartet auf den Schuss.
Spieler von Mannschaft 1 geht
zum Elfmeterpunkt und schießt.
Torwart von Mannschaft 1
geht zum Tor und wartet auf den Schuss.
Spieler von Mannschaft 0
geht zum Elfmeterpunkt und schießt.
usw.
Vervollständigen Sie die run-Methoden der Klasse Spieler und der Klasse Torwart. Eine Synchronisation kann über das Objekt dran erfolgen.
Lösen Sie das Synchronisationsproblem mit Hilfe eines Monitors in Java. Hierbei ist die Java-Anweisung synchronized zu verwenden!
Im Verzeichnis vorgabe befinden sich bereits mehrere Klassen, die zur Implementierung benutzt werden sollen. Die Klassen Main und Steuerung müssen nicht verändert werden. Die Spieler sind durch die Klasse Spieler repräsentiert und die Torwarte durch die Klasse Torwart. Diese beiden Klassen sollen ergänzt werden, um die o.g. Regeln zu erfüllen. Es können auch zusätzliche Klassen oder Interfaces implementiert werden.
Zusätzlich zu der Java-Anweisung synchronized soll die Anweisung wait() verwendet werden, durch die der aktuelle Thread wartet, bis ein anderer Thread notify() bzw. notifyAll() auf dem zugehörigen Objekt aufgeruft. Beides wird in folgenden Kapiteln genau beschrieben: Thread-Synchronisation und Wait Sets and Notification.
Mit der Klasse Main kann die Korrektheit der eigenen Implementierung überprüft werden. Die o.g. Ausgabe muss sich ergeben.
Die Dokumentation zur Testumgebung findet ihr hier!
Informatik 4 |
|
TU Berlin |
Ein produzierender und ein
konsumierender Prozeß sollen über ein Netzwerk
kommunizieren. Dazu sollen Java-Sockets verwendet werden.
Der
produzierende Prozeß (Auftraggeber) liest eine Zeile von der
Standardeingabe (Tastatur) ein und sendet diese über eine
Socketschnittstelle zu einem konsumierenden Prozeß
(Auftragnehmer). Dieser nimmt die Zeile entgegen und wandelt die
einzelnen Zeichen in Großbuchstaben um. Anschließend
wartet der Auftragnehmer 10 Sekunden und schickt die umgewandelte
Zeile wieder zurück zum Auftraggeber, wo die empfangenen Zeichen
dann am Bildschirm ausgegeben werden sollen.
Folgende Anforderungen sollen dabei erfüllt werden:
Der Auftragnehmer soll Anfragen von mehreren Auftraggebern nebenläufig bearbeiten können. Dazu soll für jede Socket-Verbindung ein neuer Prozess (Java Thread) gestartet werden.
Ein Auftraggeber darf eine Zeile erst dann zum Auftragnehmer schicken, wenn diese vollständig eingegeben ist. Anschließend soll auf die Antwort vom Auftragnehmer gewartet und dann die empfangene Zeile ausgegeben werden. Erst im Anschluß daran soll der Auftraggeber wieder auf die Eingabe einer Zeile warten.
Durch Eingabe von "Ende" soll sich der Auftraggeber beim Auftragnehmer abmelden. Dazu muß diesem eine entsprechende Nachricht geschickt werden. Der Auftragnehmer soll terminieren, wenn er von allen Auftraggebern die "Ende"-Nachricht erhalten hat.
Es sollen die Programme für den Auftraggeber und den Auftragnehmer geschrieben werden.
Für die Sockets soll der Port 30000 benutzt werden. Die Kommunikation zwischen Auftraggeber und Auftragnehmer kann auf einem Rechner getestet werden (dann muß als Host beim Auftraggeber "localhost" übergeben werden) oder auf verschiedenen Rechnern (dann muß als Host der Name des Rechners übergeben werden, auf dem der Auftragnehmer-Prozeß läuft).
Das Einlesen einer Zeile aus einer Socketschnittstelle kann folgendermaßen implementiert werden: in = new BufferedReader(new InputStreamReader(mySocket.getInputStream()))
Das Übertragen einer Zeichenkette über eine Socketschnittstelle kann implementiert werden als: out = new DataOutputStream(mySocket.getOutputStream())
Um zu verhindern, daß die ServerSocket blockiert, wenn auf eine Verbindung gewartet wird, kann die Zeit bis zum Auftreten einer InterruptedException gesetzt werden durch: myServerSocket.setSoTimeout(millisekunden)
Eine Dokumentation über die Programmierung von Sockets unter Java findet ihr hier!
Informatik 4 |
|
TU Berlin |
Es soll ein Schiffe-Versenken-Spiel für den Netzwerkbetrieb implementiert werden. Die beiden Kontrahenten verwenden je ein eigenes Java-Programm. Die Kommunikation zwischen den beiden Programmen erfolgt mittels RMI (Remote Method Invocation).
Jeder Kontrahent implementiert das GameInterface mit folgenden beiden entfernt aufrufbaren Methoden:
int shoot(int row, int column) throws RemoteException;
void begin() throws RemoteException;
Ein Kontrahent ruft shoot() auf, um einen Schuß auf dem gegnerischen Spielfeld zu plazieren. Die Rückgabewerte sind:
GameInterface.Water - bei einem Wassertreffer
GameInterface.Hit - bei einem Schiffstreffer
GameInterface.Sunk - wenn das Schiff gesunken ist
GameInterface.Lost - wenn der Spieler verloren hat
Damit ein Schiffe-Versenken-Programm die Verbindung zu einem Kontrahenten herstellen kann, läuft auf dem Rechner pepita ein Server-Programm, die Schiffe-Versenken-Registry. Diese implementiert das DistributionInterface mit
GameInterface getOpponent(GameInterface game);
als einziger Methode. Der Parameter ist eine Referenz auf das GameInterface des aufrufenden Kontrahenten, der Rückgabewert eine Referenz auf das entfernte GameInterface des zugeordneten Gegners. Jeder Kontrahent ruft die getOpponent()-Methode auf. Diese kehrt erst dann zurück, wenn ein Gegner zugewiesen wurde. Nachdem beide Aufrufe zurückgekehrt sind, wird von der Schiffe-Versenken-Registry bei dem Kontrahenten, der den ersten Zug machen soll, die parameterlose Methode begin() aufgerufen.
Um das DistributionInterface der Schiffe-Versenken-Registry zu erhalten, bedienen sich die Kontrahenten der Standard-RMI-Registry auf pepita. Der Name dieser Registry muß beim lookup()-Aufruf folgendermaßen angegeben werden: "//pepita.cs.tu-berlin.de/SV-Registry".
Die folgende Abbildung illustriert die Verteilung der Komponenten und die erforderlichen Methodenaufrufe:
Auf dem 12 mal 12 Felder großen Spielfeld plaziert jeder Kontrahent 5 Schiffe von 2 bis 6 Feldern Größe in einer horizontalen oder vertikalen Linie. Schiffe dürfen sich weder vertikal, horizontal noch diagonal berühren.
Der Kontrahent am Zug nennt die Koordinaten für einen Schuß auf das gegnerische Feld. Der Inhaber des gegnerischen Feldes meldet zurück, ob ein Schiff getroffen wurde oder ob es sich um einen Wassertreffer handelt.
Bei einem Schuß ins Wasser wechselt das Zugrecht, nach einem Treffer ist man nochmal dran.
Wenn alle Felder eines Schiffes getroffen wurden, ist das Schiff versenkt. Sobald das letzte Schiff eines Kontrahenten versenkt ist, hat dieser verloren.
Es soll die vorgegebene Klasse GameServer
ergänzt werden, die das GameInterface implementiert.
Hierbei müssen insbesondere die Methoden play(),
shoot() und begin() erweitert bzw. realisiert
werden. Deshalb im Folgenden eine Übersicht, was die einzelnen
Methoden für einen korrekten Spielablauf tun müssen.
ACHTUNG:
Einige Dinge sind der Einfachheit halber bereits in der Vorgabe
implementiert!
Die Klasse GameServer ist ein Fernobjekt und muß deshalb von der Klasse UnicastRemoteObject abgeleitet sein!
Die statische main() Methode, also der Eintrittspunkt der Applikation, darf nur das GameServer Objekt erzeugen (durch GameServer g = new GameServer()) und dort eine nichtstatische play()-Methode aufrufen.
Die play() Methode soll:
unter Zuhilfenahme der Klasse Field zwei Spielfelder erzeugen, wobei eines die Positionen der eigenen Schiffe und das andere die Treffer beim Kontrahenten enthält. Ersteres kann durch zwei Methoden der Klasse Field initialisiert werden, das zweite Feld bleibt anfangs leer:
manual_init(GameWindowInterface), wodurch der Benutzer aufgefordert wird, die Schiffe durch Mausklicks zu positionieren. Als Argument muß das zugehörige Fenster übergeben werden, auf dem die Schiffe angezeigt werden sollen.
random_init(), wodurch alle Schiffe zufällig auf dem Spielfeld verteilt werden.
jedes Spielfeld in einem eigenen Fenster dargestellen. Hierfür wird die Klasse GameWindow zur Verfügung gestellt, die beim Instanziieren eine Referenz auf ein Objekt der Klasse Field bekommt und dieses Spielfeld anzeigt. Durch Aufruf der Methode set_message() kann ein Text angegeben werden, der über dem Spielfeld dargestellt wird und Informationen wie z.B. "Treffer!" enthalten kann. Dieser Text wird solange angezeigt, bis ein neuer Text bzw. ein leerer Text übergeben wird.
durch Aufruf von wait() auf ein Signal warten und dann die shoot() Methode beim GameInterface des Kontrahenten aufrufen. Durch Auswertung des Ergebnisses kann dann festgestellt werden, ob wieder gewartet werden muß oder ob man getroffen hat und deshalb nocheinmal schießen darf.
In der Implementierung von shoot() (die von dem Kontrahenten aufgerufen wird) muß überprüft werden, ob sich auf dem eigenen Spielfeld bei den angegebenen Koordinaten ein Schiff befindet und der Rückgabewert entsprechend gesetzt werden. Anschließend kann in dieser Methode wiederum festgestellt werden, ob der Kontrahent nocheinmal schießen darf. Wenn dies nicht der Fall ist, dann kann der play()-Methode durch den Aufruf von notify() das Signal zum Schießen gegeben werden.
In der begin() Methode (die von der SV-Registry aufgerufen wird) darf keine Endlosschleife ausgeführt werden, sondern es soll der eigenen play() Methode durch einen Aufruf von notify() das Signal zum Schießen gegeben werden.
Alle 3 Methoden (shoot(), begin() und play()) müssen synchronized sein!
Die Klasse Field enthält eine Reihe von Methoden zum Abfragen und Setzen der einzelnen Spielfelder. Für eine einfache Implementierung des Schiffe-Versenken-Spiels benötigt man folgende Aufrufe:
getShip(), um herauszubekommen, ob sich bei den gegebenen Koordinaten ein Schiff oder Wasser befindet. Für einen zurückgegebenen Schiffstypen kann dann die Methode queryShip() aufgerufen werden.
setShip(), um auf dem Spielfeld mit den eigenen Treffern beim Kontrahenten einen Treffer zu kennzeichnen. Da der getroffene Schiffstyp unbekannt ist, sollte die Konstante Field.bigship verwendet werden
queryShip(), um die nichtversunkene Größe eines gegebenen Schiffes zu ermitteln
queryShips(), um herauszubekommen, ob sich noch nichtversunkene Schiffe auf dem Spielfeld befinden, denn wenn nicht, dann ist das Spiel verloren
setHit(), um einen Treffer des Gegners an der gegebenen Position zu setzen (in dem zugehörigen Fenster wird das Kästchen in der aktuellen Farbe dunkler dargestellt)
Da die Klasse GameWindow Änderungen auf dem dargestellten Spielfeld nicht mitbekommt, muß nach jedem set...() Aufruf der Klasse Field das entsprechende Fenster neu gezeichnet werden. Hierzu dient die Methode repaint() der Klasse GameWindow.
Bitte nicht System.setSecurityManager(new RMISecurityManager()) aufrufen! Dies ist nur nötig, wenn die Klassen in einem WebBrowser laufen.
Hinweis: Die Dokumentation zur Testumgebung findet ihr hier!
Informatik 4 |
|
TU Berlin |
Für ein seitenverwaltetes System sollen die Ersetzungsstrategien FIFO, SC (Second Chance) und LRU implementiert werden.
Eine Testumgebung existiert hierfür bereits in der Hauptklasse Main in der auch eine Benutzungsschnittstelle enthalten ist. Von dieser Klasse aus werden die zu implementierenden Ersetzungsstrategien in den Klassen FIFO, SC und LRU als auch zum Vergleich die korrekten Lösungen aus den Klassen Muster_xxx aufgerufen. Außerdem stehen einige Konstanten (public final static) zur Verfügung, die von allen Klassen benutzt werden können. Diese Konstanten dürfen NICHT geändert werden und definieren einerseits die Anzahl der zufälligen Anforderungen (MAXREQUESTS) und die Anzahl der Kacheln, die verfügbar sind (MAXKACHELN). Alle Klassen, in denen die Ersetzungsstrategien implementiert sind, erben von der Klasse Strategie, in der die abstrakten Methoden getName() und requestPage() und die Rückgabewerte von requestPage() als Konstanten deklariert sind. Die Methode requestPage() wird bei jeder Seitenanforderung aufgerufen und muß demzufolge in den Dateien FIFO.java, SC.java und LRU.java geändert werden. Es können zusätzliche Methoden und Variablen in die Klassen FIFO, SC und LRU eingefügt werden. Außerdem kann es sinnvoll sein, einen Konstruktor für diese Klassen zu definieren, der bestimmte globale Variablen und Strukturen initialisiert. Andere vorgegebene Klassendateien werden NICHT angefaßt!
Beim ersten Aufruf Eurer Prozeduren seien alle Kacheln unbelegt (Inhalt = EMPTY). Um einen Vergleich mit der Musterlösung zu ermöglichen, soll bei mehreren möglichen Kacheln für eine Ersetzung die Kachel mit der kleinsten Kachelnummer gewählt werden. Bei einer Seitenanforderung wird die Methode requestPage() mit folgenden Parametern aufgerufen. Zum einen mit der angeforderten Seite und mit der aktuellen Belegung der Kacheln. In die aktuelle Belegung soll, sofern noch nicht vorhanden, die angeforderte Seite eingetragen werden. Als Rückgabewert soll PAGE_FAULT geliefert werden, falls ein Seitenfehler auftrat und ALREADY_LOADED, falls dies nicht der Fall war.
Bei der Second Chance Strategie soll bei Neubelegung einer Kachel das Referenzbit auf '1' gesetzt werden!
Die Speicheranforderungen sind fest vorgegeben. Es kann aber beim Start der Klasse Main das Argument 'random' angegeben werden, wodurch zufällige Anforderungen erzeugt werden.
Die Dokumentation zur Testumgebung findet ihr hier!
Letzte Änderung: 21.03.2007 |