🔥 old rules, first on the renderings

🔥 demand analysis to achieve advanced version of mine clearance, advanced version has 30*16 grid, 480 grids, 99 mines, 381 security zones, through the following operation logic completely avoid 99 mines as customs clearance, can use the red flag of the maximum number of 99!

Operation logic:

1. Press once to insert the red flag, press the second time to fill in the question mark, and press again to restore the normal state

2. Single click to expand territory, step on land mines end of local game, and display all land mines

3. When all mines are cleared, the game wins!

🔥 Implementation analysis

Assign two two-dimensional arrays of maps, one for minefields and one for user action maps

When the user is doing something, determine the click position, up, down, left and right, and the surrounding Angle, and expand

When the user clicks for the first time, it is the time to create a minefield to prevent the user from clicking on it and exploding!!

Query through recursive way, judge the surrounding minefield, open xinjiang to expand soil!

No more words, source code start!!

🔥 code implementation

Code implementation begins!

MinefieldUtil

Public map class, this class is responsible for the role of the following annotation, including creating, allocating maps, etc.

Traps import Android.util. Log import java.utill.* /** * Minefield utility class */ Object MinefieldUtil {private const val TAG = "log-trap" var isEstablish = false // Number of remaining red flags var flagNum = 0 // Number of turnedOnNum = 0 Var isOpen = false // Create a two-dimensional array representing the landmine layout //-1: Landmine area //0-8: Val gameMap = Array(16) {Array(30) {0}} val gameMap = Array(16) {Array(30) {0}} val gameMap = Array(16) {Array(30) {0}} Question mark val operationMap = Array(16) {Array(30) {0}} // Special coordinates which are not allowed to create minefields private Lateinit var specialCoordinate: MutableList<Int> /** * create a list of names in the list of names in the list of names in the list of names in the list. Int) { if (! IsEstablish) {isEstablish = true} else {return} // createSpecialCoordinates(dTemp, For (d in OperationMap.indices) {for (k in operationMap[d].indices) {operationMap[d][k] = 0}} Set flagNum = 99 val random = random () val temp = mutableSetOf<Int>() while (true) {val nextInt = Random. NextInt (479) dTemp * 30 + kTemp // If (! SpecialCoordinate. The contains (nextInt)) {temp. Add (nextInt)} the if (99 = = temp. Size) {break}} / / buried land mines for (I in temp) {val D = I / 30 val k = i-30 * d gameMap[d][k] = -1} createTrapsNumber() //====log log.d (TAG, "\t\t\t0\t1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\t22\t23\t24\t25\t26\t27\t28\t29" ) Log.d( TAG, "\t\t------------------------------------------------------------------------------------------------------------------- -------" ) for ((c, i) in gameMap.withIndex()) { var str = "$c->\t\t" for (k in i) { str += k.toString() + "\t" } Log.d(TAG, Private fun createTrapsNumber() {for (I in gamemap.indices) {for (I in gameMap[I].indices) {for (I in gameMap[I].indices) { // Start counting if (-1! = gameMap[I][j]) {var trapNum = 0 if (j-1 >= 0 && -1 == gameMap[I][J-1]) {trapNum++} // Query if (i-1 on the upper side of the target = = > = 0 && - 1 gameMap [I - 1] [j]) {trapNum++} / / query target on the right side of the if (j + 1 < = = = 29 && - 1 gameMap [I] [m + 1]) {trapNum++} / / query target underside if (I + 1 < = = = 15 && - 1 gameMap [I + 1] [j]) {trapNum++} / / query the upper left corner if (j - 1 > = 0 && I - 1 > = = = 0 && - 1 GameMap [I - 1] [1]) {trapNum++} / / query the upper right corner of the if (j + 1 < = 29 && I - 1 > = = = 0 && - 1 gameMap [I - 1] [j + 1]) {trapNum++} / / query the lower right corner if (j + 1 + 1 < < = 29 && I = = = 15 && - 1 gameMap [I + 1] [m + 1]) {trapNum++} / / query if the lower left (j - 1 > = 0 && I + 1 < = 15 &&-1 == gameMap[I + 1][J-1]) {trapNum++} gameMap[I][j] = trapNum}}}} /** * Create special coordinates */ private fun createSpecialCoordinates(dTemp: Int, kTemp: Int) {specialCoordinate = mutableListOf() // Select position specialCoordinate. Add (dTemp * 30 + kTemp) // Select left coordinate if (kTemp >= 1) { Specialco-ordinate. add(dTemp * 30 + ktemp-1)} // Click on the coordinate if (dTemp >= 1) {specialco-ordinate. add((dTemp - 1) * 30 + KTemp) {if (kTemp <= 28) {specialCoordinate. Add (dTemp * 30 + kTemp + 1)} Specialco-ordinate. add((dTemp + 1) * 30 + kTemp)} // Click on the upper left of the coordinate if (dTemp >= 1 && kTemp >= 1) {specialco-ordinate. add((dTemp) If (dTemp >= 1 && kTemp <= 28) {specialCoordinate. Add ((dTemp - 1) * 30 + kTemp + 1) } // Click on the lower right of the coordinate if (dTemp <= 14&&ktemp <= 28) {specialcoordinate. add((dTemp + 1) * 30 + kTemp + 1)} // Click on the lower left of the coordinate if (dTemp  <= 14 && kTemp >= 1) { specialCoordinate.add((dTemp + 1) * 30 + kTemp - 1) } for (i in specialCoordinate) { Log.d(TAG, I. tostring ()}} /** * reset */ turnedOnNum () {isEstablish = false isOpen = false turnedOnNum = 0 for (d in gameMap.indices) { for (k in gameMap[d].indices) { gameMap[d][k] = 0 operationMap[d][k] = 0 } } flagNum = 0 } }Copy the code

RcAdapter

RecyclerView map adapter, this is a RecyclerView adapter, written directly with RecyclerView, function is to display the map and give click events:

package com.cyn.traps

import android.content.Context
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView

class RcAdapter(var context: Context) : RecyclerView.Adapter<RcAdapter.Holder>() {

    //当游戏失败后,失败处的坐标,此处要着重显示
    var overPosition = 0

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {

        return Holder(LayoutInflater.from(context).inflate(R.layout.item_lattice, parent, false))
    }

    override fun getItemCount(): Int {
        return 480
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        val d = position / 30
        val k = position - 30 * d

        //-1:地雷区域
        //0-8:周围地雷数量
        val indexGame = MinefieldUtil.gameMap[d][k]

        //0:未开采
        //1:已开踩
        //2:标记小红旗
        //3:问号
        val indexOperation = MinefieldUtil.operationMap[d][k]

        //判断是否公开雷区
        if (MinefieldUtil.isOpen) {
            //公开雷区,游戏结束

            when (indexOperation) {
                0, 3 -> {
                    if (indexGame == -1) {
                        holder.itemText.setBackgroundResource(R.mipmap.icon_trap_open)
                        holder.itemText.text = ""
                    } else {
                        holder.itemText.setBackgroundResource(R.mipmap.icon_lattice)
                        holder.itemText.text = ""
                    }
                }
                1 -> {
                    holder.itemText.setBackgroundResource(R.mipmap.icon_empty)
                    holder.itemText.text = indexGame.toString()

                    if (0 != indexGame) {
                        holder.itemText.text = indexGame.toString()
                    } else {
                        holder.itemText.text = ""
                    }

                    holder.itemText.setTextColor(
                        when (indexGame) {
                            1 -> ContextCompat.getColor(context, R.color.index1)
                            2 -> ContextCompat.getColor(context, R.color.index2)
                            3 -> ContextCompat.getColor(context, R.color.index3)
                            4 -> ContextCompat.getColor(context, R.color.index4)
                            5 -> ContextCompat.getColor(context, R.color.index5)
                            6 -> ContextCompat.getColor(context, R.color.index6)
                            7 -> ContextCompat.getColor(context, R.color.index7)
                            else -> ContextCompat.getColor(context, R.color.index8)
                        }
                    )
                }
                2 -> {
                    if (indexGame == -1) {
                        holder.itemText.setBackgroundResource(R.mipmap.icon_flag)
                        holder.itemText.text = ""
                    } else {
                        holder.itemText.setBackgroundResource(R.mipmap.icon_flag_error)
                        holder.itemText.text = ""
                    }
                }
            }

            if (indexOperation == 0 && -1 == indexGame) {
                holder.itemText.setBackgroundResource(R.mipmap.icon_trap_open)
                holder.itemText.text = ""
            }

            if (overPosition == position) {
                holder.itemText.setBackgroundResource(R.mipmap.icon_trap)
                holder.itemText.text = ""
            }

        } else {
            //隐藏雷区
            when (indexOperation) {
                0 -> {
                    holder.itemText.setBackgroundResource(R.mipmap.icon_lattice)
                    holder.itemText.text = ""
                    holder.itemText.setOnClickListener {
                        //开采区域
                        if (-1 == indexGame) {
                            //踩到地雷,游戏结束
                            MinefieldUtil.isOpen = true
                            overPosition = position
                            notifyDataSetChanged()
                            dataCallBack?.gameOver()
                        } else {

//                            dataCallBack?.gameWins()

                            //回调游戏开始
                            if (!MinefieldUtil.isEstablish) {
                                dataCallBack?.gameStart()
                            }

                            //本次点击排除一个格子
                            MinefieldUtil.turnedOnNum++

                            //创建地雷,本局游戏只会执行一次,内部已封装好方法
                            MinefieldUtil.establish(d, k)

                            //递归开采其他模块
                            exploitation(d, k)


                            //判断是否已经排除完地雷
                            if (381 == MinefieldUtil.turnedOnNum) {
                                dataCallBack?.gameWins()
                            }

                            //刷新
                            notifyDataSetChanged()

                        }
                    }
                    holder.itemText.setOnLongClickListener {
                        //在该区域插上小红旗

                        //判断小红旗是否用完了
                        if (MinefieldUtil.flagNum <= 0) {
                            return@setOnLongClickListener true
                        }

                        MinefieldUtil.operationMap[d][k] = 2

                        //回调使用了小红旗
                        dataCallBack?.useFlag()

                        notifyDataSetChanged()
                        return@setOnLongClickListener true
                    }
                }

                1 -> {
                    if (0 == indexGame) {
                        //已开采周围没有地雷的方块
                        holder.itemText.setBackgroundResource(R.mipmap.icon_empty)
                        holder.itemText.text = ""
                    } else {
                        //已开采周围有地雷的方块
                        holder.itemText.setBackgroundResource(R.mipmap.icon_empty)
                        holder.itemText.text = indexGame.toString()
                        holder.itemText.setTextColor(
                            when (indexGame) {
                                1 -> ContextCompat.getColor(context, R.color.index1)
                                2 -> ContextCompat.getColor(context, R.color.index2)
                                3 -> ContextCompat.getColor(context, R.color.index3)
                                4 -> ContextCompat.getColor(context, R.color.index4)
                                5 -> ContextCompat.getColor(context, R.color.index5)
                                6 -> ContextCompat.getColor(context, R.color.index6)
                                7 -> ContextCompat.getColor(context, R.color.index7)
                                else -> ContextCompat.getColor(context, R.color.index8)
                            }
                        )
                    }
                }

                2 -> {
                    holder.itemText.setBackgroundResource(R.mipmap.icon_flag)
                    holder.itemText.text = ""
                    holder.itemText.setOnLongClickListener {

                        MinefieldUtil.operationMap[d][k] = 3

                        dataCallBack?.cancelFlag()

                        notifyDataSetChanged()



                        return@setOnLongClickListener true
                    }
                }

                3 -> {
                    holder.itemText.setBackgroundResource(R.mipmap.icon_doubt)
                    holder.itemText.text = ""
                    holder.itemText.setOnLongClickListener {
                        MinefieldUtil.operationMap[d][k] = 0
                        notifyDataSetChanged()
                        return@setOnLongClickListener true
                    }
                }
            }
        }
    }

    class Holder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val itemText: TextView = itemView.findViewById(R.id.itemText)
    }


    //==============================================================================================
    /**
     * 开采领域,递归调用
     */
    private fun exploitation(d: Int, k: Int) {
        if (MinefieldUtil.gameMap[d][k] >= 0) {
            MinefieldUtil.operationMap[d][k] = 1


            if (0 != MinefieldUtil.gameMap[d][k]) {
                return
            }

            //判断左侧是否开采
            if (k >= 1 && MinefieldUtil.gameMap[d][k - 1] >= 0 && MinefieldUtil.operationMap[d][k - 1] != 1) {
                MinefieldUtil.operationMap[d][k - 1] = 1
                MinefieldUtil.turnedOnNum++
                if (MinefieldUtil.gameMap[d][k - 1] == 0) {
                    exploitation(d, k - 1)
                }
            }

            //判断上侧是否开采
            if (d >= 1 && MinefieldUtil.gameMap[d - 1][k] >= 0 && MinefieldUtil.operationMap[d - 1][k] != 1) {
                MinefieldUtil.operationMap[d - 1][k] = 1
                MinefieldUtil.turnedOnNum++
                if (MinefieldUtil.gameMap[d - 1][k] == 0) {
                    exploitation(d - 1, k)
                }
            }

            //判断右侧是否开采
            if (k <= 28 && MinefieldUtil.gameMap[d][k + 1] >= 0 && MinefieldUtil.operationMap[d][k + 1] != 1) {
                MinefieldUtil.operationMap[d][k + 1] = 1
                MinefieldUtil.turnedOnNum++
                if (MinefieldUtil.gameMap[d][k + 1] == 0) {
                    exploitation(d, k + 1)
                }
            }

            //判断下侧是否开采
            if (d <= 14 && MinefieldUtil.gameMap[d + 1][k] >= 0 && MinefieldUtil.operationMap[d + 1][k] != 1) {
                MinefieldUtil.operationMap[d + 1][k] = 1
                MinefieldUtil.turnedOnNum++
                if (MinefieldUtil.gameMap[d + 1][k] == 0) {
                    exploitation(d + 1, k)
                }
            }

            //判断左上是否开采
            if (d >= 1 && k >= 1 && MinefieldUtil.gameMap[d - 1][k - 1] >= 0 && MinefieldUtil.operationMap[d - 1][k - 1] != 1) {
                MinefieldUtil.operationMap[d - 1][k - 1] = 1
                MinefieldUtil.turnedOnNum++
                if (MinefieldUtil.gameMap[d - 1][k - 1] == 0) {
                    exploitation(d - 1, k - 1)
                }
            }

            //判断右上是否开采
            if (d >= 1 && k <= 28 && MinefieldUtil.gameMap[d - 1][k + 1] >= 0 && MinefieldUtil.operationMap[d - 1][k + 1] != 1) {
                MinefieldUtil.operationMap[d - 1][k + 1] = 1
                MinefieldUtil.turnedOnNum++
                if (MinefieldUtil.gameMap[d - 1][k + 1] == 0) {
                    exploitation(d - 1, k + 1)
                }
            }

            //判断右下是否开采
            if (d <= 14 && k <= 28 && MinefieldUtil.gameMap[d + 1][k + 1] >= 0 && MinefieldUtil.operationMap[d + 1][k + 1] != 1) {
                MinefieldUtil.operationMap[d + 1][k + 1] = 1
                MinefieldUtil.turnedOnNum++
                if (MinefieldUtil.gameMap[d + 1][k + 1] == 0) {
                    exploitation(d + 1, k + 1)
                }
            }

            //判断左下是否开采
            if (d <= 14 && k >= 1 && MinefieldUtil.gameMap[d + 1][k - 1] >= 0 && MinefieldUtil.operationMap[d + 1][k - 1] != 1) {
                MinefieldUtil.operationMap[d + 1][k - 1] = 1
                MinefieldUtil.turnedOnNum++
                if (MinefieldUtil.gameMap[d + 1][k - 1] == 0) {
                    exploitation(d + 1, k - 1)
                }
            }

        }
    }

    //==============================================================================================
    //相关事件回调
    private var dataCallBack: DataCallBack? = null

    fun setDataCallBack(dataCallBack: DataCallBack) {
        this.dataCallBack = dataCallBack
    }

    interface DataCallBack {

        fun gameStart()

        //游戏结束
        fun gameOver()

        //使用小红旗
        fun useFlag()

        //取消使用小红旗
        fun cancelFlag()

        //游戏胜利
        fun gameWins()
    }


}

