一、猜谜游戏。
1.简介:
在这个程序中,首先会生成一个1~100的随机整数,然后提示玩家进行猜测;每次玩家需要输入一个数字,程序则会告诉玩家输入的数字和随机生成的数字之间的大小关系;如果玩家猜错则会让玩家再次猜测,如果玩家猜对则提示玩家胜利。
2.代码演变:
V1:
package main
import (
"fmt"
"math/rand"
)//导入math/rand包用于生成随机数
func main() {
maxNum := 100
secretNumber := rand.Intn(maxNum)//rand.Intn(n)函数可以随机生成一个大于等于0小于n的随机整数。
fmt.Println("The secret number is ", secretNumber)//打印secret number
}
以下是运行结果:
The secret number is 87
The secret number is 87
我们可以发现 V1 生成的随机数一直是一个相同的数字,这是怎么回事呢?
V2:
原来由于伪随机数生成器的种子值没有正确地重新初始化。在 Go 语言中,每次调用 rand.Seed() 函数时,都应该使用不同的种子值来确保生成不同的随机数序列。
默认情况下,rand.Seed() 使用当前时间的秒数作为种子。因此,在短时间内连续调用 rand.Intn(n) 可能会生成相同的随机数序列。例如,如果在同一秒内多次调用 rand.Intn(n),它可能会生成相同的随机数。
为了解决这个问题,我们可以用时间戳来初始化随机数种子,可以提供不同的种子值给 rand.Seed()。例如,可以使用当前时间的纳秒数、当前系统时间、用户提供的随机值等作为种子。这样可以确保每次运行程序时都生成不同的随机数序列。
同时,我们还需要引入time的包。
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
maxNum := 100
rand.Seed(time.Now().UnixNano()) //用时间戳初始化随机数种子
secretNumber := rand.Intn(maxNum)
fmt.Println("The secret number is ", secretNumber)
}
以下是运行结果:
The secret number is 9
The secret number is 88
如此,我们就得到了一个0~100之间的随机数。
V3:
为了实现用户的输入输出,以及解析数字,引入新的包bufio、os、strconv、strings。
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)//使用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")//用strings包的函数去除换行符
guess, err := strconv.Atoi(input)//使用Atoi函数将它转成数字
if err != nil {//如果打印失败,提示错误信息
fmt.Println("Invalid input. Please enter an integer value")
return
}
fmt.Println("You guess is", guess)//经过一系列转化得到用户输入的数字
}
以下是运行结果:
The secret number is 16
Please input your guess
25
You guess is 25
bufio:
用于实现带缓冲的I/O操作。
bufio通过缓存提高了I/O操作的效率,它提供了缓冲区,读和写都先在缓冲区中操作,最后再读写文件,这样可以降低访问本地磁盘的次数,从而提高效率。
bufio封装了io.Reader或io.Writer接口对象,并创建另一个也实现了该接口的对象。同时提供了一些文本I/O的帮助函数。
bufio.New该读取器用于读取数据,尤其是从输入流(如文件、网络连接等)读取数据。
这个函数接收一个实现了io.Reader接口的对象作为参数,并返回一个*bufio.Reader对象。
使用bufio.NewReader可以提供更高的读取效率,因为它能够一次读取多个字节,而不是每次只读取一个字节。这对于需要处理大量数据的程序来说,可以显著提高性能。
bufio包中有几个重要的类型:
bufio.Reader:实现了带缓冲的io.Reader接口,可以读取数据。bufio.Writer:实现了带缓冲的io.Writer接口,可以写入数据。bufio.Scanner:用于扫描文件中的数据。
os:
os包提供了一组用于与操作系统进行交互的函数。
strconv:
用于字符串和基本数据类型之间的转换。它提供了一组函数,可以将字符串转换为整数、浮点数、布尔值等基本类型,或者将基本类型转换为字符串。
下面是一些常用的strconv包函数:
strconv.Atoi(s string) (int, error):将字符串转换为整数。strconv.Itoa(i int) string:将整数转换为字符串。strconv.FormatFloat(f float64, fmt byte, prec, bitSize int) string:将浮点数转换为字符串。strconv.ParseFloat(s string, bitSize int) (float64, error):将字符串转换为浮点数。strconv.FormatInt(i int64, base int) string:将整数转换为指定进制的字符串表示。strconv.ParseInt(s string, base int, bitSize int) (int64, error):将字符串转换为指定进制的整数。strconv.Unquote(s string) (string, error):将一个包含引号的字符串解码为普通字符串。
strings:
提供了字符串相关的常用函数。通过这个包,你可以操作和分析字符串。
以下是一些常用的 strings 包函数:
strings.Contains(s, substr string) bool:检查字符串s是否包含子串substr,返回一个布尔值。strings.ContainsAny(s, chars string) bool:检查字符串s是否包含任一字符chars,返回一个布尔值。strings.Count(s, substr string) int:计算字符串s中子串substr出现的次数。strings.EqualFold(s1, s2 string) bool:比较两个字符串是否相等(忽略大小写),返回一个布尔值。strings.Index(s, substr string) int:返回子串substr在字符串s中第一次出现的索引位置,如果未找到则返回 -1。strings.Join(strSlice []string, separator string) string:将字符串切片strSlice使用分隔符separator连接成一个字符串。strings.LastIndex(s, substr string) int:返回子串substr在字符串s中最后一次出现的索引位置,如果未找到则返回 -1。strings.Repeat(s string, count int) string:重复字符串scount次,返回一个新字符串。strings.Replace(s, old, new string, n int) string:在字符串s中替换第n个出现的子串old为new。strings.Split(s, sep string) []string:按照分隔符sep将字符串s分割成多个子串,返回一个字符串切片。strings.TrimSpace(s string) string:去除字符串s开头和结尾的空白字符。
V4:
实现判断逻辑。
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)
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)
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!")
}
}
以下是运行结果: The secret number is 66
Please input your guess
45
You guess is 45
Your guess is smaller than the secret number. Please try again
V5:
实现循环,同时不输出secret number是什么。
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 {//添加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//玩家猜对,结束整个循环
}
}
}
以下是运行结果:
Please input your guess
50
You guess is 50
Your guess is bigger than the secret number. Please try again
25
You guess is 25
Your guess is smaller than the secret number. Please try again
37
You guess is 37
Your guess is bigger than the secret number. Please try again
30
You guess is 30
Correct, you Legend!
最后使用了一个for的无限循环,使得游戏可以一直进行,直到玩家猜到数字break整个程序。同时把之前的return改为continue,如果出错,结束本次循环,执行下一次输入。
总结
本案例是一个简单的猜字游戏,从中可以学到:
- 随机数生成:使用
rand.Seed()和rand.Intn()来生成随机数。 - 输入处理:使用
bufio.NewReader()来从标准输入读取用户输入,并使用strings.Trim()来去除输入字符串的换行符。 - 错误处理:在读取输入和处理输入时都需要进行错误检查,以避免程序崩溃或产生不可预期的行为。
- 控制流程:使用
for循环来实现无限循环,直到用户猜对为止。在循环内部,使用if和else语句来判断用户的猜测是否正确。