package net.ilbanshee.gol import swing._ import java.awt.Color import java.awt.Dimension import scala.collection.immutable.HashSet import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import java.util.concurrent.TimeUnit._ object Life extends SimpleSwingApplication { val startData: Set[Coord] = Set( (2,6),(3,6),(2,7),(3,7), (12,6),(12,7),(12,8),(13,5),(13,9),(14,4),(15,4),(14,10),(15,10),(16,7), (17,5),(17,9),(18,6),(18,7),(18,8),(19,7), (22,6),(22,5),(22,4),(23,6),(23,5),(23,4),(24,3),(24,7),(26,7),(26,8),(26,3),(26,2), (36,4),(37,4),(36,5),(37,5)) val it = Iterator.iterate(Generation(startData))(_.nextGeneration) val board = new Board def top = new MainFrame { title = "Game of Life" contents = board } val marketDataRequest = new java.lang.Runnable { def run() = board.updateStatus(it.next) } val es = Executors.newScheduledThreadPool(1); es.scheduleAtFixedRate(marketDataRequest, 0, 130, MILLISECONDS) } class Board extends Component { var status: Set[Coord] = new HashSet[Coord] preferredSize = new Dimension(500, 250) override def paintComponent(g: Graphics2D) = { super.paintComponent(g) g.setColor(new Color(100, 100, 100)) for (i <- 0 to size.width / 10) g.drawLine(i * 10, 0, i * 10, size.height) for (i <- 0 to size.height / 10) g.drawLine(0, i * 10, size.width, i * 10) g.setColor(Color.red) g.drawString("There are %d cells alive".format(status.size), 10, size.height - 10) g.setColor(Color.green) for (coord <- status) { val x = coord.x * 10 - 9 val y = coord.y * 10 - 9 g.fillRect(x, y, 9, 9) } } def updateStatus(newCoords: Set[Coord]) = { status = newCoords repaint } } class Coord(val x: Int, val y: Int) { private val offsets = List(-1, 0, 1) private def offsetsOf(n: Int) = offsets map (_ + n) def neighbors = for { xn <- offsetsOf(x) yn <- offsetsOf(y) if (x, y) != (xn, yn) } yield Coord(xn, yn) override def equals(other: Any) = other match { case c: Coord => x == c.x && y == c.y case _ => false } override def hashCode = ((x * 31) + y) * 61 } object Coord { implicit def tupleToCoord(t: (Int, Int)): Coord = apply(t._1, t._2) private val board = new scala.collection.mutable.HashMap[(Int, Int), Coord] def apply(x: Int, y: Int) = board getOrElseUpdate ((x, y), new Coord(x, y)) } class Generation(val alive: Set[Coord]) extends Set[Coord] { import Generation._ def contains(elem: Coord): Boolean = alive contains elem def iterator: Iterator[Coord] = alive.iterator def +(elem: Coord): Generation = if (alive contains elem) this else Generation(alive + elem) def -(elem: Coord): Generation = if (alive contains elem) Generation(alive - elem) else this override def hashCode = alive.hashCode private val neighbors = alive.toList flatMap(_.neighbors) groupBy (identity) map { case (c, l) => (c, l.size) } private def neighborhood(filter: Filter) = for (filter(coord) <- neighbors) yield coord private def babies = neighborhood(newborn) private def adults = alive & neighborhood(stable).toSet def nextGeneration = Generation(adults ++ babies) } object Generation { 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) val newborn = Filter { case (c, 3) => Some(c) case _ => None } val stable = Filter { case (c, 2) => Some(c) case (c, 3) => Some(c) case _ => None } }