Game of Life mit Scala – das Tutorial
Swing-Oberfläche
Nun sind wir fast am Ziel angelangt. Aber natürlich brauchen wir noch eine grafische Oberfläche, denn es ist unglaublich faszinierend, der Entwicklung von Generationen zuzuschauen. Da es hier um ein Tutorial geht und nicht um eine mächtige Visualisierung für das Game of Life, beschränken wir uns auf eine einfache Oberfläche, dargestellt in Abbildung 2, mit der folgenden Funktionalität:
- Wir stellen nur einen begrenzten Ausschnitt des unendlichen Spielfeldes dar
- Mit der Maus können wir jederzeit den Status der dargestellten Zelle ändern
- Mit einem Button gelangen wir zur nächsten Generation

Wir wollen alleine mit Scala-Bordmitteln auskommen. Daher verwenden wir die Scala-Swing-Bibliothek [4], die die Arbeit mit Swing deutlich vereinfacht. Leider können wir hier nicht mehr im Detail auf die Implementierung eingehen, die in Listing 4 gezeigt ist. Das würde trotz der Knappheit des Codes den Rahmen dieses Tutorials sprengen, insbesondere weil einige Konzepte angewendet werden, die weit über das Einstiegsniveau hinausgehen. Vielmehr wollen wir im Folgenden ein paar Highlights herauspicken.
Zunächst sehen wir, wie einfach wir eine Swing-Anwendung erstellen können: Wir erweitern einfach SimpleSwingApplication und überschreiben die top-Methode. Komponenten bauen wir auf, indem wir zum Feld contents weitere Komponenten hinzufügen. Events behandeln wir, indem wir für Komponenten das Feld reactions überschreiben und so genannte Reactions hinterlegen. Actions können wir direkt mit einem Button verknüpfen, indem wir das Singleton Object Button „aufrufen“ und den Code für die Action als Argument übergeben. Dann begegnen uns einige Konzepte wieder, die wir schon in den vorigen Schritten angewendet haben, z. B. eine For Comprehension, um das begrenzte Spielfeld aufzubauen, oder eine Funktion, die als Parameter übergeben wird, um zu bestimmen, ob eine Zelle lebendig oder tot ist.
Auch wenn die Klasse SwingUI insgesamt über das hinausgeht, was wir bisher gelernt haben, so können wir doch einen Eindruck davon gewinnen, was mit Scala im Allgemeinen und mit der Scala-Swing-Bibliothek im Speziellen möglich ist.
Fazit
Wir haben in diesem Tutorial viele wichtige Scala-Grundlagen kennengelernt, sowohl im Hinblick auf die Sprache selbst, als auch betreffend Werkzeugen wie die REPL und SBT. Hoffentlich konnten wir durch das praktische Beispiel vermitteln, dass Scala nicht nur knappen (z. B. Cell) und gut verständlichen (z. B. CellSpec) Code möglich macht, sondern durch den funktionalen Charakter oft auch sehr einfache Lösungen (z. B. Generation.aliveNeighbours) erlaubt. Selbstverständlich freuen wir uns über Rückmeldungen, sei es in Form von Fragen, Bug-Reports (bitte über den Issue Tracker [5]) oder Ideen, wie wir das Tutorial verbessern oder ausbauen können. Viel Spaß mit Scala!
Listing 4: SwingUI.scala
package com.weiglewilczek.gameoflife import java.awt.Color import javax.swing.border.LineBorder import scala.swing._ import scala.swing.event.MouseClicked object SwingUI extends SimpleSwingApplication { override def startup(args: Array[String]) { dimension = args.headOption map { arg => try { arg.toInt } catch { case _ => DefaultDimension } } getOrElse DefaultDimension super.startup(args) } override def top = new MainFrame { title = "Game of Life" resizable = false contents = new BoxPanel(Orientation.Vertical) { contents += new GenerationPanel(new Generation, update(contents(0) = _)) contents += Button("Next") { val next = contents(0).asInstanceOf[GenerationPanel].generation.next update(contents(0) = _)(next) } } private def update(swap: GenerationPanel => Unit)(generation: Generation) { swap(new GenerationPanel(generation, update(swap))) pack() } } private class GenerationPanel( val generation: Generation, update: Generation => Unit) extends GridPanel(dimension, dimension) { contents appendAll cells private lazy val cells = { def cell(cell: Cell, alive: Cell => Boolean) = { new FlowPanel { border = LineBorder.createBlackLineBorder background = if (alive(cell)) Color.BLUE else Color.WHITE contents += new Label("") listenTo(mouse.clicks) reactions += { case e: MouseClicked => { val aliveCells = if (alive(cell)) generation.aliveCells - cell else generation.aliveCells + cell update(new Generation(aliveCells)) } } } } for { y
Hinterlasse einen Kommentar