Copy the code

MainActivity

The first page to launch, for instance creation! Through it to call the above two classes to achieve, its function has also written a very detailed annotation! You can look directly at the following:

package com.cyn.traps import android.annotation.SuppressLint import android.content.Context import android.os.Bundle import android.os.Handler import android.view.LayoutInflater import android.view.View import android.widget.LinearLayout  import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity() { lateinit var rcAdapter: RcAdapter lateinit var flagNum: TextView lateinit var time: TextView lateinit var reset: TextView private var time1 = 0L private var time2 = 0L private val handler: Private val mCounter: Runnable = object: private val mCounter: Runnable = object: Runnable { @SuppressLint("SetTextI18n") override fun run() { handler.postDelayed(this, 1000) time2++ if (60L == time2) { time1++ time2 = 0 } time.text = (if (time1 < 10) "0$time1" else time1.toString()) + ":" + if (time2 < 10) "0$time2" else time2.toString() } } override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState) setContentView(r.layout.activity_main) // Set the status bar to val titleBar = findViewById<LinearLayout>(R.id.titleBar) StatusBarUtil.immersive(this) StatusBarUtil.darkMode(this) StatusBarUtil.setPaddingSmart(this, TitleBar) // Control instantiate flagNum = findViewById(R.i.D.fragnum) time = findViewById(R.I.D.time) reset = findViewById(R.I.D.reset) // Load the game layout initView() // reset click reset.setonClickListener {// recreate the game Minefieldutil.reset () // timer reset Handler.removecallbacks (mCounter) time.text = "00:00" // Small red flag reset flagNum rcAdapter.notifyDataSetChanged() } } @SuppressLint("ClickableViewAccessibility") fun initView() { val rc = findViewById<RecyclerView>(R.id.rc) val layoutParams = rc.layoutParams val pxValue = dip2px(this, 38F) layoutParams.width = pxValue * 30 layoutParams.height = pxValue * 16 rc.layoutManager = GridLayoutManager(this, 30) rcAdapter = RcAdapter(this) rc.adapter = rcAdapter rcAdapter.setDataCallBack(object : Rcadapter.datacallback {override fun gameStart() {time1 = 0 time2 = 0 handler.post(mCounter) Fun gameOver () {MinefieldUtil. IsEstablish = false flagNum. Text = "--" / / stop the timer handler removeCallbacks (mCounter)} / / use small red flag override fun useFlag () {if (MinefieldUtil. IsEstablish) {MinefieldUtil. FlagNum - flagNum. Text = MinefieldUtil. FlagNum. ToString ()}} / / cancel using a small red flag override fun cancelFlag () {if (MinefieldUtil. IsEstablish) { MinefieldUtil. FlagNum++ flagNum. Text = MinefieldUtil. FlagNum. The toString ()}} / / game victory @ SuppressLint override (" SetTextI18n ") Fun gameWins() {// Stop timing handler.removecallbacks (mCounter) // Pop game victory val inflate: View = LayoutInflater.from(this@MainActivity) .inflate(R.layout.dialog_win, null, False) val consume = constructor.findviewbyId <TextView>(R.i. D.c onsume) consume. Text =" " + (if (time1 < 10) "0$time1" else time1.toString()) + ":" + if (time2 < 10) "0$time2" else time2.toString() val again Constructor.findviewbyid <TextView>(r.ida gain) again. SetOnClickListener {// recreate the game time1 = 0 time2 = 0 MinefieldUtil.reset() rcAdapter.notifyDataSetChanged() boxDialog.dismiss() } boxDialog = BoxDialog(this@MainActivity, inflate, BoxDialog. LocationView. CENTER) BoxDialog. SetCancelable (false) / / if you can click DialogView outside to close the Dialog BoxDialog. SetCanceledOnTouchOutside (false) / / whether the back button to close the Dialog can be boxDialog. The show ()}})} / dp turn p * * * * / private fun dip2px(context: Context, dpValue: Float): Int { val scale: Float = context. Resources. DisplayMetrics. Density return (dpValue * scale + 0.5 f), toInt ()}}Copy the code

To sum up, the completion of the basic rely on the above 3 classes, this is the main logic code!