这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天
通过前几篇对 Go 语言的简单介绍,分别介绍了开发环境的搭建,Go 语言变量和常量,Go 流程语句和函数以及Go 语言中的部分数据结构等内容,本章我们就利用之前所介绍的知识来进行相应的实战练习。
在这个游戏里面,程序首先会生成一个介于 1 到 100 之间的随机整数,然后提示玩家进行猜测玩家每次输入一个数字,程序会告诉玩家这个猜测的值是高于还是低于那个秘密的随机数,并且让玩家再次猜测。如果猜对了,就告诉玩家胜利并且退出程序。
1. Version 1
在此版本中,仅实现了随机数的生成。定义变量maxNum为随机数的最大值,使用rand.Intn(maxNum)生成随机数,最后打印测试。
func main() {
maxNum := 100
secretNumber := rand.Intn(maxNum)
fmt.Println("The secret number is ", secretNumber)
}
执行结果如下图所示。经过多次打印测试,发现生成的随机数总是同一个数 81。
?为什么一直是同一个数字
通过查看相应的文档,发现在使用rand.Intn()之前需要设置随机数种子,否则的话每一次都会生成相同的随机数序列。一般惯例用法是在程序启动的时候,用启动的时间戳来初始化随机数种子time.now.unix。
2. Version 2
在此版本中,修改了上一版本同一个数字的问题。
func main() {
maxNum := 100
// 用启动的时间戳来初始化随机数种子
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)
fmt.Println("The secret number is ", secretNumber)
}
执行结果如下。经过多次打印测试,每次都会生成随机的不同的数字。
3 读取用户输入模块
在解决了生成随机数的问题后,接着就是完成读取用户输入的功能。此部分先打印提示用户输入,之后对读取的用户输入进行处理——去掉换行符以及转换为数字,与此同时进行相应的错误处理。代码如下:
/* 读取用户输入模块 */
fmt.Println("Please input your guess")
reader := bufio.NewReader(os.Stdin)
// 读取一行输入
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("An error occured while reading input. Please try again", err)
return
}
// 去掉换行符
input = strings.Trim(input, "\r\n")
// 转换为数字
guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("Invalid input. Please enter an integer value")
return
}
fmt.Println("You guess is", guess)
执行结果如下,打印输出结果符合要求。
4 实现判断逻辑模块
现在我们有了一个秘密的值,然后也从用户的输入里面读到了一个值,我们来比较这两个值的大小。如果是用户输入的值比那个秘密的值要大的话,就告诉用户你猜的数值太大了,请再试一次。如果是小了也同理,如果是相等的话,那么我们就告诉用户赢了。代码如下:
/* 实现判断逻辑功能模块 */
if guess > secretNumber {
fmt.Println("Your guess is bigger than the secret number. Please try again")
} else if guess < secretNumber {
fmt.Println("Your guess is smaller than the secret number. Please try again")
} else {
fmt.Println("Correct, you Legend!")
}
运行代码并进行用户输入操作,测试代码无误,符合题目要求。
5 实现游戏循环功能模块
目前实现的功能玩家只能输入一次猜测,无论猜测是否正确,程序都会退出。为了改变这种行为,需要加一个循环。我们把刚刚的代码挪到一个 for 循环里面,再把 return 改成 continue 以便于在出错的时候能够继续循环。在用户输入正确的时候 break,这样才能够在用户胜利的时候退出游戏。
完整的实战项目(猜谜游戏)代码如下所示。
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
func main() {
maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)
// fmt.Println("The secret number is ", secretNumber)
fmt.Println("Please input your guess")
reader := bufio.NewReader(os.Stdin)
for {
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("An error occured while reading input. Please try again", err)
continue
}
input = strings.Trim(input, "\r\n")
guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("Invalid input. Please enter an integer value")
continue
}
fmt.Println("You guess is", guess)
if guess > secretNumber {
fmt.Println("Your guess is bigger than the secret number. Please try again")
} else if guess < secretNumber {
fmt.Println("Your guess is smaller than the secret number. Please try again")
} else {
fmt.Println("Correct, you Legend!")
break
}
}
}
实战项目——猜谜游戏的执行结果如下图所示
个人总结
1. 目前存在问题
- 在此次案例中,我们设定的随机数最大值
maxNum为 100,但如果这个最大值比较大,则猜谜游戏进行的轮数也会相应地变多,有可能会出现 “游戏无法结束的情况” - 若玩家一直未猜中正确的秘密值,前期也许还会因为好奇心接着玩下去,而后期很可能会厌烦此类游戏
解决方案
- 设置 “认输” 功能。既可以解决在未猜中的情况下,知道此次的秘密值;也可以解决由于长时间得不到正确结果玩家厌烦的心理。
- 针对 “认输” 功能,还应该设置一个游戏的最低轮次,否则玩家还没开始猜谜,就可以通过此功能知道秘密值,从而失去了猜谜游戏的意义。
2. 总结
通过本次猜谜游戏实战项目,不仅复习并运用了 Go 语言基础知识,还加深了对编程的理解。对于编程而言,大致分为 4 个步骤:
第一、需求分析:明确我们要处理什么问题(要完成什么功能)。确定我们处理问题(要完成功能)所需的资源。
第二、算法设计(就是如何做一件事情):根据所需的功能和我们拥有的资源,理清思路,排列出完成此功能的具体步骤(这就是算法)。每一个步骤都应当是简单的、确定的。这一步也被称为“编程逻辑”。
第三、编写程序:根据前期设计好的步骤,编写符合c语言规则的程序。
第四、检查程序,没有错误的编译和链接程序。
对于编程而言,不仅仅是实现功能,更重要的是如何编写 “好” 程序。 不知道大家有没有经历过维护一个别人的代码的痛苦,一个方法写老长,还有很多的if else ,根本无法阅读,更不知道代码背后的含义,此时只能心里默默地问候这个留坑的兄弟。其实想要写出 “好” 代码,就要注重代码规范的问题,如果写的规范,注释好,其实很多问题也就解决了。