142 lines
3.3 KiB
Kotlin
Vendored
142 lines
3.3 KiB
Kotlin
Vendored
/**
|
|
* This is a straightforward implementation of The Game of Life
|
|
* See http://en.wikipedia.org/wiki/Conway's_Game_of_Life
|
|
*/
|
|
|
|
/*
|
|
* A field where cells live. Effectively immutable
|
|
*/
|
|
class Field(
|
|
val width: Int,
|
|
val height: Int,
|
|
// This function tells the constructor which cells are alive
|
|
// if init(i, j) is true, the cell (i, j) is alive
|
|
init: (Int, Int) -> Boolean
|
|
) {
|
|
private val live: Array<Array<Boolean>> = Array(height) { i -> Array(width) { j -> init(i, j) } }
|
|
|
|
private fun liveCount(i: Int, j: Int)
|
|
= if (i in 0..height - 1 &&
|
|
j in 0..width - 1 &&
|
|
live[i][j]) 1
|
|
else 0
|
|
|
|
// How many neighbors of (i, j) are alive?
|
|
fun liveNeighbors(i: Int, j: Int) =
|
|
liveCount(i - 1, j - 1) +
|
|
liveCount(i - 1, j) +
|
|
liveCount(i - 1, j + 1) +
|
|
liveCount(i, j - 1) +
|
|
liveCount(i, j + 1) +
|
|
liveCount(i + 1, j - 1) +
|
|
liveCount(i + 1, j) +
|
|
liveCount(i + 1, j + 1)
|
|
|
|
// You can say field[i, j], and this function gets called
|
|
operator fun get(i: Int, j: Int) = live[i][j]
|
|
}
|
|
|
|
/**
|
|
* This function takes the present state of the field
|
|
* and return a new field representing the next moment of time
|
|
*/
|
|
fun next(field: Field): Field {
|
|
return Field(field.width, field.height) { i, j ->
|
|
val n = field.liveNeighbors(i, j)
|
|
if (field[i, j])
|
|
// (i, j) is alive
|
|
n in 2..3 // It remains alive iff it has 2 or 3 neighbors
|
|
else
|
|
// (i, j) is dead
|
|
n == 3 // A new cell is born if there are 3 neighbors alive
|
|
}
|
|
}
|
|
|
|
/** A few colony examples here */
|
|
fun main(args: Array<String>) {
|
|
// Simplistic demo
|
|
printField("***", 3)
|
|
// "Star burst"
|
|
printField("""
|
|
__*__
|
|
_***_
|
|
__*__
|
|
""", 10)
|
|
// Stable colony
|
|
printField("""
|
|
__*__
|
|
_*_*_
|
|
__*__
|
|
""", 3)
|
|
// Stable from the step 2
|
|
printField("""
|
|
__**__
|
|
__**__
|
|
__**__
|
|
""", 3)
|
|
// Oscillating colony
|
|
printField("""
|
|
__**__
|
|
__**__
|
|
__**__
|
|
__**__
|
|
""", 6)
|
|
// A fancier oscillating colony
|
|
printField("""
|
|
---------------
|
|
---***---***---
|
|
---------------
|
|
-*----*-*----*-
|
|
-*----*-*----*-
|
|
-*----*-*----*-
|
|
---***---***---
|
|
---------------
|
|
---***---***---
|
|
-*----*-*----*-
|
|
-*----*-*----*-
|
|
-*----*-*----*-
|
|
---------------
|
|
---***---***---
|
|
---------------
|
|
""", 10)
|
|
}
|
|
|
|
// UTILITIES
|
|
|
|
fun printField(s: String, steps: Int) {
|
|
var field = makeField(s)
|
|
for (step in 1..steps) {
|
|
println("Step: $step")
|
|
for (i in 0..field.height - 1) {
|
|
for (j in 0..field.width - 1) {
|
|
print(if (field[i, j]) "*" else " ")
|
|
}
|
|
println("")
|
|
}
|
|
field = next(field)
|
|
}
|
|
}
|
|
|
|
fun makeField(s: String): Field {
|
|
val lines: List<String> = s.split("\n")
|
|
|
|
val w = lines.map { it.length }.max()!!
|
|
val data = Array(lines.size) { Array(w) { false } }
|
|
|
|
// workaround
|
|
for (i in data.indices) {
|
|
data[i] = Array(w) { false }
|
|
for (j in data[i].indices)
|
|
data[i][j] = false
|
|
}
|
|
|
|
for (line in lines.indices) {
|
|
for (x in lines[line].indices) {
|
|
val c = lines[line][x]
|
|
data[line][x] = c == '*'
|
|
}
|
|
}
|
|
|
|
return Field(w, lines.size) { i, j -> data[i][j] }
|
|
}
|