Game of Life Library

Source Code

Let´s continue with an app which is more challenging implementing.

Conway’s Game of Life is a zero player game. Starting with some cells on a 2d board, a simple set of rules lets these cells evolve to something which seems like a living organism.

Game of Life Animation

Licensing information

The Wikipedia articel describes the game, and it´s rules.

I use this rules for teaching test driven development, so let´s start with the specification of the rules in Kotlin and put them into our existing Kotlin Multi Platform project

@Test
fun aDeadCellWithExactlyThreeLivingNeighboursShouldBeAliveInTheNextGeneration() {
    //given
    val cell = Cell(false)
    cell.livingNeighbours = 3

    //when
    cell.calculateNextGeneration()

    //then
    assertTrue(cell.alive)
}

@Test
fun aLivingCellWithLessThanTwoNeighboursShouldBeDeadInTheNextGeneration() {
    //given
    val cell = Cell(true)
    cell.livingNeighbours = 1

    //when
    cell.calculateNextGeneration()

    //then
    assertFalse(cell.alive)

}

@Test
fun aLivingCellWithTwoNeighboursShouldBeAliveInTheNextGeneration() {
    //given
    val cell = Cell(true)
    cell.livingNeighbours = 2

    //when
    cell.calculateNextGeneration()

    //then
    assertTrue(cell.alive)
}
@Test
fun aLivingCellWithThreeNeighboursShouldBeAliveInTheNextGeneration() {
    //given
    val cell = Cell(true)
    cell.livingNeighbours = 3

    //when
    cell.calculateNextGeneration()

    //then
    assertTrue(cell.alive)
}

@Test
fun aLivingCellWithMoreThanThreeNeighboursShouldBeDeadInTheNextGeneration() {
    //given
    val cell = Cell(true)
    cell.livingNeighbours = 4

    //when
    cell.calculateNextGeneration()

    //then
    assertFalse(cell.alive)

}

@Test
fun aDeadCellWithLessThanThreeLivingNeighboursShouldBeDead() {
    //given
    val cell = Cell(false)
    cell.livingNeighbours = 2

    //when
    cell.calculateNextGeneration()

    //then
    assertFalse(cell.alive)
}

Starting from this specification the GOL rules can be implemented like this.

package egger.software.kotlinmpp.libgol

data class Cell(var alive: Boolean) {

    var livingNeighbours: Int = 0

    fun calculateNextGeneration() {
        if (alive) {
            if (!(livingNeighbours == 2 || livingNeighbours == 3)) {
                alive = false
            }
        } else {
            if (livingNeighbours == 3) {
                alive = true
            }
        }
    }

}
fun calculateNextGeneration() {

    for ((rowIdx, row) in cells.withIndex()) {
        for ((colIdx, cell) in row.withIndex()) {
            cell.livingNeighbours = countLivingNeighbours(colIdx, rowIdx)
        }
    }

    for (row in cells) {
        for (cell in row) {
            cell.calculateNextGeneration()
        }
    }

}

One of the great things of the IntelliJ integration of Kotlin Multi Platform is the execution of test. When clicking on “Run” the IDE lets you choose which target platform the test should run on. It not only runs that tests on the selected platform it also configures a standard test runner for that platform (JUnit in case of JVM) and collects the results.

Run common test

JVM Test Output

iOS Test Output

Of course Gradle also executes the test cases. It does so for all configured platforms. For the following example I added a failing test to show the output in the test execution.

Gradle Test Output