Banshee's stuff

An aleph-zero potpourri blog...

Life

| Comments

Or the game of life written in Scala with a little bit of Swing for the gui.

Let’s explore this code a little bit…

The basic idea is to visualize a Set[Coord] instances on the graphical grid and obtain, with every iteration, the new Set of alive cells.

In order to do that let’s define our beginning state inside the Life main object and create an iterable element using Iterator.iterate method..

1
2
3
4
5
6
object Life extends SimpleSwingApplication {
  val startData: Set[Coord] = Set(/* initial state */)
  val it = Iterator.iterate(Generation(startData))(_.nextGeneration)
  // setup swing window components and 
  // a runnable element to use as an "updater"
}

The Generation class is a extended Set[Coord] which is able to obtain the nextGeneration concatenating (still) alive cells with newborn cells

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Generation(val alive: Set[Coord]) extends Set[Coord] {
  // Set contract methods...

  // get all neighbors for every cell, group by cell
  // and get a tuple with Coord and neighbors count
  private val neighbors = alive.toList flatMap(_.neighbors) groupBy (identity) map { case (c, l) => (c, l.size) }
  // define a generic method to obtain a filtered selection of cells
  private def neighborhood(filter: Filter) = for (filter(coord) <- neighbors) yield coord
  // obtain new born cells 
  // (ie the ones that meet newborn filter requirements)
  private def babies = neighborhood(newborn)
  // obtain current alive cells, watch out for the "alive &" stuff...
  private def adults = alive & neighborhood(stable).toSet
  // concatenate adults with babies
  def nextGeneration = Generation(adults ++ babies)
}

Filters are defined in Generation Object as

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
object Generation {
  // case class already defines an apply() method so
  // let's only define an unapply method which
  // accepts a function((Coord, Int)) to Option[Coord]
  // as filtering logic
  case class Filter(f: ((Coord, Int)) => Option[Coord]) {
    def unapply(t: (Coord, Int)): Option[Coord] = f(t)
  }
  def apply(alive: Set[Coord]) = new Generation(alive)

  // if a cell has 3 neighbors it's ok for a new birth
  val newborn = Filter {
    case (c, 3) => Some(c)
    case _ => None
  }
  // 2 or 3 neighbors are ok...
  val stable = Filter {
    case (c, 2) => Some(c)
    case (c, 3) => Some(c)
    case _ => None
  }
}

The last interesting stuff is the implicit conversion from Tuple to Coord objects

1
  implicit def tupleToCoord(t: (Int, Int)): Coord = apply(t._1, t._2)

Now add some graphic code and here we are:

Update (04 feb): code is now stored on github

Comments