3. Übungsblatt
3. Übungsblatt
3. Übungsblatt
Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.
YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.
Prof. aa Dr. J. GieslProgrammierung WS11/12<strong>Übungsblatt</strong> 3 (Abgabe 10.11.2011)M. Brockschmidt, F. Emmes, C. Fuhs, C. Otto, T. StröderAllgemeine Hinweise:• Die Hausaufgaben sollen in Gruppen von je 2 Studierenden aus der gleichen Kleingruppenübung bearbeitetwerden. Namen und Matrikelnummern der Studierenden sind auf jedes Blatt der Abgabe zu schreiben.Heften bzw. tackern Sie die Blätter!• Die Nummer der Übungsgruppe muss links oben auf das erste Blatt der Abgabe geschrieben werden.• Die Lösungen müssen bis Donnerstag, den 10.11.2011 um 17:30 Uhr in den entsprechenden Übungskasteneingeworfen werden. Sie finden die Kästen am Eingang Halifaxstr. des Informatikzentrums (Ahornstr. 55).Alternativ können Sie die Lösungen direkt im Tutorium abgeben.• Beachten Sie die auf der Webseite veröffentlichten Code-Konventionen in Ihren Programmen.• In einigen Aufgaben müssen Sie in Java programmieren und .java-Dateien anlegen. Drucken Sie diese ausund schicken Sie sie per E-Mail vor Donnerstag, den 10.11.2011 um 17:30 Uhr an Ihre Tutorin/Ihren Tutor.Stellen Sie sicher, dass Ihr Programm von javac akzeptiert wird, ansonsten werden keine Punkte vergeben.• Beachten Sie, dass am Dienstag, dem 8.11.2011 die Fachschaftsvollversammlungen zwischen 10:00 und14:00 Uhr stattfinden. Die Vorlesung findet daher an diesem Termin nicht statt.Tutoraufgabe 1 (Verifikation):Gegeben sei folgendes Java-Programm P :〈 a.length>0 〉 (Vorbedingung)i = a.length - 1;res = a[i];while (i > 0) {i = i - 1;res = Math.max(res, a[i]);}〈 res = max{ a[j] | 0 ≤ j < a.length } 〉 (Nachbedingung)a) Vervollständigen Sie die folgende Verifikation des Algorithmus im Hoare-Kalkül, indem Sie die unterstrichenenTeile ergänzen. Hierbei dürfen zwei Zusicherungen nur dann direkt untereinander stehen, wenn dieuntere aus der oberen folgt. Hinter einer Programmanweisung darf nur eine Zusicherung stehen, wenn diesaus einer Regel des Hoare-Kalküls folgt.Hinweise:• Sie dürfen beliebig viele Zusicherungs-Zeilen ergänzen oder streichen. In der Musterlösung werdenallerdings genau die angegebenen Zusicherungen benutzt.• Bedenken Sie, dass die Regeln des Kalküls syntaktisch sind, weshalb Sie semantische Änderungen(beispielsweise von x + 1 = y + 1 zu x = y) nur unter Zuhilfenahme der Konsequenzregeln vornehmendürfen.• Nehmen Sie als gegeben an, dass Math.max(a,b) der mathematischen Funktion max(a, b) entspricht.1
Programmierung WS11/12<strong>Übungsblatt</strong> 3 (Abgabe 10.11.2011)〈a.length > 0〉i = a.length - 1;〈 〉res = a[i];〈 〉〈 〉while (i > 0) {〈 〉〈 〉i = i - 1;〈 〉〈 〉res = Math.max(res, a[i]);}〈 〉〈 〉〈res = max{ a[j] | 0 ≤ j < a.length }〉b) Untersuchen Sie den Algorithmus P auf seine Terminierung. Für einen Beweis der Terminierung muss eineVariante angegeben werden und unter Verwendung des Hoare-Kalküls die Terminierung unter der Voraussetzunga.length > 0 bewiesen werden.Aufgabe 2 (Verifikation):Gegeben sei folgendes Java-Programm P :〈y ≥ 0〉 (Vorbedingung)r = 1;c = y;while (c > 0) {r = r * x;c = c - 1;}〈r = x y 〉 (Nachbedingung)(6 + 2 = 8 Punkte)a) Vervollständigen Sie die folgende Verifikation des Algorithmus im Hoare-Kalkül, indem Sie die unterstrichenenTeile ergänzen. Hierbei dürfen zwei Zusicherungen nur dann direkt untereinander stehen, wenn die2
Programmierung WS11/12<strong>Übungsblatt</strong> 3 (Abgabe 10.11.2011)untere aus der oberen folgt. Hinter einer Programmanweisung darf nur eine Zusicherung stehen, wenn diesaus einer Regel des Hoare-Kalküls folgt.Hinweise:• Sie dürfen beliebig viele Zusicherungs-Zeilen ergänzen oder streichen. In der Musterlösung werdenallerdings genau die angegebenen Zusicherungen benutzt.• Bedenken Sie, dass die Regeln des Kalküls syntaktisch sind, weshalb Sie semantische Änderungen(beispielsweise von x + 1 = y + 1 zu x = y) nur unter Zuhilfenahme der Konsequenzregeln vornehmendürfen.〈y ≥ 0〉r = 1;〈 〉c = y;〈 〉〈 〉while (c > 0) {〈 〉〈 〉r = r * x;〈 〉c = c - 1;〈 〉}〈 〉〈 〉〈r = x y 〉b) Untersuchen Sie den Algorithmus P auf seine Terminierung. Für einen Beweis der Terminierung muss eineVariante angegeben werden und unter Verwendung des Hoare-Kalküls die Terminierung unter der Voraussetzungy ≥ 0 bewiesen werden.Aufgabe 3 (Verifikation):Gegeben sei folgendes Java-Programm P :(7 + 3 = 10 Punkte)〈 a.length > 0 〉 (Vorbedingung)3
Programmierung WS11/12<strong>Übungsblatt</strong> 3 (Abgabe 10.11.2011)res = a[0];i = 1;while (i < a.length) {if (a[i] > res) {res = a[i];}i = i + 1;}〈 res = max{ a[j] | 0 ≤ j < a.length } 〉 (Nachbedingung)a) Vervollständigen Sie die folgende Verifikation des Algorithmus im Hoare-Kalkül, indem Sie die unterstrichenenTeile ergänzen. Hierbei dürfen zwei Zusicherungen nur dann direkt untereinander stehen, wenn dieuntere aus der oberen folgt. Hinter einer Programmanweisung darf nur eine Zusicherung stehen, wenn diesaus einer Regel des Hoare-Kalküls folgt.Hinweise:• Sie dürfen beliebig viele Zusicherungs-Zeilen ergänzen oder streichen. In der Musterlösung werdenallerdings genau die angegebenen Zusicherungen benutzt.• Bedenken Sie, dass die Regeln des Kalküls syntaktisch sind, weshalb Sie semantische Änderungen(beispielsweise von x + 1 = y + 1 zu x = y) nur unter Zuhilfenahme der Konsequenzregeln vornehmendürfen.4
Programmierung WS11/12<strong>Übungsblatt</strong> 3 (Abgabe 10.11.2011)〈a.length > 0〉res = a[0];〈 〉i = 1;〈 〉〈 〉〈 〉while (i < a.length) {〈 〉〈 〉if (a[i] > res) {〈 〉res = a[i];〈 〉}〈 〉i = i + 1;〈 〉}〈 〉〈 〉〈res = max{ a[j] | 0 ≤ j < a.length }〉b) Untersuchen Sie den Algorithmus P auf seine Terminierung. Für einen Beweis der Terminierung muss eineVariante angegeben werden und unter Verwendung des Hoare-Kalküls die Terminierung unter der Voraussetzunga.length > 0 bewiesen werden.Tutoraufgabe 4 (Vier gewinnt):In dieser Aufgabe sollen Sie das Spiel “Vier Gewinnt” (für zwei Spieler) programmieren. Ziel dieses Spieles ist es,vier eigene Spielsteine in einer Reihe auf dem Spielbrett zu platzieren. Diese Reihe darf waagerecht, senkrechtoder diagonal auf dem Spielbrett liegen. Das Spielbrett ist aufgeteilt in sieben Spalten mit je sechs Zeilen. Die5
Programmierung WS11/12<strong>Übungsblatt</strong> 3 (Abgabe 10.11.2011)beiden Spieler werfen abwechselnd ihre Spielsteine von oben ins Spielbrett, die dann bis in die unterste freie Zeilefallen. In einer vollen Spalte kann natürlich kein Stein eingeworfen werden.Ein Grundgerüst mit einigen bereits implementierten Funktionen finden Sie in der Datei VierGewinnt.java, diesie von der Webseite herunterladen können. Das Spielbrett wird hier mithilfe eines verschachtelten int-Arraysint[][] spielbrett dargestellt. Dabei ist spielbrett[i] die (i + 1)-te Spalte, spielbrett[i][0] der erste(oberste) Eintrag in der Spalte i +1 und spielbrett[i][5] ist der sechste (unterste) Eintrag in Spalte i +1. Hatein Eintrag den Wert 0, befindet sich kein Stein an dieser Position. Ein Eintrag mit Wert 1 entspricht einem Steinvon Spieler 1, ein Eintrag mit Wert 2 einem von Spieler 2. In der vorgegebenen Ausgabefunktion ausgabe(int[][]spielbrett) werden die Steine von Spieler 1 als X und die von Spieler 2 als O auf dem Bildschirm ausgegeben.Als Beispiel können Sie das folgende Spielbrett betrachten:| | | | | | | |+---+---+---+---+---+---+---+| | | | | | | |+---+---+---+---+---+---+---+| | | | | | | |+---+---+---+---+---+---+---+| | | | | | | |+---+---+---+---+---+---+---+| | | X | | | | |+---+---+---+---+---+---+---+| | | O | X | | | |+---+---+---+---+---+---+---+1 2 3 4 5 6 7Hier hat Spieler 2 einen Spielstein in der untersten Zeile von Spalte 3 (also in spielbrett[2][5]) platziert.Spieler 1 hat einen Stein in Spalte 4 (in spielbrett[3][5]) und einen Stein über den von Spieler 2 (inspielbrett[2][4]) fallen lassen.a) Implementieren Sie die Funktion macheZug(int[][] spielbrett, int spieler), die von Spieler spielereinen gültigen Zug einliest und diesen dann dann auf dem Spielbrett umsetzt. Dabei müssen folgende Punktebeachtet werden:1. Das aktuelle Spielbrett soll ausgegeben werden.2. Spieler spieler soll aufgefordert werden, eine Spalte einzugeben, in die ein Spielstein geworfen werdensoll. Die Spalten sollen von 1 bis 7 durchnummeriert werden.<strong>3.</strong> Falls eine ungültige Spalte eingegeben wurde, soll der Fehler gemeldet werden und wieder bei 2. begonnenwerden. Eine Spalte ist ungültig, wenn sie nicht auf dem Feld liegt oder bereits voll ist.4. Der Spielstein soll in das unterste freie Feld in der eingegebenen Spalte fallen.Hinweise:zu 1. Verwenden Sie die vorgegebene Funktion ausgabe(int[][] spielbrett).zu <strong>3.</strong> Vervollständigen Sie die Funktion spalteKorrekt und benutzen Sie sie, um zu überprüfen, ob dieEingabe korrekt ist. Die Anzahl der Spalten im Feld spielbrett ist mit spielbrett.length abfragbar.Wenn die (i + 1)-te Spalte voll ist, ist ihr oberster Eintrag spielbrett[i][0] nicht 0.zu 4. Benutzen Sie eine for-Schleife, um den Spielstein ins Spielbrett “fallen” zu lassen, indem Sie vomuntersten Eintrag spielbrett[i][spielbrett[i].length-1] aus bis zu spielbrett[i][0] iterierenund jeweils überprüfen, ob der aktuelle Eintrag leer ist. Wenn dies der Fall ist, brechen Sie ab und fügeneinen Stein des aktuellen Spielers ein.b) Implementieren Sie nun die main-Funktion. Diese Funktion soll das gesamte Spiel unter Verwendung derFunktion macheZug aus a) und der vorgegebenen Funktion werGewinnt(int[][] spielfeld) ausführen.Dabei gibt werGewinnt das Ergebnis 1 zurück, wenn Spieler 1 auf spielfeld gewonnen hat, 2 wenn Spieler2 gewonnen hat und 0, wenn kein Spieler gewonnen hat. Sie können die folgenden Punkte als Leitfadenverwenden:6
Programmierung WS11/12<strong>Übungsblatt</strong> 3 (Abgabe 10.11.2011)1. Initialisieren Sie das Spielbrett mit der richtigen Größe.2. Benutzen Sie eine int Variable, um zu speichern, welcher Spieler am Zug ist. Zu Beginn ist immer Spieler1 am Zug.<strong>3.</strong> Lassen Sie eine Schleife solange laufen, bis ein Spieler gewonnen hat oder kein Zug mehr möglich ist.Verwenden Sie zum Überprüfen, ob das Spiel beendet ist, werGewinnt. Verwenden Sie macheZug ausTeil a), um einen Zug durchzuführen.4. Hat ein Spieler gewonnen oder ist das Spielbrett voll und das Spiel ist daher unentschieden, so endet dasSpiel und der Gewinner (bzw. “unentschieden”) wird ausgegeben.Aufgabe 5 (Reversi):(1 + 3 + 2 + 4 + 2 = 12 Punkte)In dieser Aufgabe soll das Spiel “Reversi” (auch als “Othello” bekannt), realisiert werden. “Reversi” ist ein Brettspielfür zwei Personen. Das Brett hat 8 × 8 Felder, auf denen Spielmarken verteilt werden können. Jede Spielmarkeist auf der einen Seite weiß und auf der anderen Seite schwarz. Spieler 1 legt Spielmarken stets mit der weißenSeite nach oben und Spieler 2 legt Spielmarken stets mit der schwarzen Seite nach oben. Zum Anfang sind jezwei Steine der beiden Spieler in der Mitte des Spielbretts platziert. Die Spieler können nun abwechselnd einenSpielstein auf das Brett setzen.Falls es bereits eine Reihe von Steinen gibt, bei der auf einen weißen Stein ein oder mehrere schwarze Steinefolgen, so kann Spieler 1 einen neuen weißen Stein neben den letzten schwarzen Stein der Reihe setzen. Dies hatzur Folge, dass alle schwarzen Steine der Reihe, die nun zwischen den weißen Steinen liegen, umgedreht werden.Sie werden dadurch also ebenfalls zu weißen Steinen. Hierbei können Reihen waagerecht, senkrecht oder diagonalsein. Spieler 1 darf nur solche Züge machen, bei denen mindestens ein schwarzer Stein umgedreht wird. Analogverhält es sich natürlich bei Zügen von Spieler 2. Kann ein Spieler keinen Zug tätigen, ist der andere Spieler erneutan der Reihe. Betrachten Sie dazu die beiden folgenden Beispiele:1 2 3 4 5 6 7 81 2 3 4 5 6 7 81 12 23 34 B 45 A 56 67 78 81 12 23 34 45 56 67 78 81 2 3 4 5 6 7 81 2 3 4 5 6 7 8Auf dem linken Spielbrett können Sie die Startposition sehen. Weiß ist am Zug. Es ist nicht möglich, einen Steinauf Position A zu setzen, da hier zwar ein schwarzer Stein angrenzt (in Feld (4,5)), aber kein schwarzer Steindurch zwei weiße Steine eingeschlossen wird. Andererseits ist es möglich, einen Stein auf die Position B zu setzen.In diesem Fall wird der eingeschlossene schwarze Stein auf dem Feld (4,5) umgedreht (d.h., er verwandelt sich ineinen weißen Stein). Das Spielfeld nach diesem Zug ist rechts zu sehen.Das Spiel endet, wenn beide Spieler keinen Zug mehr tätigen können (z.B. weil das Spielbrett voll ist). Gewinnerist der Spieler, dem die Mehrheit der Steine auf dem Spielbrett gehört.Sie benötigen als Grundlage das Grundgerüst Reversi.java und die Datei ReversiSecret.class von unsererWebseite. Wenn Sie Reversi.java kompilieren, haben Sie bereits eine lauffähige Version des Spiels zur Verfügung.Ihre Aufgabe ist es, einige Aufrufe von Funktionen aus der Klasse ReversiSecrect durch Ihren eigenen Code zuersetzen. Lesen Sie die Aufgabenstellung zunächst ganz durch und entscheiden Sie dann, in welcher ReihenfolgeSie die Teilaufgaben angehen.Wie in der Tutoraufgabe zu “Vier Gewinnt” stellen wir das Spielbrett als verschachteltes int-Array spielbrettdar. Hierbei ist spielbrett[i][j] das Feld in der (i + 1)-ten Zeile und (j + 1)-ten Spalte. Hat der Eintragspielbrett[i][j] den Wert 0, so ist das Feld frei; ist er 1, ist das Feld durch einen Stein von Spieler 1 belegtund 2 markiert einen Stein von Spieler 2.Die folgenden Teile sollen Sie nun selber programmieren:7
Programmierung WS11/12<strong>Übungsblatt</strong> 3 (Abgabe 10.11.2011)a) initSpielbrett: Ersetzen Sie die Anweisungint[][] spielbrett = ReversiSecret.initSpielbrett();in der Methode main. Hier sollen Sie ein neues int[][]-Array spielbrett erstellen, das das Spielbrett inder Ausgangssituation darstellt.Alle Felder des Spielbretts sollen frei sein, nur die vier Felder in der Mitte sollen (wie oben im linken Beispieldargestellt) mit Spielsteinen belegt sein. Dabei soll das Feld in der 4. Zeile und 4. Spalte und das Feld in der5. Zeile und 5. Spalte einen Stein von Spieler 1 enthalten. Entsprechend soll das Feld in der 4. Zeile und 5.Spalte und das Feld in der 5. Zeile und 4. Spalte einen Stein von Spieler 2 enthalten.b) zaehleSteine: Ersetzen Sie die Anweisungint anzahl = ReversiSecret.zaehleSteine(spielbrett, spieler);in der Funktion zaehleSteine. Das Ziel ist, die Variable anzahl auf die Zahl der Steine des Spielers spielerauf dem Spielbrett spielbrett zu setzen. Laufen Sie dafür jedes Feld auf dem Spielbrett spielbrett abund inkrementieren Sie einen Zähler, wenn das Feld durch einen Stein von Spieler spieler belegt ist.c) setzeSteine: Ersetzen Sie die AnweisungReversiSecret.setzeSteine(spielbrett, felderZuSetzen, spieler);in der Funktion macheZug. Hier sollen Sie jedes Element von felderZuSetzen einzeln als Position auf demSpielfeld betrachten und das entsprechende Feld mit einem Stein des Spielers spieler besetzen.Die Elemente von felderZuSetzen sind zwei-elementige int-Arrays, bei denen das erste Element jeweilsdie Zeile und das zweite Element die Spalte des zu setzenden Feldes beschreibt. Beachten Sie dabei, dassdie Werte hier bereits Array-Indizes sind, die erste Spalte bzw. Zeile hat also den Index 0. So beschreibt z.B.das Array {{3,3}, {3,4}, {3,5}, {4,4}} alle Positionen weißer Steine auf dem Spielbrett des rechtenobigen Beispiels. Das 2-elementige int-Array {3,3} steht hier also für das Feld in der 4. Zeile und Spalte.d) zuegeMoeglich: Ersetzen Sie die Anweisungboolean zuegeMoeglich = ReversiSecret.zuegeMoeglich(spielbrett, spieler);in der Funktion zuegeMoeglich. Hier sollen Sie überprüfen, ob Spieler spieler auf irgendeinem Feld desSpielbretts einen Stein setzen kann. Betrachten Sie dazu alle Felder des Spielfeldes einzeln. Das Ergebnissoll am Ende in der Variablen zuegeMoeglich gespeichert sein, die true sein soll, wenn Spieler spielerirgendeinen Zug tätigen kann und sonst false sein soll.Sie dürfen hier die Funktion Reversi.felderZuSetzen(int[][] spielbrett, int zeilId, int spId,int spieler) verwenden. Diese liefert ein Array wie in Teilaufgabe c) zurück, dessen Elemente zweielementigeint-Arrays sind. Es gibt die Felder an, die auf dem Spielbrett spielbrett mit Steinen desSpielers spieler besetzt werden müssen, wenn ein Stein von spieler in die (zeilIdx + 1)-te Zeile und(spIdx + 1)-te Spalte gesetzt wird. Das zurückgelieferte Array enthält dann nicht nur die Positionen der zudrehenden Steine des anderen Spielers, sondern auch die Positionen des neu gesetzten Steins von spieler.Ist das von felderZuSetzen zurückgelieferte Array leer, so ist der Zug nicht möglich.Stellt spielbrett das Spielbrett unseres linkes obigen Beispiels dar, ist die Position B als {3, 5} darstellbar.Dann ergibt der Aufruf Reversi.felderZuSetzen(spielbrett, 3, 5, 1) das Array {3,4}, {3,5}}.Für Position A ({4,5}) würde der Aufruf das leere Array {} ergeben, weil der Zug nicht möglich ist.e) frageZugAb: Ersetzen Sie die Anweisungint[][] felderZuSetzen = ReversiSecret.frageZugAb(spielbrett, spieler);in der Funktion macheZug. Hier sollen Sie zuerst nach einer Zeile und dann nach einer Spalte fragen,an der Spieler spieler einen Stein setzen möchte. Überprüfen Sie dann wie in Teilaufgabe d) mittelsReversi.felderZuSetzen, ob der eingegebene Zug möglich war. Ist dies der Fall, soll der Rückgabewertvon Reversi.felderZuSetzen verwendet werden, um an setzeSteine weitergegeben zu werden (vgl.Teilaufgabe c)). War die Eingabe kein erlaubter Zug, soll dies ausgegeben werden und erneut nach einemZug gefragt werden.Hinweise:• Testen Sie nach jeder Teilaufgabe, ob Ihre Version (bei der Sie Aufrufe von Methoden der KlasseReversiSecret durch eigenen Code ersetzt haben) sich noch verhält wie die ursprüngliche Version.8