Dice

Posted 临风而眠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dice相关的知识,希望对你有一定的参考价值。

Dice

​ 我们一起来学习用Android studio做一个Dice roller 掷骰子APP,最终类似于一个随机数猜数小游戏,只是猜数字的形式换成了掷骰子

一.随机数

​ 怎么表现掷骰子的随机性呢,我们很自然的会想到随机数,默默的想起了曾经玩飞行棋当过欧皇也经常当非洲人的经历,我们对C语言的随机数已经很熟悉,下面直接看kotlin语言的随机数代码:

fun main(){
    val diceRange = 1..6
    val randomNumber = diceRange.random()
    println("Random number:${randomNumber}")
}

​ 其中m..n的用法是一个Kotlin整数范围,表示数字m到数字n(包括m,n)

​ random()函数是生成并返回给定范围内的随机数

​ 可直接对范围调用random()函数,如(1..6).random()

二.Dice类

​ kotlin作为一种和java比较类似的面向对象的语言,自然要用到类与对象,那么下面就把骰子的属性和方法封装到Dice类中

1.面数属性

​ 先来测试一下:

fun main(){
    val firstDice = Dice()//创建Dice类的对象实例
    println(firstDice.sides)
}
class Dice{
    var sides = 6//骰子有六个面
}

​ 运行结果是6

​ 看上面的代码会发现和C++类与对象的区别就是实例化的时候,如果用C++就是:

	Dice firstDice

​ 与变量命名稍有不同,类名称采用驼峰式大小写形式。例如:AirPlane、OnlineBook 、Dice

2.掷骰子方法

​ 掷骰子产生随机数就要用到前面的random()函数了

fun main(){
    val firstDice = Dice()//创建Dice类的对象实例
   
    firstDice.roll()
}
class Dice{
    var sides = 6//骰子有六个面
    fun roll(){
        val randomNumber = (1..6).random()
        println(randomNumber)
    }
}

​ 学过C++的话上面代码非常好理解

​ 函数和方法的命名惯例是以小写字母开头,采用驼峰式大小写形式,尽可能以操作动词开头

函数返回值

​ 指定返回类型的语法如下:在函数名称后面,在括号后添加冒号、空格,然后为函数返回类型添加关键字

fun main() {
    val firstDice = Dice()
    val diceRoll = firstDice.roll()
    println("Your ${firstDice.sides} sided dice rolled ${diceRoll}!")
}

class Dice {
    var sides = 6

    fun roll(): Int {
        val randomNumber = (1..6).random()
        return randomNumber
    }
}

3.自定义骰子

自定义骰子的面数numSides

fun main() {
    val firstDice = Dice(6)
    println("Your ${firstDice.numSides} sided dice rolled ${firstDice.roll()}!")

    val secondDice = Dice(20)
    println("Your ${secondDice.numSides} sided dice rolled ${secondDice.roll()}!")
}

class Dice (val numSides: Int) {

    fun roll(): Int {
        return (1..numSides).random()
    }
}

三.创建交互按钮

​ 下面就可以去android studio中操作了,先用empty activity新建一个project

1.添加button

​ 打开activity_main.xml,选择Button拖动到显示“Hello world!”的那个TextView下面

image-20210610004631019

2.确定button位置

Button要在TextView正下方,拖动使其重重合

image-20210610004905429image-20210610004917308

​ 添加从 Button 的左侧和右侧到父级 ConstraintLayout的左侧和右侧的水平约束条件。

image-20210610005140165

3.修改button文字

​ 在Button的text属性里面改成你想要的文字,老问题,规范编码@string

​ 我们之后需要在button上面的textview上显示骰子的点数,所以先把Hello world删掉,此处介绍一个调试功能

tools text

image-20210610010034829

​ 这些文字仅在布局预览中显示,在应用运行时不会显示

4.添加button交互

​ 我们需要实现点击button 就显示骰子的点数

先介绍下导包的配置

​ 用过python和Java 都知道import导包

​ 在这里找到Settings for New Projects

image-20210610080154930

​ 继续找到Auto Import

