【Rust 入门学习】2.1 Rust 猜谜游戏

猜谜游戏

在深入学习 rust 之前,先通过一个简单的小游戏练练手。

实现一个经典的初学者编程问题:猜谜游戏。

它是这样工作的:程序将生成一个介于 1 和 100 之间的随机整数。然后它会提示玩家输入猜测。输入猜测值后,程序将指示猜测值是过低还是过高。如果猜对了,游戏将打印祝贺信息并退出。

新建项目

进入目录并使用 Cargo 创建一个新项目

cd ~/projects
cargo new guessing_game
cd guessing_game

目录文件结构和内容和上一章节介绍的一样只是项目名变了而已

编译运行它,依旧是一个 helloword 。

$ cargo run
   Compiling guessing_game v0.1.0 (/home/mshing/projects/guessing_game)
    Finished dev [unoptimized + debuginfo] target(s) in 4.07s
     Running `target/debug/guessing_game`
Hello, world!

在后续的练习中,直接使用 run 命令来进行测试版本迭代,无需每次都编译。

处理猜想

猜谜游戏程序的第一部分将要求用户输入,处理该输入,并检查输入是否符合预期形式。首先,我们将允许玩家输入猜测。

文件名:src/main.rs

use std::io;

fn main() {
    println!("猜猜数字!");

    println!("请输入你猜到的数字:");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    println!("你猜对了: {guess}");
}

这段代码不长,但是信息挺多的。

首先看第一行,因为要获取用户的输入然后将结果打印出来,就需要将 io 输入、输出导入代码中。

io库来自标准库,称为std

use std::io;

可以在标准库文档中看到其中的所有内容。

下面是main函数是程序的入口:

