summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/AndroidManifest.xml21
-rw-r--r--app/src/main/java/space/jakob/mines/GameActivity.kt11
-rw-r--r--app/src/main/java/space/jakob/mines/GameView.kt21
-rw-r--r--app/src/main/java/space/jakob/mines/Grid.kt113
-rw-r--r--app/src/main/java/space/jakob/mines/SetupActivity.kt18
-rw-r--r--app/src/main/res/drawable/atlas.pngbin0 -> 13629 bytes
-rw-r--r--app/src/main/res/layout/activity_game.xml6
-rw-r--r--app/src/main/res/layout/activity_setup.xml82
-rw-r--r--app/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 1631 bytes
-rw-r--r--app/src/main/res/mipmap-mdpi/ic_launcher.pngbin0 -> 1110 bytes
-rw-r--r--app/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 2036 bytes
-rw-r--r--app/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 3106 bytes
-rw-r--r--app/src/main/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 4137 bytes
-rw-r--r--app/src/main/res/values/colors.xml6
-rw-r--r--app/src/main/res/values/dimens.xml5
-rw-r--r--app/src/main/res/values/strings.xml4
-rw-r--r--app/src/main/res/values/styles.xml11
-rw-r--r--app/src/test/java/space/jakob/mines/GridTest.kt17
18 files changed, 315 insertions, 0 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..7a4a4d0
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="space.jakob.mines">
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <activity
+ android:name=".SetupActivity"
+ android:label="@string/title_activity_setup">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".GameActivity">
+ </activity>
+ </application>
+</manifest>
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))
+ }
+ }
+}
diff --git a/app/src/main/res/drawable/atlas.png b/app/src/main/res/drawable/atlas.png
new file mode 100644
index 0000000..b218df9
--- /dev/null
+++ b/app/src/main/res/drawable/atlas.png
Binary files differ
diff --git a/app/src/main/res/layout/activity_game.xml b/app/src/main/res/layout/activity_game.xml
new file mode 100644
index 0000000..794c96e
--- /dev/null
+++ b/app/src/main/res/layout/activity_game.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<space.jakob.mines.GameView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/view"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"/>
diff --git a/app/src/main/res/layout/activity_setup.xml b/app/src/main/res/layout/activity_setup.xml
new file mode 100644
index 0000000..5e3a4cc
--- /dev/null
+++ b/app/src/main/res/layout/activity_setup.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:context=".SetupActivity" android:orientation="vertical">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" android:id="@+id/difficultyButtons">
+ <Button
+ android:text="Easy"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:id="@+id/selectEasy" android:layout_weight="1"/>
+ <Button
+ android:text="Intermediate"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:id="@+id/selectIntermediate" android:layout_weight="1"/>
+ <Button
+ android:text="Expert"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:id="@+id/selectExpert" android:layout_weight="1"/>
+ </LinearLayout>
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" android:id="@+id/widthRow">
+ <TextView
+ android:text="Width:"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:id="@+id/widthLabel" android:layout_weight="1"/>
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:inputType="number"
+ android:ems="10"
+ android:id="@+id/widthEntry" android:layout_weight="1" android:text="8"/>
+ </LinearLayout>
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" android:id="@+id/heightRow">
+ <TextView
+ android:text="Height:"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:id="@+id/heightLabel" android:layout_weight="1"/>
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:inputType="number"
+ android:ems="10"
+ android:id="@+id/heightEntry" android:layout_weight="1" android:text="8"/>
+ </LinearLayout>
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" android:id="@+id/minesRow">
+ <TextView
+ android:text="Mines:"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:id="@+id/minesLabel" android:layout_weight="1"/>
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:inputType="number"
+ android:ems="10"
+ android:id="@+id/minesEntry" android:layout_weight="1" android:text="10"/>
+ </LinearLayout>
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" android:id="@+id/goRow" android:gravity="center">
+ <Button
+ android:text="Let's Do It!"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:id="@+id/goButton" android:layout_weight="1"
+ android:maxWidth="64dp" android:width="64dp"/>
+ </LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..310c838
--- /dev/null
+++ b/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..1a2ce8c
--- /dev/null
+++ b/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..c515c23
--- /dev/null
+++ b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..cf67e6d
--- /dev/null
+++ b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4c5cee6
--- /dev/null
+++ b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..69b2233
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#008577</color>
+ <color name="colorPrimaryDark">#00574B</color>
+ <color name="colorAccent">#D81B60</color>
+</resources>
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..77d7fb3
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<resources>
+ <string name="app_name">Mines</string>
+ <string name="title_activity_setup">Mines</string>
+</resources>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</item>
+ </style>
+
+</resources>
diff --git a/app/src/test/java/space/jakob/mines/GridTest.kt b/app/src/test/java/space/jakob/mines/GridTest.kt
new file mode 100644
index 0000000..94f6c7f
--- /dev/null
+++ b/app/src/test/java/space/jakob/mines/GridTest.kt
@@ -0,0 +1,17 @@
+package space.jakob.mines
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class GridTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}