diff options
Diffstat (limited to 'app/src/main/java/space')
| -rw-r--r-- | app/src/main/java/space/jakob/mines/GameActivity.kt | 11 | ||||
| -rw-r--r-- | app/src/main/java/space/jakob/mines/GameView.kt | 21 | ||||
| -rw-r--r-- | app/src/main/java/space/jakob/mines/Grid.kt | 113 | ||||
| -rw-r--r-- | app/src/main/java/space/jakob/mines/SetupActivity.kt | 18 |
4 files changed, 163 insertions, 0 deletions
diff --git a/app/src/main/java/space/jakob/mines/GameActivity.kt b/app/src/main/java/space/jakob/mines/GameActivity.kt new file mode 100644 index 0000000..ad09481 --- /dev/null +++ b/app/src/main/java/space/jakob/mines/GameActivity.kt @@ -0,0 +1,11 @@ +package space.jakob.mines + +import android.support.v7.app.AppCompatActivity +import android.os.Bundle + +class GameActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_game) + } +} diff --git a/app/src/main/java/space/jakob/mines/GameView.kt b/app/src/main/java/space/jakob/mines/GameView.kt new file mode 100644 index 0000000..eed0d9c --- /dev/null +++ b/app/src/main/java/space/jakob/mines/GameView.kt @@ -0,0 +1,21 @@ +package space.jakob.mines + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.util.AttributeSet +import android.view.View + +class GameView(context: Context, attrs: AttributeSet) : View(context, attrs) { + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + val textPaint = Paint().apply { + textSize = 64f + } + + canvas.apply { + drawText("Hello, world!", 24f, 24f, textPaint) + } + } +} diff --git a/app/src/main/java/space/jakob/mines/Grid.kt b/app/src/main/java/space/jakob/mines/Grid.kt new file mode 100644 index 0000000..a532632 --- /dev/null +++ b/app/src/main/java/space/jakob/mines/Grid.kt @@ -0,0 +1,113 @@ +data class Tile(val adjacentMines: Int = 0, val mine: Boolean = false, val masked: Boolean = true) + +/** + * The Minesweeper "grid", containing instances of [Tile]. + */ +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 valid(x: Int, y: Int) = y in 0 until height && x in 0 until 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). + */ + operator fun get(x: Int, y: Int) = if (valid(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 (!valid(x, y)) { + throw IllegalArgumentException("Invalid coordinates (${x}, ${y})") + } + + if (this[x, y].mine) { + return; + } + + place(x, y, Tile(mine = true)) + + val xMin = (x - 1).coerceAtLeast(0) + val xMax = (x + 1).coerceAtMost(width - 1) + val yMin = (y - 1).coerceAtLeast(0) + val yMax = (y + 1).coerceAtMost(height - 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 (!valid(x, y) || !this[x, y].masked) { + return; + } + + with (this[x, y]) { + place(x, y, copy(masked = false)) + + if (!mine && adjacentMines == 0) { + val xMin = (x - 1).coerceAtLeast(0) + val xMax = (x + 1).coerceAtMost(width - 1) + val yMin = (y - 1).coerceAtLeast(0) + val yMax = (y + 1).coerceAtMost(height - 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 = this@Grid[x, y] + append(when { + tile.masked -> "." + tile.mine -> "M" + tile.adjacentMines == 0 -> " " + else -> tile.adjacentMines.toString() + } + " ") + } + append("\n") + } + } +} diff --git a/app/src/main/java/space/jakob/mines/SetupActivity.kt b/app/src/main/java/space/jakob/mines/SetupActivity.kt new file mode 100644 index 0000000..e78322e --- /dev/null +++ b/app/src/main/java/space/jakob/mines/SetupActivity.kt @@ -0,0 +1,18 @@ +package space.jakob.mines + +import android.support.v7.app.AppCompatActivity +import android.os.Bundle +import android.widget.Button +import android.content.Intent + +class SetupActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_setup) + + val goButton = findViewById(R.id.goButton) as Button + goButton.setOnClickListener { + startActivity(Intent(this, GameActivity::class.java)) + } + } +} |