diff options
Diffstat (limited to 'app/src')
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 Binary files differnew file mode 100644 index 0000000..b218df9 --- /dev/null +++ b/app/src/main/res/drawable/atlas.png 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 Binary files differnew file mode 100644 index 0000000..310c838 --- /dev/null +++ b/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..1a2ce8c --- /dev/null +++ b/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..c515c23 --- /dev/null +++ b/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..cf67e6d --- /dev/null +++ b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..4c5cee6 --- /dev/null +++ b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png 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) + } +} |