Last active
December 12, 2015 06:08
-
-
Save rladstaetter/4726641 to your computer and use it in GitHub Desktop.
Conway's Game of Life - in Scala with JavaFX
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.ladstatt.apps | |
import scala.collection.JavaConversions.asScalaBuffer | |
import scala.collection.JavaConversions.seqAsJavaList | |
import scala.util.Random | |
import javafx.animation.Animation | |
import javafx.animation.KeyFrame | |
import javafx.animation.Timeline | |
import javafx.application.Application | |
import javafx.collections.ObservableList | |
import javafx.event.ActionEvent | |
import javafx.event.Event | |
import javafx.event.EventHandler | |
import javafx.scene.Group | |
import javafx.scene.Node | |
import javafx.scene.Scene | |
import javafx.scene.input.MouseEvent | |
import javafx.scene.paint.Color | |
import javafx.scene.paint.CycleMethod | |
import javafx.scene.paint.LinearGradient | |
import javafx.scene.paint.Stop | |
import javafx.scene.shape.Rectangle | |
import javafx.stage.Stage | |
import javafx.util.Duration | |
/** | |
* John Conway's Game of Life - with JavaFX and Scala | |
*/ | |
object GameOfLife { | |
def main(args: Array[String]): Unit = { | |
Application.launch(classOf[GameOfLife], args: _*) | |
} | |
} | |
trait JfxUtils { | |
def mkEventHandler[E <: Event](f: E => Unit) = new EventHandler[E] { def handle(e: E) = f(e) } | |
} | |
class GameOfLife extends javafx.application.Application with JfxUtils { | |
val canvasWidth = 800 | |
val canvasHeight = 600 | |
val cellCount = 40 | |
val lifeSpeed = 2 | |
// -------------------------------------------------------------------------- | |
val (width, height) = ((canvasWidth / cellCount) - 2, (canvasHeight / cellCount) - 2) | |
case class Cell(x: Int, y: Int, alive: Boolean = false) extends Rectangle { | |
setUserData(alive) | |
setX(x * (width + 2)) | |
setY(y * (height + 2)) | |
setWidth(width) | |
setHeight(height) | |
setArcWidth(2) | |
setArcHeight(2) | |
paint | |
setOnMousePressed(mkEventHandler((e: MouseEvent) => { killOrResurrect })) | |
def paint = if (isAlive) setFill(Color.RED) else setFill(Color.WHITESMOKE) | |
def isAlive: Boolean = getUserData().asInstanceOf[Boolean] | |
def killOrResurrect = { | |
setUserData(!isAlive) | |
paint | |
} | |
} | |
override def start(stage: Stage): Unit = { | |
stage.setTitle("Conway's Game of Life") | |
val drawingArea = new Group() | |
val background = { | |
val b = new Rectangle(0, 0, canvasWidth, canvasHeight) | |
val stops = List(new Stop(0, Color.BLACK), new Stop(1, Color.WHITESMOKE)) | |
val g = new LinearGradient(0.0, 1.0, 0.0, 0.0, true, CycleMethod.NO_CYCLE, stops) | |
b.setFill(g) | |
b | |
} | |
val cells = new Group() | |
cells.getChildren().addAll(for { | |
x <- 0 to cellCount | |
y <- 0 to cellCount | |
} yield Cell(x, y, Random.nextBoolean)) | |
drawingArea.getChildren.addAll(background, cells) | |
val growTimeline = new Timeline | |
growTimeline.setRate(lifeSpeed) | |
growTimeline.setCycleCount(Animation.INDEFINITE) | |
growTimeline.getKeyFrames().add( | |
new KeyFrame(Duration.seconds(1), | |
new EventHandler[ActionEvent]() { | |
def handle(event: ActionEvent) { | |
val nextGen = nextGeneration((convert2CellList(cells.getChildren()))) | |
cells.getChildren().clear() | |
cells.getChildren.addAll(nextGen) | |
} | |
})) | |
growTimeline.play() | |
stage.setScene(new Scene(drawingArea, canvasWidth, canvasHeight)) | |
stage.show() | |
} | |
def nextGeneration(allCells: List[Cell]): List[Cell] = { | |
for (c <- allCells) yield { | |
if (c.isAlive) { | |
getAliveNeighbors(c, allCells).size match { | |
case 1 => c.copy(alive = false) | |
case 2 => c | |
case 3 => c | |
case _ => c.copy(alive = false) | |
} | |
} else { | |
if (getAliveNeighbors(c, allCells).size == 3) | |
c.copy(alive = true) | |
else c | |
} | |
} | |
} | |
def getAliveNeighbors(c: Cell, cells: List[Cell]) = getNeighbors(c, cells).filter(_.isAlive) | |
def convert2CellList(nodes: ObservableList[Node]): List[Cell] = { | |
(for ( | |
n <- nodes if (n match { | |
case c: Cell => true | |
case _ => false | |
}) | |
) yield n.asInstanceOf[Cell]).toList | |
} | |
def getNeighbors(cell: Cell, cells: List[Cell]) = | |
cells.filter(c => c != cell && (scala.math.abs(cell.x - c.x) <= 1 && scala.math.abs(cell.y - c.y) <= 1)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment