Conway’s Game of Life in Scala

I attended a Code Retreat yesterday in Hamburg. As usual we used Conway’s Game of Life to practice our coding skills. I planned to do at least one session using Scala, but my Scala IDE wasn’t working any more, probably due to an incompatible upgrade of some description.

So, today I took the time to get my IDE up to scratch and implemented the game of life in Scala. It just had to be done. And since no-one forced me to delete my code after 45 minutes (as it’s common at code retreats) I actually got to finish it. Including console output. Bonus.

object GameOfLife extends App {
  var civilization = new Civilization(List((3, 1), (3, 2), (3, 3), (2, 3), (1, 2)), 10)

  civilization.printGrid
  for (period <- 1 to 100) {
    aGenerationPasses
    civilization = civilization.tick
    civilization.printGrid
  }

  def aGenerationPasses = synchronized {
    wait(100)
  }
}

class Civilization(seed: List[(Int, Int)], size: Int) {
  private val STAY_ALIVE = 2
  private val BE_BORN = 3
  private val grid = new Array[Array[Boolean]](size, size)
  seed foreach (cell => containsCellAt(cell._1, cell._2))

  def tick = {
    val nextGeneration = new Civilization(Nil, size)
    for (row <- 0 until size; col <- 0 until size)
      if (isCellAt((row, col)) && numberOfNeighboursFor(row, col) == STAY_ALIVE
          || numberOfNeighboursFor(row, col) == BE_BORN)
        nextGeneration.containsCellAt(row, col)
    nextGeneration
  }

  private def numberOfNeighboursFor(row: Int, col: Int): Int =
    areaAround(row, col) map wrap filter isCellAt length

  private def areaAround(row: Int, col: Int): List[(Int, Int)] =
    List((row-1, col-1), (row,col-1), (row+1,col-1), (row-1,col), (row+1,col), (row-1,col+1), (row,col+1), (row+1,col+1))

  private def wrap(cell: (Int, Int)): (Int, Int) =
    (((cell._1 + size) % size), ((cell._2 + size) % size))

  private def isCellAt(cell: (Int, Int)): Boolean =
    grid(cell._1)(cell._2)

  private def containsCellAt(row: Int, col: Int) =
    grid(row)(col) = true

  def printGrid = {
    println
    grid foreach {
      row => row foreach {
        cell => if (cell) print("*") else print(".")
      }
      println
    }
  }
}

The GameOfLife object runs the game by creating an initial civilization and then looping through a fixed number of generations, printing each of them along the way.

The civilization creates its next generation upon call of the tick method. This happens by applying (a simplified version of) the four rules of the game to each spot on the grid. Since I initialize the next generation with no cells, I only care about rules 2 and 4 that deal with alive cells.

gameofliferules1

I decided to wrap the grid at the edges. So objects leaving the grid on the right don’t disappear, but re-enter on the left instead. This happens in the wrap method.

The numberOfNeighboursFor method is a bit magical. There is 6 method calls in a one-liner. Surely, people who are used to functional language list comprehensions can appreciate this. Read: In the area around the given cell coordinates (1) for each spot apply (2) the wrapping (3) then filter (4) by spots that contain a cell (5) and finally give me the length of the resulting list (6). Voilà.

I am not entirely happy about the use of a nested Array for the grid and the for loop in the tick method, but I couldn’t come up with a better solution. Suggestions welcome.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s