fn main() {

fn语法声明了一个新函数,括号 ,表示()没有参数,大括号{, 开始函数体。

println!是一个将字符串打印到屏幕

    println!("Guess the number!");

    println!("Please input your guess.");

此代码正在打印一个提示,说明游戏是什么并请求用户输入。

再下一行是创建一个用来存储用户输入信息的变量:

    let mut guess = String::new();

存储用户输入的信息

从模块 io 中调用 stdin 函数,这允许我们处理用户输入:

    io::stdin()
        .read_line(&mut guess)

如果我们没有在程序开头导入 iouse std::io ,我们仍然可以通过将此函数调用编写为 std::io::stdin

Result使用类型处理潜在故障

        .expect("Failed to read line");

我们可以把这段代码写成:

io::stdin().read_line(&mut guess).expect("Failed to read line");

但是,一长行很难阅读,因此最好将其分开。

更多的相关知识后面章节介绍。

println!使用占位符打印值

    println!("You guessed: {guess}");

运行测试

$ cargo run
   Compiling guessing_game v0.1.0 (/home/mshing/projects/guessing_game)
    Finished dev [unoptimized + debuginfo] target(s) in 0.48s
     Running `target/debug/guessing_game`
猜猜数字!
请输入你猜到的数字:
12
你猜对了: 12

到这里完成第一部分:从键盘输入,然后打印出来。

生成秘密数字

接下来,将生成一个用户猜测的秘密数字,它是1到100之间的随机数。

Rust 的标准库中还没有包含随机数功能,但是有一个类似功能的 rand crate 函数可以使用。

crate 是 Rust 源代码文件的集合,在构建的项目是一个二进制 crate,它是一个可执行文件。rand crate 是一个库 crate,其中包含旨在用于其他程序且不能单独执行的代码。

需要修改Cargo.toml文件以包含randcrate 作为依赖项

文件名:Cargo.toml

[dependencies]
rand = "0.8.5"

编译项目

$ cargo build
  Downloaded ppv-lite86 v0.2.16 (registry `tuna`)
  Downloaded cfg-if v1.0.0 (registry `tuna`)
  Downloaded getrandom v0.2.7 (registry `tuna`)
  Downloaded rand v0.8.5 (registry `tuna`)
  Downloaded rand_core v0.6.3 (registry `tuna`)
  Downloaded rand_chacha v0.3.1 (registry `tuna`)
  Downloaded libc v0.2.126 (registry `tuna`)
  Downloaded 7 crates (773.8 KB) in 4.92s
   Compiling libc v0.2.126
   Compiling cfg-if v1.0.0
   Compiling ppv-lite86 v0.2.16
   Compiling getrandom v0.2.7
   Compiling rand_core v0.6.3
   Compiling rand_chacha v0.3.1
   Compiling rand v0.8.5
   Compiling guessing_game v0.1.0 (/home/mshing/projects/guessing_game)
    Finished dev [unoptimized + debuginfo] target(s) in 10.16s

当我们包含一个外部依赖项时,Cargo 会从注册表中获取依赖项所需的所有内容的最新版本,该注册表是来自Crates.io的数据副本。Crates.io 是 Rust 生态系统中的人们发布他们的开源 Rust 项目供其他人使用的地方。

官方源速度太慢,文中的源已经换成国内的清华大学源了。

生成随机数

使用刚才引入的 rand 依赖创建随机数

文件名:src/main.rs

use std::io;
use rand::Rng;

fn main() {
    println!("猜猜数字!");

    let secret_number = rand::thread_rng().gen_range(1..=100);

    println!("谜底数字是:{secret_number}");

    println!("请输入你猜到的数字(1-100):");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    println!("你猜对了: {guess}");
}

添加行use rand::Rng。该Rng特征定义了随机数生成器实现的方法,我们使用他定义生成(1-100)之间的随机整数。

对于打印出最后的谜底数字是好随时调试程序,最终是需要删除的,如果一开始就知道答案,那肯定就没法玩了。

多运行几次程序,可以看到随机数据:

$ cargo run
   Compiling guessing_game v0.1.0 (/home/mshing/projects/guessing_game)
    Finished dev [unoptimized + debuginfo] target(s) in 2.57s
     Running `target/debug/guessing_game`
猜猜数字!
谜底数字是:78
请输入你猜到的数字(1-100):
7
你猜对了: 7

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/guessing_game`
猜猜数字!
谜底数字是:92
请输入你猜到的数字(1-100):
4
你猜对了: 4

循环对比输入数字和谜底数字

首先,我们添加另一个use语句,将标准库中调用的类型 std::cmp::Ordering引入范围。该Ordering类型是一个枚举,具有 LessGreaterEqual。这是比较两个值时可能出现的三种结果。

最终程序

我们新增循环输入直到答案正确为止、判断输入的是不是整数。

文件名:src/main.rs

use std::io;
use rand::Rng;
use std::cmp::Ordering;

fn main() {
    println!("猜猜数字!");

    let secret_number = rand::thread_rng().gen_range(1..=100);

    loop {
        println!("请输入你猜到的数字(1-100):");
        let mut guess = String::new();
        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");

        println!("你猜的数字是: {guess}");

        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => {
                println!("请确保输入的是数字,且范围是1到100的整数!");
                continue;
            }
        };

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("太小了!"),
            Ordering::Greater => println!("太大了!"),
            Ordering::Equal => {
            println!("你赢了!") ;
            break;
            }
        }
    }
}

运行结果:

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/guessing_game`
猜猜数字!
请输入你猜到的数字(1-100):
100
你猜的数字是: 100

太大了!
请输入你猜到的数字(1-100):
60
你猜的数字是: 60

太小了!
请输入你猜到的数字(1-100):
asd
你猜的数字是: asd

请确保输入的是数字,且范围是1到100的整数!
请输入你猜到的数字(1-100):
80
你猜的数字是: 80

你赢了!

总结

到这里,已经成功构建了一个猜数字的游戏。

这个项目介绍了很多 Rust 新概念:let 、match、 外部crate的使用等等。接下来的学习将会详细的了解到这些概念。

相关文章

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注