image-20210610080306967

​ 设置成如下配置

image-20210610080458593

Add unambiguous imports on the fly能让 Android Studio 智能补全import

Optimize imports on the fly(for current project)设置会指示 Android Studio 移除unused的导包

在Mainactivity.kt中添加代码

首先调用setContentView方法,关于这个方法,可以看这位大佬的简书的详细解释

findViewById

 val rollButton: Button = findViewById(R.id.button)

​ findViewById() 方法在布局中找到 Button。R.id.button是 Button 的资源 ID,该 ID 是 Button 的唯一标识符

​ 代码将 Button 对象的引用保存到名为 rollButton 的变量中**,而不是保存 Button 对象本身**(引用的概念学过C++应该知道)

​ Android 会自动为应用中的资源分配 ID 号。例如,Roll 按钮具有资源 ID,按钮文字的字符串也具有资源 ID资源 ID 的格式为 R.<type>.<name>;例如 R.string.roll。对于 View ID,<type>id,例如 R.id.button

鼠标监听事件setOnClickListener()

val rollButton: Button = findViewById(R.id.button)
       rollButton.setOnClickListener {
           val toast = Toast.makeText(this, "Dice Rolled!", Toast.LENGTH_SHORT)
           toast.show()
       }

​ 其中Toast是一条向用户显示的简短消息

​ 通过调用 Toast.makeText()创建包含文字 “Dice Rolled!” 的 Toast,再调用show()方法让Toast自行显示

也可以将两行代码合为一行

Toast.makeText(this, "Dice Rolled!", Toast.LENGTH_SHORT).show()

效果如下:

点击Button后,下方显示一条简短消息

image-20210610083802455

点击 Button 时更新 TextView

最终代码:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val rollButton: Button = findViewById(R.id.button)
        rollButton.setOnClickListener {
            val resultTextView: TextView = findViewById(R.id.textView)
            resultTextView.text = "6"
        }
    }
}

现在就实现了如下效果

image-20210610084702239

5.添加掷骰子逻辑

完整代码如下

package com.example.dice_roller

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import android.widget.Toast

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val rollButton: Button = findViewById(R.id.button)
        rollButton.setOnClickListener {
            rollDice()
        }

    }

    private fun rollDice() {
        val dice=Dice(6)
        val diceRoll = dice.roll()
        val resultTextView: TextView = findViewById(R.id.textView)
        resultTextView.text=diceRoll.toString()
    }
}

class Dice(private val numSides: Int){
    fun roll(): Int{
        return(1..numSides).random()
    }
}

toString()是把diceRoll的数字转化为TextView的text属性所需使用的文字

感觉学下来和当时用C++做Qt的时候很相像

效果如下,每点击一次就会随机显示1到numSides的数字

image-20210610085858828

6.代码习惯优化

​ 先全选,再使用键盘快捷键 Ctrl+Alt+L格式化代码

在每个方法与类前面添加注释

package com.example.dice_roller

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import android.widget.Toast

/**
 * 用户掷骰子,并且能在屏幕上看到结果
 */
 
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val rollButton: Button = findViewById(R.id.button)
        rollButton.setOnClickListener {
            rollDice()
        }

    }

    /**
     * 点击掷骰子,就更新数字
     */
    private fun rollDice() {
        //实例化6个面的骰子对象
        val dice = Dice(6)
        val diceRoll = dice.roll()
        //更新数字
        val resultTextView: TextView = findViewById(R.id.textView)
        resultTextView.text = diceRoll.toString()
    }
}

/**
 * 骰子类
 * 属性:面数
 * 方法:返回随机数
 */
class Dice(private val numSides: Int) {
    fun roll(): Int {
        return (1..numSides).random()
    }
}

四.游戏逻辑

​ 在最开头的时候说了,最终类似于一个随机数猜数小游戏,只是猜数字的形式换成了掷骰子

​ 那我们就设置一个幸运数字luckyNumber

1.if else

​ 下面的代码和C语言很像,判断语句也和C语言差不多,较容易看懂,

