Prof. Dr. Manfred Schmidt-Schauß Institut für Informatik Fachbereich Informatik und Mathematik/ Institut für Informatik Johann Wolfgang Goethe-Universität Frankfurt am Main Einführung in die funktionale Programmierung Wintersemester 2013/2014 Aufgabenblatt Nr. 4 Abgabe: Montag 11. November 2013 vor der Vorlesung Senden Sie bitte Ihren Quellcode auch per Email an [email protected] Ziel dieses Aufgabenblattes ist es eine einfache Variante von Conways Game of Life“ in Haskell ” zu implementieren1 . Dabei sind Teile des Codes schon vorgegeben. Die meisten zu implementierenden Funktionen sind Funktionen auf Listen in Haskell. Game of Life: Das Spielfeld des Game of Life ist eine quadratische Matrix, wobei jeder Eintrag eine Zelle darstellt, die entweder bewohnt oder unbewohnt ist. In Abbildungen 1 (a) bis (d) sind solche Matrizen zu sehen, wobei die gefüllten Zellen, die bewohnten Zellen sind. Ein Eintrag mit Index (i, j) in der Matrix bezeichnet die Zelle in Zeile i und Spalte j, wobei von 0 angefangen wird zu zählen. Ein Beispiel zur Indizierung zeigt Abbildung 1 (e). (0,0) (0,1) (0,2) (1,0) (1,1) (1,2) (2,0) (2,1) (2,2) (a) (b) (c) (d) Abbildung 1: Verschiedene Matrizen (e) Beim Game of Life wird aus der aktuellen Generation k (also die Belegung einer Matrix) die nächste Generation k + 1 (eine neue Matrix) berechnet. Dabei werden die folgenden Regeln verwendet, die die Nachbarn der Zelle betrachten. Nachbarn sind alle Zellen ringsherum um die Zelle (auch diagonal). Ist eine Zelle (i, j) in Generation k bewohnt, so ist sie in Generation k + 1: • bewohnt, wenn genau zwei oder genau drei Nachbarzellen in Generation k bewohnt sind. • unbewohnt, anderenfalls (Aussterben wegen Einsamkeit oder Überbevölkerung). Ist eine Zelle (i, j) in Generation k unbewohnt, so ist sie in Generation k +1 genau dann bewohnt, wenn genau drei Nachbarzellen bewohnt sind (Wiederbelebung). Abbildung 1 (b) zeigt die Nachfolgegeneration der Matrix in Abbildung 1 (a) und Abbildung 1 (d) zeigt die Nachfolgegeneration der Matrix in Abbildung 1 (c). Modellierung in Haskell: Das Spielfeld wird durch den Datentyp Matrix im (Modul GameOfLife) modelliert: data Matrix = Matrix [[Bool]] Die Liste von Listen von Booleschen Werten repräsentiert die Liste der Zeilen der Matrix, wobei jede Zeile eine Liste von Booleschen Werten ist. Ist ein Eintrag True, so ist die Zelle bewohnt, und ist der Eintrag False, so ist die Zelle unbewohnt. Z.B. stellt Matrix [[True,False,False],[False,True,False],[False,False,True]] die Matrix in Abbildung 1 (a) dar. Das Modul GameOfLife enthält bereits eine Show-Instanz für Matrix, um das Spielfeld gut lesbar auszudrucken. 1 Der hier verwendete Quellcode ist auch auf der Webseite der Veranstaltung zu finden. 1 Aufgabe 1 (30 Punkte) Implementieren Sie im Modul GameOfLife a) eine Funktion (!!!) :: Matrix -> (Int, Int) -> Bool, die eine Matrix und einen Index als Eingabe erwartet und die Belegung des durch den Index gegebenen MatrixEintrages berechnet. (4 Punkte) b) eine Funktion nachbarn :: (Int, Int) -> Int -> [(Int, Int)] mit Hilfe einer ListComprehension, die einen Index und die Dimension einer Matrix erwartet und die Positionen aller benachbarten Felder berechnet. Beispielsweise liefert nachbarn (1,1) 3 als Resultat die Liste [(0,0),(0,1),(0,2),(1,0),(1,2),(2,0),(2,1),(2,2)]. (8 Punkte) c) eine Funktion anzahlBewohnterNachbarn :: (Int, Int) -> Matrix -> Int, die für einen Index und eine Matrix, die Anzahl der bewohnten Nachbarn der entsprechenden Zelle berechnet. Z.B. liefert der Aufruf von anzahlBewohnterNachbarn für Index (1,1) und der Matrix aus Abbildung 1 (a) den Wert 2. (8 Punkte) d) eine Funktion naechsteGeneration :: Matrix -> Matrix, die für eine gegebene Generation die Nachfolgegeneration gemäß der o.g. Spielregeln berechnet. (10 Punkte) Aufgabe 2 (10 Punkte) Game-of-Life-Spielfelder können wiefolgt als String dargestellt werden: • Die Zeilen der Matrix (Matrixzeilen) sind Zeilen des Strings • Matrixzeilen enthalten ausschließlich die Zeichen ’.’ und ’*’, wobei ’.’ eine unbewohnte Zelle und ’*’ eine bewohnte Zelle darstellt. • Kommentarzeilen beginnen mit dem Zeichen ’#’, diese Zeilen sind keine Matrixzeilen, sondern sind zu ignorieren. Leere Zeilen sind genau wie Kommentarzeilen zu ignorieren. • Alle bewohnten Zellen sind explizit durch das Zeichen ’*’ gegeben, während nicht alle unbewohnten Zellen explizit angegeben werden müssen, denn zu kurze Zeilen werden mit unbewohnten Zellen am Ende aufgefüllt, fehlende Zeilen werden durch Zeilen bestehend aus unbewohnten Zellen gefüllt. • Die Dimension der Matrix berechnet sich aus dem Maximum der Anzahl der Matrixzeilen und der Länge der längsten Matrixzeile. Z.B. ist "\n\n#Ein Kommentar\n**..*\n*.*\n.***" eine mögliche Repräsentation für die in Abbildung 1 (c) gezeigte Matrix. Implementieren Sie im Modul GameOfLife die Funktion parseInput :: String -> Matrix, die einen String obiger Form erhält und diesen in eine Matrix konvertiert. 2 Aufgabe 3 (10 Punkte) Im Modul Main finden Sie eine Implementierung zur Anzeige der Game of Life-Welt vor. Hierbei handelt es sich um ein Konsolenprogramm, dass nach Kompilieren sowohl die Möglichkeit bietet, das Spiel in einem OpenGL-Fenster oder auf der Konsole auszudrucken. Für die Verwendung der OpenGL-Ausgabe muss die Gloss-Bibliothek installiert sein2 . Das Konsolenprogramm erwartet neben dem Dateinamen, welche die Matrix in der Darstellung aus der vorherigen Ausgabe enthält, optionale Parameter. Diese können sein: -?, -h, --help zum anzeigen eines Hilfetexts, --speedZahl zur Anpassung der Simulationsgeschwindigkeit, --spaceZahl, um die Matrix in allen vier Richtungen um Zahl viele unbewohnte Zellen zu vergrößern, und --text um die Konsolenausgabe anstelle der Grafikausgabe zu verwenden. Implementieren Sie im Modul Main die Funktion hilfetextAnzeigen :: [String] -> Bool, die die Liste der übergebenen Parameter erhält und entscheidet, ob der Hilfetext angezeigt werden soll. Das letzte Element der Eingabe ist dabei der Dateiname (falls einer angegeben wurde). Der Hilfetext soll in folgenden Fällen angezeigt werden: • in den Parametern befindet sich "-h", "--help" oder "-?" • Die Liste der Parameter ist leer • Nicht alle Parameter (außer dem letzten) sind von der Form -h, --help, -?, --speedZahl, --spaceZahl, --text, wobei Zahl ein beliebiger String ohne Leerzeichen ist. Anschließend können Sie die Module kompilieren mit: ghc -O2 -threaded -o gol Main.hs und das Konsolenprogramm mit ./gol dateiname (Linux / OS X) bzw. gol.exe dateiname (Windows) starten und mit den verschiedenen Parametern und Eingabedateien testen. 2 Durch Eingabe von cabal install gloss in eine Konsole sollte sich diese installieren lassen. 3