Game of life in Kotlin

Chris Chen
3 min readNov 29, 2020
Photo by National Cancer Institute on Unsplash

The reason why I build this Game of Life example is because an interview. That’s really interesting because the leetcode also has this problem (289. Game of Life). So I try to use a brute force solution to get the next state. Then follow the Android MVP pattern to build the app.

Photo credit by wiki

Rule

The rule of this game is:

  1. Any live cell with fewer than two live neighbours dies, as if by underpopulation.
  2. Any live cell with two or three live neighbours lives on to the next generation.
  3. Any live cell with more than three live neighbours dies, as if by overpopulation.
  4. Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.

Algorithm

The main transition will be this function:

override fun getNextStatus(): Array<IntArray> {
val newPixelArray = Array(cellArray.size) { IntArray(cellArray[0].size) }
for (i in newPixelArray.indices) {
for (j in newPixelArray[0].indices) {
val newValue = if (isLive(cellArray, i, j)) 1 else 0
if (newPixelArray[i][j] == newValue) continue

newPixelArray[i][j] = newValue
}
}

for (i in cellArray.indices) {
for (j in cellArray[0].indices) {
cellArray[i][j] = newPixelArray[i][j]
}
}

return newPixelArray
}

So I copy an array then check each element neighbours finally assign back to the array cellArray

Follow rules, the alive function would be:

private fun isLive(pixelArray: Array<IntArray>, i: Int, j: Int): Boolean {
var neighborLiveCount = 0
for (k in ref.indices) {
if (i + ref[k][0] < 0 || j + ref[k][1] < 0
|| i + ref[k][0] >= pixelArray.size
|| j + ref[k][1] >= pixelArray[0].size
) continue

neighborLiveCount += pixelArray[i + ref[k][0]][j + ref[k][1]]
}

return if (pixelArray[i][j] == 0) {
neighborLiveCount == 3
} else {
neighborLiveCount in 2..3
}
}

For these logic we should define in ViewModel or Presenter (Depends on what kind of architecture pattern you using), then we can write some unit test to make sure our logic is run as our expected.

Write a Test

/**
* leetcode example
* https://leetcode.com/problems/game-of-life/
* input [[0,1,0],[0,0,1],[1,1,1],[0,0,0]]
* output [[0,0,0],[1,0,1],[0,1,1],[0,1,0]]
*/
@Test
fun getNextStatus_failed() {
// Given
presenter.setCellArrayElement(0, 1)
presenter.setCellArrayElement(1, 2)
presenter.setCellArrayElement(2, 0)
presenter.setCellArrayElement(2, 1)
presenter.setCellArrayElement(2, 2)

// When
presenter.getNextStatus()

// Then
assertEquals(0, presenter.cellArray[0][0])
assertEquals(0, presenter.cellArray[0][1])
assertEquals(0, presenter.cellArray[0][2])
assertEquals(1, presenter.cellArray[1][0])
assertEquals(0, presenter.cellArray[1][1])
assertEquals(1, presenter.cellArray[1][2])
assertEquals(0, presenter.cellArray[2][0])
assertEquals(1, presenter.cellArray[2][1])
assertEquals(1, presenter.cellArray[2][2])
assertEquals(0, presenter.cellArray[3][0])
assertEquals(1, presenter.cellArray[3][1])
assertEquals(0, presenter.cellArray[3][2])
}

Then we can use CountDownTimer , Rxjava2 …etc method to change the state by periodically. Consider it might be block UI, so I strongly recommended to put these logic into other worker thread to prevent large works doing in main thread.

Draw the game

After that, we need to think about how to draw this game. We can simply to use a custom view to draw the game. So first we should define the base cell size for the game then we can use drawRect accordingly. Here is a code snipped FYR.

override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
drawCells(canvas)
}

private fun drawCells(canvas: Canvas) {
cellArray.let {
for (i in it.indices) {
for (j in it[0].indices) {
rect.set(
i * cellSize, j * cellSize,
(i + 1) * cellSize, (j + 1) * cellSize
)
paint.color = if (it[i][j] == 1) {
liveColor
} else {
deadColor
}
canvas.drawRect(rect, paint)
}
}
}
}

That’s it. And we can use a touch event to draw everywhere you want to init. Finally the game will looks like:

Game of Life demo

For that interview experience is really good. Because i never imagine that one day I could apply a game algorithm into the app. Here is my full demo code. Hope you like it. :)

--

--