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 2014/2015 Aufgabenblatt Nr. 4 Abgabe: Montag 10. November 2014 vor der Vorlesung Senden Sie bitte Ihren Quellcode auch per Email an [email protected] Ziel dieses Aufgabenblattes ist es eine Variante von Conways Game of Life“ in Haskell zu im” plementieren1 . Dabei sind Teile des Codes schon vorgegeben. Die meisten zu implementierenden Funktionen sind Funktionen auf Listen in Haskell. Das Spiel: Das Spielfeld ist eine quadratische Matrix, wobei jeder Eintrag eine Zelle darstellt, die entweder unbewohnt oder mit einer Blaukappe oder einer Rotmütze bewohnt ist. In Abbildungen 1 (a) bis (d) sind solche Matrizen zu sehen, wobei mit Rotmütze bewohnt mit Blaukappe bewohnt unbewohnt 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 Spiel wird aus der aktuellen Generation k (die Belegung einer Matrix) die nächste Generation k + 1 (eine neue Matrix) nach folgenden Regeln berechnet,welche die Nachbarn der Zelle betrachten. Nachbarn sind alle Zellen ringsherum um die Zelle (auch diagonal). Ist eine Zelle (i, j) in Generation k mit einer Blaukappe oder einer Rotmütze bewohnt, so ist sie in Generation k + 1: • bewohnt, wenn genau zwei oder genau drei Nachbarzellen (mit Blaukappen und Rotmützen) in Generation k bewohnt sind. Dabei bleibt eine Blaukappe eine Blaukappe und eine Rotmütze eine Rotmütze außer in den Fällen – Aus einer Blaukappe wird eine Rotmütze, wenn alle 3 Nachbarn Rotmützen sind. – Aus einer Rotmütze wird eine Blaukappe, wenn alle 3 Nachbarn Blaukappen 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 (durch Blaukappen und Rotmützen) bewohnt sind (Wiederbelebung), wobei die entstehende Zelle eine Blaukappe ist, wenn es in Generation k mehr Rotmützen als Blaukappen unter den Nachbarn gab, und eine Rotmütze ist, wenn es in Generation k mehr Blaukappen als Rotmützen unter den Nachbarn gab. 1 Der hier verwendete Quellcode ist auch auf der Webseite der Veranstaltung zu finden. 1 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 [[Zelle]] Die Liste von Listen von Zellen repräsentiert die Liste der Zeilen der Matrix, wobei jede Zeile eine Liste von Werten des Typs Zelle ist, der definiert ist als: data Zelle = Blaukappe | Rotmuetze | Leer Z.B. stellt Matrix [[Rotmuetze,Leer,Leer],[Leer,Blaukappe,Leer],[Leer,Leer,Rotmuetze]] 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. Aufgabe 1 (35 Punkte) Implementieren Sie im Modul GameOfLife a) eine Funktion (!!!) :: Matrix -> (Int, Int) -> Zelle, 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) Funktionen – anzahlRoteNachbarn :: (Int, Int) -> Matrix -> Int, – anzahlBlaueNachbarn :: (Int, Int) -> Matrix -> Int und – anzahlBewohnterNachbarn :: (Int, Int) -> Matrix -> Int, die für einen Index und eine Matrix, die Anzahl der roten, blauen und aller bewohnten Nachbarn der entsprechenden Zelle berechnet. Z.B. liefert der Aufruf von anzahlBewohnterNachbarn für Index (1,2) und der Matrix aus Abbildung 1 (c) den Wert 4, während anzahlBlaueNachbarn und anzahlRoteNachbarn jeweils den Wert 2 liefern. (9 Punkte) d) eine Funktion naechsteGeneration :: Matrix -> Matrix, die für eine gegebene Generation die Nachfolgegeneration gemäß der o.g. Spielregeln berechnet. (14 Punkte) Aufgabe 2 (15 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 ’.’, B und ’R’, wobei ’.’ eine unbewohnte Zelle, ’B’ eine durch eine Blaukappe und R eine durch eine Rotmütze 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. 2 • Alle bewohnten Zellen sind explizit durch eines der Zeichen ’B’ oder ’R’ 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# (c)\nBR..R\nR.B..\n.BRB.\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. Animation Im Modul Main finden Sie eine Implementierung zur Anzeige der Spiel-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. Sie können 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