Go 语言实践:猜数字游戏 | 青训营

142 阅读3分钟

程序会随机生成一个0到99之间的整数,然后提示用户输入猜测的数字。如果用户猜测的数字大于随机数,程序会输出“>”;如果用户猜测的数字小于随机数,程序会输出“<”;如果用户猜测的数字等于随机数,程序会输出“win!”并结束游戏。

这段代码的实现过程如下:

  1. 导入必要的包,包括bufio、fmt、math/rand、os、strconv、strings和time。\
  2. 定义main函数。
  3. 在main函数中,首先定义最大值maxNum为100。
  4. 使用time.Now().UnixNano()初始化随机数种子。
  5. 使用rand.Intn(maxNum)生成一个0到99之间的随机整数secretNumber。
  6. 输出提示信息“Please input the number you guess: ”。
  7. 使用bufio.NewReader(os.Stdin)创建一个读取器reader。
  8. 进入一个无限循环,每次循环执行以下操作: a. 使用reader.ReadString(‘\n’)读取用户输入的一行字符串input。 b. 如果读取过程中发生错误,则输出错误信息并继续下一次循环。 c. 使用strings.TrimSuffix(input, “\r\n”)去掉input字符串末尾的换行符。 d. 使用strconv.Atoi(input)将input字符串转换为整数guess。 e. 如果转换过程中发生错误,则输出错误信息并继续下一次循环。 f. 输出用户猜测的数字guess。 g. 如果secretNumber < guess,则输出“>”;如果secretNumber > guess,则输出“<”;否则,输出“win!”并退出循环。
package main

import (
	"bufio"
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"time"
)

func main4() {
	maxNum := 100
	rand.Seed(time.Now().UnixNano()) //用时间戳来初始化随机数种子
	secretNumber := rand.Intn(maxNum)
	fmt.Println(secretNumber)

	fmt.Println("Please input the number you 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!")
			continue
		}
		input = strings.TrimSuffix(input, "\r\n")
		// fmt.Println(input)

		guess, err := strconv.Atoi(input)
		if err != nil {
			fmt.Println("Invalid input. Please enter an integer value!")
			continue
		}
		// fmt.Printf("%T, %v\n", err, err)
		fmt.Println("you guess is :", guess)

		if secretNumber < guess {
			fmt.Println(">")
		} else if secretNumber > guess {
			fmt.Println("<")
		} else {
			fmt.Println("win!")
			break
		}
	}

}

对于这段代码,一个可行的优化方案是增加一个计数器来记录用户猜测的次数,并在游戏结束时显示出来。这样可以让用户知道自己猜了多少次才猜中数字。此外,还可以增加一个退出游戏的选项,让用户在任何时候都可以选择退出游戏。例如,在每次循环开始时,可以提示用户输入“q”来退出游戏。如果用户输入了“q”,则退出循环并结束游戏。这样可以提高游戏的可玩性和用户体验。

下面解释一些关键问题:\

在上面的代码中,随机数是通过调用rand.Intn(maxNum)函数来生成的。rand.Intn(maxNum)函数会返回一个在[0, maxNum)范围内的伪随机整数。这个伪随机整数是由计算机根据一定的算法生成的,它并不是真正意义上的随机数。
为了生成伪随机数,计算机需要一个初始值,也就是随机数种子。随机数种子决定了伪随机数生成器的初始状态,不同的随机数种子会导致生成不同的伪随机数序列。在上面的代码中,随机数种子是通过调用rand.Seed(time.Now().UnixNano())函数来设置的。这个函数会使用当前时间的纳秒值作为随机数种子。
之所以要这样设置随机数种子,是因为当前时间的纳秒值具有很高的随机性。每次运行程序时,当前时间都会发生变化,从而导致随机数种子发生变化。这样就可以保证每次运行程序时都能生成不同的伪随机数序列。

在上面的代码中,接受用户输入的功能是通过调用reader.ReadString('\n')函数来实现的。这个函数会读取用户从标准输入(即键盘)输入的一行字符串,直到遇到换行符为止。读取到的字符串会被存储在变量input中,然后通过调用strings.TrimSuffix(input, "\r\n")函数去掉字符串末尾的换行符。 除了使用reader.ReadString('\n')函数来接受用户输入,还有其他方法可以实现这个功能。例如,可以使用fmt.Scanln()函数来读取用户输入的一行字符串。这个函数会读取用户从标准输入输入的一行字符串,直到遇到换行符为止。读取到的字符串会被存储在指定的变量中。

在上面的代码中,使用bufio.NewReader(os.Stdin)函数创建一个缓冲区是为了提高读取输入的效率。bufio.NewReader(os.Stdin)函数会创建一个带缓冲区的读取器,它可以一次性读取多个字节并将它们存储在缓冲区中。
当程序需要读取输入时,它会首先从缓冲区中获取数据,如果缓冲区中没有足够的数据,才会从标准输入中读取更多的数据。如果不使用带缓冲区的读取器,而是直接从标准输入中读取数据,那么每次读取一个字节时都需要进行一次系统调用。这样会增加程序的开销,并降低读取输入的效率。而使用带缓冲区的读取器可以减少系统调用的次数,从而提高读取输入的效率。
当然,对于简单的程序来说,使用带缓冲区的读取器和直接从标准输入中读取数据在效率上可能没有太大差别。但是对于需要频繁读取输入的复杂程序来说,使用带缓冲区的读取器可以显著提高程序的运行效率。

升级输入函数

func readInput() string {
    var input string
    fmt.Scanln(&input)
    return input
}

func main() {
    for {
        input := readInput()
        // 处理用户输入
    }
}