summaryrefslogtreecommitdiff
path: root/mines.kt
blob: b4d7d778766b0f8192d520b01702aff7482c2188 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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)
    }
}