fun main() {
    val myFirstDice = Dice(6)
    val rollResult = myFirstDice.roll()
    val luckyNumber = 4

    if (rollResult == luckyNumber) {
        println("You win!")
    } else if (rollResult == 1) {
        println("So sorry! You rolled a 1. Try again!")
    } else if (rollResult == 2) {
        println("Sadly, you rolled a 2. Try again!")
    } else if (rollResult == 3) {
        println("Unfortunately, you rolled a 3. Try again!")
    } else if (rollResult == 4) {
        println("No luck! You rolled a 4. Try again!")
    } else if (rollResult == 5) {
        println("Don't cry! You rolled a 5. Try again!")
    } else {
        println("Apologies! you rolled a 6. Try again!")
    }
}
class Dice(val numSides:Int){
    fun roll():Int{
        return (1..numSides).random()
        
    }
}

这个和刚学C语言的时候学的猜数游戏就是新瓶子里装旧酒啊

2.when (类似于C的switch)

​ C语言中,如果条件很多,要写好多的if,else于是有时候我们会选择switch

​ 那么在kotlin中,类似的就是when,就是把C语言中的case : 换成了->

​ 直接看代码吧

fun main() {
    val myFirstDice = Dice(6)
    val rollResult = myFirstDice.roll()
    val luckyNumber = 4

    when (rollResult) {
        luckyNumber -> println("You won!")
        1 -> println("So sorry! You rolled a 1. Try again!")
        2 -> println("Sadly, you rolled a 2. Try again!")
        3 -> println("Unfortunately, you rolled a 3. Try again!")
        4 -> println("No luck! You rolled a 4. Try again!")
        5 -> println("Don't cry! You rolled a 5. Try again!")
        6 -> println("Apologies! you rolled a 6. Try again!")
    }
}
class Dice(val numSides:Int){
    fun roll():Int{
        return (1..numSides).random()
        
    }
}

五.加入Dice

1.更新布局

​ 删除TextView,并将一个ImageView拖动到Button上方

​ 选择 Sample data 中的 avatars作为临时图片

确定 ImageView 和 Button 的位置
  • 为 ImageView 添加水平约束条件。将 ImageView 的左侧与父级 ConstraintLayout 的左边缘连接起来
  • 将 ImageView 的右侧与父级的右边缘连接起来。这会使 ImageView 在父级内水平居中
  • 为 ImageView 添加垂直约束条件,将 ImageView 的顶部与父级的顶部连接起来。ImageView 会向上滑动到 ConstraintLayout的顶部
  • 为 Button 添加垂直约束条件,将 Button 的顶部与 ImageView 的底部连接起来。Button 会向上滑动到ImageView的下方
  • 再次选择 ImageView,然后添加垂直约束条件,将 ImageView 的底部与父级的底部连接起来。这会使 ImageView 在 ConstraintLayout内垂直居中

​ 此时已经居中

image-20210610133111250

2.导入骰子图片

​ 如果在APP内没有骰子,真的就变成纯猜数游戏了,下面我们就加入骰子的图片

​ 图片下载链接点击👉这里

​ 按我的上一篇博客里所说的导入图片的方法把六张骰子的图片导入

image-20210610131718170

3.替换临时图片

​ 找到ImageView的设置为头像图片的工具 srcCompat 属性

image-20210610133231556

点击那个小头像,进入此界面,先选择dice_1

image-20210610133319176

emm…too big,调整大小

image-20210610133349041

  • 找到layout_widthlayout_height 属性。它们当前设置为 wrap_content,这意味着 ImageView 的高度和宽度将与其中的内容(源图片)保持一致。
  • ImageView 设置 160dp 的固定宽度和 200dp 的固定高度。按 Enter 键。

image-20210610133701634

还需要调整Button和ImageView之间的距离,在 Constraint Widget 中将按钮的上边距设置为 16dp

image-20210610150152522

4.修改代码

下面就要修改代码啦,掷出数字几,骰子就显示那一面

​ 控件的id可以在Attributes中看到

