summaryrefslogtreecommitdiff
path: root/mines.kt
diff options
context:
space:
mode:
authorJakob L. Kreuze <jakob@memeware.net>2018-12-13 19:13:33 -0500
committerJakob L. Kreuze <jakob@memeware.net>2018-12-13 19:13:33 -0500
commitb9348a41231a4578f0d69c335a5409ec8559f92e (patch)
treeefe25273a61cab4cecc710ec57caa33d8dac17e1 /mines.kt
parenta816e658f0bd09461d1516b7a05bd9fa39aed863 (diff)
Refactor code and rename project.
Diffstat (limited to 'mines.kt')
-rw-r--r--mines.kt118
1 files changed, 118 insertions, 0 deletions
diff --git a/mines.kt b/mines.kt
new file mode 100644
index 0000000..b4d7d77
--- /dev/null
+++ b/mines.kt
@@ -0,0 +1,118 @@
+data class Tile(val adjacentMines: Int = 0, val mine: Boolean = false, val masked: Boolean = true)
+
+class Grid(val width: Int, val height: Int, val tiles: Array<Tile>) {
+ constructor(width: Int = 8, height: Int = 8, mines: Int = 10)
+ : this(width, height, Array<Tile>(width * height) { Tile() }) {
+ for (i in 0 until mines) {
+ val x = (0 until width).random()
+ val y = (0 until height).random()
+ placeMine(x, y)
+ }
+ }
+
+ private fun index(x: Int, y: Int) = y * width + x
+ private fun coordinatesValid(x: Int, y: Int) = y >= 0 && y < height && x >= 0 && x < width
+
+ private fun place(x: Int, y: Int, tile: Tile) {
+ tiles[index(x, y)] = tile
+ }
+
+ /**
+ * Returns the tile at the given coordinates.
+ *
+ * @throws IllegalArgumentException if the X coordinate is outside
+ * the range of [0, width), or if the Y coordinate is outside the
+ * range of [0, height).
+ */
+ fun tile(x: Int, y: Int) = if (coordinatesValid(x, y)) {
+ tiles[index(x, y)]
+ } else {
+ throw IllegalArgumentException("Invalid coordinates (${x}, ${y})")
+ }
+
+ /**
+ * Places a mine at the given coordinates.
+ *
+ * @throws IllegalArgumentException if the X coordinate is outside
+ * the range of [0, width), or if the Y coordinate is outside the
+ * range of [0, height).
+ */
+ fun placeMine(x: Int, y: Int) {
+ if (!coordinatesValid(x, y)) {
+ throw IllegalArgumentException("Invalid coordinates (${x}, ${y})")
+ }
+
+ place(x, y, Tile(mine = true))
+
+ val xMin = if (x - 1 < 0) { x } else { x - 1 }
+ val xMax = if (x + 1 >= width) { x } else { x + 1 }
+ val yMin = if (y - 1 < 0) { y } else { y - 1 }
+ val yMax = if (y + 1 >= height) { y } else { y + 1 }
+
+ for (y in yMin..yMax) {
+ for (x in xMin..xMax) {
+ with (tiles[index(x, y)]) {
+ if (!mine) {
+ place(x, y, copy(adjacentMines + 1))
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Reveals a tile at the given coordinates according to the game rules.
+ *
+ * @throws IllegalArgumentException if the X coordinate is outside
+ * the range of [0, width), or if the Y coordinate is outside the
+ * range of [0, height).
+ */
+ fun reveal(x: Int, y: Int) {
+ if (!coordinatesValid(x, y) || !tile(x, y).masked) {
+ return;
+ }
+
+ with (tile(x, y)) {
+ place(x, y, copy(masked = false))
+
+ if (!mine && adjacentMines == 0) {
+ val xMin = if (x - 1 < 0) { x } else { x - 1 }
+ val xMax = if (x + 1 >= width) { x } else { x + 1 }
+ val yMin = if (y - 1 < 0) { y } else { y - 1 }
+ val yMax = if (y + 1 >= height) { y } else { y + 1 }
+
+ for (y in yMin..yMax) {
+ for (x in xMin..xMax) {
+ reveal(x, y)
+ }
+ }
+ }
+ }
+ }
+
+ override fun toString(): String = buildString {
+ for (y in 0 until height) {
+ for (x in 0 until width) {
+ val tile = tile(x, y)
+ append(when {
+ tile.masked -> "."
+ tile.mine -> "M"
+ tile.adjacentMines == 0 -> " "
+ else -> tile.adjacentMines.toString()
+ } + " ")
+ }
+ append("\n")
+ }
+ }
+}
+
+fun main(args: Array<String>) {
+ val grid = Grid()
+
+ for (i in 0 until 9) {
+ val x = (0 until 8).random()
+ val y = (0 until 8).random()
+ grid.reveal(x, y)
+ println(grid)
+ }
+}