Go 语言基础实战 — 猜谜游戏| 青训营笔记

102 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 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。

V1执行结果.png

?为什么一直是同一个数字

通过查看相应的文档,发现在使用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)
}

执行结果如下。经过多次打印测试,每次都会生成随机的不同的数字。

V2执行结果.png

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)

执行结果如下,打印输出结果符合要求。

读取用户输入执行结果.png

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!")
	}

运行代码并进行用户输入操作,测试代码无误,符合题目要求。

判断逻辑执行结果.png

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
		}
	}
}

实战项目——猜谜游戏的执行结果如下图所示

实战项目执行结果.png

个人总结

1. 目前存在问题

  • 在此次案例中,我们设定的随机数最大值 maxNum 为 100,但如果这个最大值比较大,则猜谜游戏进行的轮数也会相应地变多,有可能会出现 “游戏无法结束的情况”
  • 若玩家一直未猜中正确的秘密值,前期也许还会因为好奇心接着玩下去,而后期很可能会厌烦此类游戏

解决方案

  • 设置 “认输” 功能。既可以解决在未猜中的情况下,知道此次的秘密值;也可以解决由于长时间得不到正确结果玩家厌烦的心理。
  • 针对 “认输” 功能,还应该设置一个游戏的最低轮次,否则玩家还没开始猜谜,就可以通过此功能知道秘密值,从而失去了猜谜游戏的意义。

2. 总结

通过本次猜谜游戏实战项目,不仅复习并运用了 Go 语言基础知识,还加深了对编程的理解。对于编程而言,大致分为 4 个步骤:

第一、需求分析:明确我们要处理什么问题(要完成什么功能)。确定我们处理问题(要完成功能)所需的资源。

第二、算法设计(就是如何做一件事情):根据所需的功能和我们拥有的资源,理清思路,排列出完成此功能的具体步骤(这就是算法)。每一个步骤都应当是简单的、确定的。这一步也被称为“编程逻辑”。

第三、编写程序:根据前期设计好的步骤,编写符合c语言规则的程序。

第四、检查程序,没有错误的编译和链接程序。

对于编程而言,不仅仅是实现功能,更重要的是如何编写 “好” 程序。 不知道大家有没有经历过维护一个别人的代码的痛苦,一个方法写老长,还有很多的if else ,根本无法阅读,更不知道代码背后的含义,此时只能心里默默地问候这个留坑的兄弟。其实想要写出 “好” 代码,就要注重代码规范的问题,如果写的规范,注释好,其实很多问题也就解决了。

参考资料