将rollDice()方法改成如下:

 private fun rollDice() {
        //实例化6个面的骰子对象
        val dice = Dice(6)
        val diceRoll = dice.roll()
        //更新数字
        val diceImage: ImageView = findViewById(R.id.imageView)
        when (diceRoll) {
            1 -> diceImage.setImageResource(R.drawable.dice_1)
            2 -> diceImage.setImageResource(R.drawable.dice_2)
            3 -> diceImage.setImageResource(R.drawable.dice_3)
            4 -> diceImage.setImageResource(R.drawable.dice_4)
            5 -> diceImage.setImageResource(R.drawable.dice_5)
            6 -> diceImage.setImageResource(R.drawable.dice_6)
        }
    }
优化代码

​ 上面的rollDice()方法中,调用了6次diceImage.setImageResource()

​ 改成下面的,就只需要调用一次

val drawableResource = when(diceRoll) {
            1 -> R.drawable.dice_1
            2 -> R.drawable.dice_2
            3 -> R.drawable.dice_3
            4 -> R.drawable.dice_4
            5 -> R.drawable.dice_5
            6 -> R.drawable.dice_6
        }
        diceImage.setImageResource(drawableResource)

但此时,when下面出现了红色下划线

出现该错误的原因是 when 表达式的值被赋给 drawableResource,因此 when 必须涵盖所有情况,它必须处理所有可能的情况,这样,即使您更改为 12 面的骰子,也始终会返回值。Android Studio 建议添加一个 else 分支。您可以通过将 6 的情况更改为 else 来解决此问题。从 15 的情况不变,但包括 6 在内的所有其他情况都通过 else 处理

 val drawableResource = when (diceRoll) {
            1 -> R.drawable.dice_1
            2 -> R.drawable.dice_2
            3 -> R.drawable.dice_3
            4 -> R.drawable.dice_4
            5 -> R.drawable.dice_5
            else -> R.drawable.dice_6
        }

        diceImage.setImageResource(drawableResource)

5.优化界面,添加代码注释

​ 首次打开APP时,也应该显示一个随机的骰子,让用户一目了然这个APP是干什么的,于是在onCreate方法中添加代码

有点像C++里面的构造函数,自动调用

rollDice()

完整代码如下:

package com.example.dice_roller

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast

/**
 * 用户掷骰子,并且能在屏幕上看到结果
 */

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val rollButton: Button = findViewById(R.id.button)
        rollButton.setOnClickListener {
            rollDice()
        }
        //APP初始化的时候就出现骰子
        rollDice()
    }

    /**
     * 点击掷骰子,就更新数字
     */
    private fun rollDice() {
        //实例化6个面的骰子对象
        val dice = Dice(6)
        val diceRoll = dice.roll()
        //更新数字
        val diceImage: ImageView = findViewById(R.id.imageView)

        val drawableResource = when (diceRoll) {
            1 -> R.drawable.dice_1
            2 -> R.drawable.dice_2
            3 -> R.drawable.dice_3
            4 -> R.drawable.dice_4
            5 -> R.drawable.dice_5
            else -> R.drawable.dice_6
        }
        //更新显示的图片资源
        diceImage.setImageResource(drawableResource)
        //屏幕阅读器可以朗读此内容说明  需要添加
        diceImage.contentDescription = diceRoll.toString()
    }
}

/**
 * 骰子类
 * 属性:面数
 * 方法:返回随机数
 */
class Dice(private val numSides: Int) {
    fun roll(): Int {
        return (1..numSides).random()
    }
}

下面就可以尽情玩耍啦!😃

想把自己做的APP分享给别人一起玩点击菜单栏的Build-Build APKs,然后把.apk文件发给别人就可以啦!😄

以上是关于Dice的主要内容,如果未能解决你的问题,请参考以下文章

调用Dice :: Dice(类构造函数)没有匹配函数

CodeForces 534C - Polycarpus' Dice(思路)

SP1026 FAVDICE - Favorite Dice

Dice Similarity Coefficent vs. IoU Dice系数和IoU

Dice

Dice (III)(概率,期望)