Skip to content

Instantly share code, notes, and snippets.

@akimboyko
Created December 15, 2013 13:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akimboyko/7972910 to your computer and use it in GitHub Desktop.
Save akimboyko/7972910 to your computer and use it in GitHub Desktop.
Game of Life implemented on Scala during Global Day of Coderetreat 2013 at Kiev
package main.scala.com.coderetreat
object GameOfLife {
// data structure
class Cell(x: Int, y: Int) {
val posX = x
val posY = y
override def toString = "Cell: " + x + "," + y
override def equals(that: Any) = {
that match {
case t: Cell => t.posX == x && t.posY == y
case _ => false
}
}
override def hashCode = x.hashCode() + y.hashCode()
def isNeighbour(cell: Cell): Boolean =
!equals(cell) && Math.abs(posX - cell.posX) <= 1 && Math.abs(posY - cell.posY) <= 1
}
// functions
def generate(cells: Set[Cell]): Set[Cell] = {
nextGenerationNeighbour(cells).filter(cell => {
val probability = neighboursCount(cell, cells)
probability == 3 || (probability == 2 && cells.contains(cell))
})
}
def neighboursCount(targetCell: Cell, cells: Set[Cell]): Int = {
cells.foldLeft(0)((neighbours, cell) => {
if(targetCell.isNeighbour(cell)) {
neighbours + 1
} else {
neighbours
}
})
}
def nextGenerationNeighbour(generation: Set[Cell]): Set[Cell] = {
val range = Set(-1, 0, +1)
val all = range.flatMap(x => range.map(y => (x, y)))
generation.flatMap(cell => all.map{ case (x, y) => new Cell(cell.posX + x, cell.posY + y) })
}
def toPrintableForm(generation: Set[Cell]): Array[String] = {
def min(n1: Int, n2: Int) = Math.min(n1, n2)
def max(n1: Int, n2: Int) = Math.max(n1, n2)
def abs(n1: Int) = Math.abs(n1)
val dimensions =
generation.foldLeft(None: Option[(Int, Int, Int, Int)])((dimensions, cell) => {
dimensions match {
case Some((minX, minY, maxX, maxY)) =>
Some(
min(cell.posX, minX), min(cell.posY, minY),
max(cell.posX, maxX), max(cell.posY, maxY))
case None =>
Some(cell.posX, cell.posY, cell.posX, cell.posY)
}
})
val (mX, mY, width, height) =
dimensions match {
case Some((minX, minY, maxX, maxY)) =>
(minX, minY, abs(maxX - minX) + 1, abs(maxY - minY) + 1)
case None => (0, 0, 1, 1)
}
val printable =
(0 until height).map(_ => (0 until width).map(_ => ' ').toArray).toArray
generation.foreach(cell =>
printable(cell.posY - mY)(cell.posX - mX) = '#'
)
printable.map(chars => new String(chars))
}
def main(args: Array[String]) {
val r = new scala.util.Random
var generation = (0 until 100).map(_ => new Cell(r.nextInt(20), r.nextInt(20))).toSet
while(!generation.isEmpty) {
toPrintableForm(generation).foreach(System.out.println)
System.out.println(new String((0 until 20).map(_ => '-').toArray))
generation = generate(generation)
Thread.sleep(1000)
}
}
}
package com.coderetreat
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.FunSuite
import main.scala.com.coderetreat.GameOfLife._
@RunWith(classOf[JUnitRunner])
class GameOfLifeSpec extends FunSuite with ShouldMatchers {
//test
test("Single cell is going to die on next generation") {
val cells = Set(new Cell(0, 0))
val expected = Set[Cell]()
val actual = generate(cells)
assert(actual === expected)
}
test("Still life is not changing") {
val block = Set(new Cell(1, 1), new Cell(1, 2), new Cell(2, 1), new Cell(2, 2))
val expected = Set(new Cell(1, 1), new Cell(1, 2), new Cell(2, 1), new Cell(2, 2))
val actual = generate(block)
assert(actual === expected)
}
test("Oscillator is changing") {
val block = Set(new Cell(1, 2), new Cell(2, 2), new Cell(3, 2))
val expected = Set(new Cell(2, 1), new Cell(2, 2), new Cell(2, 3))
val actual = generate(block)
assert(actual === expected)
}
test("Single cell has no neighbours") {
val cell = new Cell(0, 0)
val cells = Set(cell)
val expected = 0
val actual: Int = neighboursCount(cell, cells)
assert(actual === expected)
}
test("Corner of block has 3 neighbours") {
val cell = new Cell(1, 1)
val block = Set(cell, new Cell(1, 2), new Cell(2, 1), new Cell(2, 2))
val expected = 3
val actual: Int = neighboursCount(cell, block)
assert(actual === expected)
}
test("Distance block has no neighbours") {
val cell = new Cell(-1, -1)
val block = Set(new Cell(1, 1), new Cell(1, 2), new Cell(2, 1), new Cell(2, 2))
val expected = 0
val actual: Int = neighboursCount(cell, block)
assert(actual === expected)
}
test("Are neighbours neighbours") {
val cellA = new Cell(0, 0)
val cellB = new Cell(0, 1)
assert(cellA.isNeighbour(cellB) === true)
}
test("Is cell neighbour to itself") {
val cell = new Cell(0, 0)
assert(cell.isNeighbour(cell) === false)
}
test("Are distance cells not neighbour by X") {
val cellA = new Cell(0, 0)
val cellB = new Cell(2, 0)
assert(cellA.isNeighbour(cellB) === false)
}
test("Are distance cells not neighbour by Y") {
val cellA = new Cell(0, 0)
val cellB = new Cell(0, 2)
assert(cellA.isNeighbour(cellB) === false)
}
test("No next generation out of empty one") {
val prevGeneration = Set[Cell]()
val nextGeneration = Set[Cell]()
val actual: Set[Cell] = nextGenerationNeighbour(prevGeneration)
assert(actual === nextGeneration)
}
test("Get possible places for live") {
val prevGeneration = Set(new Cell(1, 1))
val nextGeneration = Set(
new Cell(0, 0), new Cell(0, 1), new Cell(0, 2),
new Cell(1, 0), new Cell(1, 1), new Cell(1, 2),
new Cell(2, 0), new Cell(2, 1), new Cell(2, 2)
)
val actual: Set[Cell] = nextGenerationNeighbour(prevGeneration)
assert(actual === nextGeneration)
}
test("Get possible places for live out of block") {
val prevGeneration = Set(new Cell(1, 1), new Cell(1, 2), new Cell(2, 1), new Cell(2, 2))
val nextGeneration = Set(
new Cell(0, 0), new Cell(1, 0), new Cell(2, 0), new Cell(3, 0),
new Cell(0, 1), new Cell(1, 1), new Cell(2, 1), new Cell(3, 1),
new Cell(0, 2), new Cell(1, 2), new Cell(2, 2), new Cell(3, 2),
new Cell(0, 3), new Cell(1, 3), new Cell(2, 3), new Cell(3, 3)
)
val actual: Set[Cell] = nextGenerationNeighbour(prevGeneration)
assert(actual === nextGeneration)
}
test("Convert empty generation to printable form") {
val generation = Set[Cell]()
val expectedForm = Array(" ")
val actual: Array[String] = toPrintableForm(generation)
assert(actual === expectedForm)
}
test("Convert single cell to printable form") {
val generation = Set(new Cell(0, 0))
val expectedForm = Array("#")
val actual: Array[String] = toPrintableForm(generation)
assert(actual === expectedForm)
}
test("Convert vertical line to printable form") {
val generation = Set(new Cell(1, 1), new Cell(1, 2), new Cell(1, 3))
val expectedForm = Array("#", "#", "#")
val actual: Array[String] = toPrintableForm(generation)
assert(actual === expectedForm)
}
test("Convert horizontal line to printable form") {
val generation = Set(new Cell(1, 1), new Cell(2, 1), new Cell(3, 1))
val expectedForm = Array("###")
val actual: Array[String] = toPrintableForm(generation)
assert(actual === expectedForm)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment