在这里插入图片描述

引言

猜数字游戏是学习编程的经典项目,它综合运用了变量、数据类型、输入输出、控制流、循环、错误处理等核心知识点。通过实现一个完整的猜数字游戏,你可以将之前学到的Rust知识融会贯通。

本文将带你从零开始构建一个完整的猜数字游戏,包括:

  • 基础版本:最简单的实现
  • 增强版本:添加次数限制和统计
  • 完整版本:错误处理、输入验证、难度选择
  • 高级版本:使用随机数、优化用户体验

通过本文的学习,你将能够:

  • 理解Rust的输入输出机制
  • 掌握随机数生成的方法
  • 学会处理用户输入和错误
  • 综合运用循环、条件判断等控制流
  • 写出健壮、用户友好的Rust程序

1. 项目准备

1.1 创建项目

首先,使用Cargo创建新项目:

cargo new guessing_game
cd guessing_game

1.2 添加依赖(用于生成随机数)

编辑 Cargo.toml 文件,添加 rand 依赖:

[package]
name = "guessing_game"
version = "0.1.0"
edition = "2021"

[dependencies]
rand = "0.8"

2. 基础版本:最简单的猜数字游戏

2.1 版本1:固定数字,基本循环

这是最基础的版本,用于理解游戏逻辑:

use std::io;

fn main() {
    println!("=== 猜数字游戏 ===");
    println!("我已经想好了一个1到100之间的数字,来猜猜看!");
    
    let secret_number = 42;  // 固定数字,便于测试
    let mut attempts = 0;
    
    loop {
        attempts += 1;
        println!("\n尝试第 {} 次", attempts);
        print!("请输入你的猜测: ");
        
        // 刷新输出缓冲区,确保提示信息立即显示
        io::Write::flush(&mut io::stdout()).expect("刷新失败");
        
        let mut guess = String::new();
        io::stdin()
            .read_line(&mut guess)
            .expect("读取输入失败");
        
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => {
                println!("请输入一个有效的数字!");
                attempts -= 1;  // 不计入尝试次数
                continue;
            }
        };
        
        println!("你猜的是: {}", guess);
        
        if guess == secret_number {
            println!("🎉 恭喜!你猜对了!");
            println!("你用了 {} 次尝试", attempts);
            break;
        } else if guess < secret_number {
            println!("太小了!");
        } else {
            println!("太大了!");
        }
    }
}

代码解析

  • 使用 loop 创建游戏主循环
  • io::stdin().read_line() 读取用户输入
  • trim().parse() 去除换行符并转换为数字
  • match 表达式处理解析可能失败的情况
  • break 在猜对时退出循环

2.2 版本2:添加次数限制

use std::io;

fn main() {
    println!("=== 猜数字游戏 ===");
    println!("我已经想好了一个1到100之间的数字,来猜猜看!");
    println!("你有7次机会!");
    
    let secret_number = 42;
    let max_attempts = 7;
    let mut attempts = 0;
    
    loop {
        attempts += 1;
        let remaining = max_attempts - attempts + 1;
        
        println!("\n尝试第 {} 次(还剩 {} 次机会)", attempts, remaining);
        print!("请输入你的猜测: ");
        io::Write::flush(&mut io::stdout()).expect("刷新失败");
        
        let mut guess = String::new();
        io::stdin()
            .read_line(&mut guess)
            .expect("读取输入失败");
        
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => {
                println!("请输入一个有效的数字!");
                attempts -= 1;
                continue;
            }
        };
        
        if guess == secret_number {
            println!("🎉 恭喜!你猜对了!");
            println!("你用了 {} 次尝试", attempts);
            break;
        } else if attempts >= max_attempts {
            println!("❌ 游戏结束!你已经用完了所有机会。");
            println!("答案是: {}", secret_number);
            break;
        } else if guess < secret_number {
            println!("太小了!");
        } else {
            println!("太大了!");
        }
    }
}

3. 增强版本:添加随机数和更好体验

3.1 版本3:使用随机数生成器

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

fn main() {
    println!("=== 猜数字游戏 ===");
    
    // 生成1到100之间的随机数
    let secret_number = rand::thread_rng().gen_range(1..=100);
    
    println!("我已经想好了一个1到100之间的数字,来猜猜看!");
    println!("(提示:秘密数字是 {} - 仅用于调试)", secret_number);
    
    let mut attempts = 0;
    const MAX_ATTEMPTS: u32 = 7;
    
    loop {
        attempts += 1;
        let remaining = MAX_ATTEMPTS - attempts + 1;
        
        if remaining == 0 {
            println!("\n❌ 游戏结束!你已经用完了所有机会。");
            println!("答案是: {}", secret_number);
            break;
        }
        
        println!("\n尝试第 {} 次(还剩 {} 次机会)", attempts, remaining);
        print!("请输入你的猜测 (1-100): ");
        io::Write::flush(&mut io::stdout()).expect("刷新失败");
        
        let mut guess = String::new();
        io::stdin()
            .read_line(&mut guess)
            .expect("读取输入失败");
        
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => {
                println!("⚠️  请输入一个有效的数字!");
                attempts -= 1;
                continue;
            }
        };
        
        // 验证输入范围
        if guess < 1 || guess > 100 {
            println!("⚠️  请输入1到100之间的数字!");
            attempts -= 1;
            continue;
        }
        
        println!("你猜的是: {}", guess);
        
        // 使用 match 和 Ordering
        match guess.cmp(&secret_number) {
            Ordering::Less => {
                println!("📉 太小了!");
            }
            Ordering::Greater => {
                println!("📈 太大了!");
            }
            Ordering::Equal => {
                println!("🎉 恭喜!你猜对了!");
                println!("你用了 {} 次尝试", attempts);
                
                // 根据尝试次数给予不同评价
                match attempts {
                    1 => println!("🌟 太厉害了!一次就猜中!"),
                    2..=3 => println!("⭐ 很棒!用很少的次数就猜中了!"),
                    4..=5 => println!("👍 不错!猜中了!"),
                    _ => println!("🎯 虽然用了不少次数,但还是猜中了!"),
                }
                break;
            }
        }
    }
}

新增特性

  • 使用 rand::thread_rng().gen_range(1..=100) 生成随机数
  • 使用 Ordering 枚举进行更优雅的比较
  • 添加输入范围验证(1-100)
  • 根据尝试次数给出不同评价

3.2 版本4:添加难度选择

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

struct GameConfig {
    min: u32,
    max: u32,
    max_attempts: u32,
}

impl GameConfig {
    fn easy() -> Self {
        Self {
            min: 1,
            max: 50,
            max_attempts: 8,
        }
    }
    
    fn medium() -> Self {
        Self {
            min: 1,
            max: 100,
            max_attempts: 7,
        }
    }
    
    fn hard() -> Self {
        Self {
            min: 1,
            max: 200,
            max_attempts: 6,
        }
    }
}

fn select_difficulty() -> GameConfig {
    loop {
        println!("\n=== 选择难度 ===");
        println!("1. 简单 (1-50, 8次机会)");
        println!("2. 中等 (1-100, 7次机会)");
        println!("3. 困难 (1-200, 6次机会)");
        print!("请选择 (1-3): ");
        io::Write::flush(&mut io::stdout()).expect("刷新失败");
        
        let mut choice = String::new();
        io::stdin()
            .read_line(&mut choice)
            .expect("读取输入失败");
        
        match choice.trim() {
            "1" => {
                println!("你选择了:简单难度");
                return GameConfig::easy();
            }
            "2" => {
                println!("你选择了:中等难度");
                return GameConfig::medium();
            }
            "3" => {
                println!("你选择了:困难难度");
                return GameConfig::hard();
            }
            _ => {
                println!("无效选择,请输入 1、2 或 3");
            }
        }
    }
}

fn main() {
    println!("=== 猜数字游戏 ===");
    
    let config = select_difficulty();
    let secret_number = rand::thread_rng().gen_range(config.min..=config.max);
    
    println!("\n我已经想好了一个{}到{}之间的数字,来猜猜看!", 
             config.min, config.max);
    println!("你有 {} 次机会!", config.max_attempts);
    
    let mut attempts = 0;
    let mut min_guess = config.min;
    let mut max_guess = config.max;
    
    loop {
        attempts += 1;
        let remaining = config.max_attempts - attempts + 1;
        
        if remaining == 0 {
            println!("\n❌ 游戏结束!你已经用完了所有机会。");
            println!("答案是: {}", secret_number);
            break;
        }
        
        println!("\n=== 尝试第 {} 次(还剩 {} 次机会) ===", attempts, remaining);
        println!("当前范围提示: {} - {}", min_guess, max_guess);
        print!("请输入你的猜测: ");
        io::Write::flush(&mut io::stdout()).expect("刷新失败");
        
        let mut guess = String::new();
        io::stdin()
            .read_line(&mut guess)
            .expect("读取输入失败");
        
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => {
                println!("⚠️  请输入一个有效的数字!");
                attempts -= 1;
                continue;
            }
        };
        
        if guess < config.min || guess > config.max {
            println!("⚠️  请输入{}到{}之间的数字!", config.min, config.max);
            attempts -= 1;
            continue;
        }
        
        match guess.cmp(&secret_number) {
            Ordering::Less => {
                println!("📉 太小了!");
                min_guess = guess.max(min_guess);  // 更新最小值提示
            }
            Ordering::Greater => {
                println!("📈 太大了!");
                max_guess = guess.min(max_guess);  // 更新最大值提示
            }
            Ordering::Equal => {
                println!("\n🎉🎉🎉 恭喜!你猜对了!🎉🎉🎉");
                println!("秘密数字是: {}", secret_number);
                println!("你用了 {} 次尝试", attempts);
                
                // 评价系统
                let score = (config.max_attempts - attempts + 1) * 100 / config.max_attempts;
                println!("得分: {} 分", score);
                
                match attempts {
                    1 => println!("🌟 传奇!一次就猜中!你是神吗?"),
                    2..=3 => println!("⭐ 卓越!你的直觉太准了!"),
                    4..=5 => println!("👍 优秀!很棒的表现!"),
                    _ => println!("🎯 完成了挑战!继续努力!"),
                }
                break;
            }
        }
    }
}

新增特性

  • 使用结构体 GameConfig 配置游戏参数
  • 难度选择功能
  • 动态范围提示(显示当前可能的范围)
  • 评分系统

4. 完整版本:完整的错误处理和用户体验

4.1 版本5:完整的游戏实现

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

// 游戏配置结构体
struct GameConfig {
    min: u32,
    max: u32,
    max_attempts: u32,
    difficulty: String,
}

impl GameConfig {
    fn new(min: u32, max: u32, max_attempts: u32, difficulty: &str) -> Self {
        Self {
            min,
            max,
            max_attempts,
            difficulty: difficulty.to_string(),
        }
    }
    
    fn easy() -> Self {
        Self::new(1, 50, 8, "简单")
    }
    
    fn medium() -> Self {
        Self::new(1, 100, 7, "中等")
    }
    
    fn hard() -> Self {
        Self::new(1, 200, 6, "困难")
    }
    
    fn expert() -> Self {
        Self::new(1, 500, 5, "专家")
    }
}

// 游戏统计
struct GameStats {
    attempts: u32,
    min_range: u32,
    max_range: u32,
    guesses: Vec<u32>,
}

impl GameStats {
    fn new(config: &GameConfig) -> Self {
        Self {
            attempts: 0,
            min_range: config.min,
            max_range: config.max,
            guesses: Vec::new(),
        }
    }
    
    fn add_guess(&mut self, guess: u32, config: &GameConfig) {
        self.attempts += 1;
        self.guesses.push(guess);
        
        // 根据猜测结果更新范围
        if guess < self.max_range {
            self.max_range = guess.max(self.min_range);
        }
        if guess > self.min_range {
            self.min_range = guess.min(self.max_range);
        }
    }
    
    fn get_remaining(&self, config: &GameConfig) -> u32 {
        config.max_attempts.saturating_sub(self.attempts)
    }
    
    fn show_history(&self) {
        if !self.guesses.is_empty() {
            print!("历史猜测: ");
            for (i, &g) in self.guesses.iter().enumerate() {
                print!("{}", g);
                if i < self.guesses.len() - 1 {
                    print!(", ");
                }
            }
            println!();
        }
    }
}

// 读取用户输入的函数
fn read_user_input(prompt: &str) -> Result<String, io::Error> {
    print!("{}", prompt);
    io::Write::flush(&mut io::stdout())?;
    
    let mut input = String::new();
    io::stdin().read_line(&mut input)?;
    
    Ok(input.trim().to_string())
}

// 读取数字输入的函数
fn read_number(prompt: &str, min: u32, max: u32) -> Result<u32, String> {
    let input = read_user_input(prompt)
        .map_err(|_| "读取输入失败".to_string())?;
    
    let num: u32 = input
        .parse()
        .map_err(|_| "请输入一个有效的数字!".to_string())?;
    
    if num < min || num > max {
        return Err(format!("请输入{}到{}之间的数字!", min, max));
    }
    
    Ok(num)
}

// 选择难度
fn select_difficulty() -> GameConfig {
    loop {
        println!("\n╔════════════════════════════════════╗");
        println!("║        选择游戏难度                ║");
        println!("╠════════════════════════════════════╣");
        println!("║  1. 简单   (1-50,   8次机会)      ║");
        println!("║  2. 中等   (1-100,  7次机会)      ║");
        println!("║  3. 困难   (1-200,  6次机会)      ║");
        println!("║  4. 专家   (1-500,  5次机会)      ║");
        println!("╚════════════════════════════════════╝");
        
        match read_user_input("请选择 (1-4): ") {
            Ok(choice) => {
                match choice.as_str() {
                    "1" => {
                        println!("\n✅ 你选择了:简单难度");
                        return GameConfig::easy();
                    }
                    "2" => {
                        println!("\n✅ 你选择了:中等难度");
                        return GameConfig::medium();
                    }
                    "3" => {
                        println!("\n✅ 你选择了:困难难度");
                        return GameConfig::hard();
                    }
                    "4" => {
                        println!("\n✅ 你选择了:专家难度");
                        return GameConfig::expert();
                    }
                    _ => {
                        println!("❌ 无效选择,请输入 1、2、3 或 4");
                    }
                }
            }
            Err(_) => {
                println!("❌ 读取输入失败,请重试");
            }
        }
    }
}

// 显示游戏结果
fn show_result(secret_number: u32, stats: &GameStats, config: &GameConfig) {
    println!("\n╔════════════════════════════════════╗");
    println!("║         🎉 游戏结果 🎉              ║");
    println!("╠════════════════════════════════════╣");
    println!("║  秘密数字: {:>23}  ║", secret_number);
    println!("║  使用次数: {:>23}  ║", stats.attempts);
    println!("║  难度等级: {:>23}  ║", config.difficulty);
    
    let score = if stats.attempts <= config.max_attempts {
        ((config.max_attempts - stats.attempts + 1) * 100) / config.max_attempts
    } else {
        0
    };
    println!("║  得分: {:>25}  ║", score);
    println!("╚════════════════════════════════════╝");
    
    // 评价
    match stats.attempts {
        1 => println!("\n🌟 传奇!一次就猜中!你是神吗?"),
        2..=3 => println!("\n⭐ 卓越!你的直觉太准了!"),
        4..=5 => println!("\n👍 优秀!很棒的表现!"),
        _ if stats.attempts <= config.max_attempts => {
            println!("\n🎯 完成了挑战!继续努力!")
        }
        _ => println!("\n💪 不要气馁,再试一次!"),
    }
}

// 主游戏循环
fn play_game(config: GameConfig) {
    let secret_number = rand::thread_rng().gen_range(config.min..=config.max);
    let mut stats = GameStats::new(&config);
    
    println!("\n╔════════════════════════════════════╗");
    println!("║        游戏开始!                  ║");
    println!("╠════════════════════════════════════╣");
    println!("║  范围: {} - {}", config.min, config.max);
    println!("║  机会: {} 次", config.max_attempts);
    println!("╚════════════════════════════════════╝");
    
    loop {
        let remaining = stats.get_remaining(&config);
        
        if remaining == 0 {
            println!("\n❌ 游戏结束!你已经用完了所有机会。");
            show_result(secret_number, &stats, &config);
            break;
        }
        
        println!("\n═══════════════════════════════════════");
        println!("尝试第 {} 次(还剩 {} 次机会)", stats.attempts + 1, remaining);
        println!("当前范围提示: {} - {}", stats.min_range, stats.max_range);
        stats.show_history();
        
        match read_number("请输入你的猜测: ", config.min, config.max) {
            Ok(guess) => {
                println!("你猜的是: {}", guess);
                
                match guess.cmp(&secret_number) {
                    Ordering::Less => {
                        println!("📉 太小了!");
                        stats.add_guess(guess, &config);
                    }
                    Ordering::Greater => {
                        println!("📈 太大了!");
                        stats.add_guess(guess, &config);
                    }
                    Ordering::Equal => {
                        stats.attempts += 1;
                        stats.guesses.push(guess);
                        println!("\n🎉🎉🎉 恭喜!你猜对了!🎉🎉🎉");
                        show_result(secret_number, &stats, &config);
                        break;
                    }
                }
            }
            Err(err) => {
                println!("⚠️  {}", err);
                // 不增加尝试次数
            }
        }
    }
}

// 询问是否继续游戏
fn ask_play_again() -> bool {
    loop {
        match read_user_input("\n是否再玩一次?(y/n): ") {
            Ok(input) => {
                match input.to_lowercase().as_str() {
                    "y" | "yes" | "是" => return true,
                    "n" | "no" | "否" => return false,
                    _ => println!("请输入 y 或 n"),
                }
            }
            Err(_) => {
                println!("读取输入失败");
                return false;
            }
        }
    }
}

fn main() {
    println!("╔════════════════════════════════════╗");
    println!("║      🎮 猜数字游戏 🎮              ║");
    println!("║                                     ║");
    println!("║  欢迎来到猜数字游戏!               ║");
    println!("║  我会想一个数字,你来猜!          ║");
    println!("╚════════════════════════════════════╝");
    
    loop {
        let config = select_difficulty();
        play_game(config);
        
        if !ask_play_again() {
            println!("\n👋 感谢游玩!再见!");
            break;
        }
    }
}

需要加上库的使用

[package]
name = “hello_world_new”
version = “0.1.0”
edition = “2021”
[dependencies]
rand = “0.8.5” # 版本号可根据实际需求调整,也可用最新稳定版

在这里插入图片描述
在这里插入图片描述

完整功能清单

  • ✅ 难度选择(简单、中等、困难、专家)
  • ✅ 随机数生成
  • ✅ 输入验证和错误处理
  • ✅ 范围提示(动态更新)
  • ✅ 猜测历史记录
  • ✅ 尝试次数限制
  • ✅ 评分系统
  • ✅ 再次游戏功能
  • ✅ 友好的用户界面

5. 代码解析与知识点

5.1 关键Rust特性应用

1. 结构体和方法(初步了解)
struct GameConfig {
    min: u32,
    max: u32,
    max_attempts: u32,
}

impl GameConfig {
    fn easy() -> Self {
        // 关联函数
    }
}
2. Result和错误处理
fn read_number(...) -> Result<u32, String> {
    // 使用Result处理可能的错误
    Ok(value)  // 成功
    Err(message)  // 失败
}
3. 模式匹配
match guess.cmp(&secret_number) {
    Ordering::Less => { ... }
    Ordering::Greater => { ... }
    Ordering::Equal => { ... }
}
4. 向量(Vec)
let mut guesses: Vec<u32> = Vec::new();
guesses.push(guess);  // 添加元素

5.2 随机数生成详解

use rand::Rng;

// 生成1到100之间的随机数(包含边界)
let secret_number = rand::thread_rng().gen_range(1..=100);

// 生成1到100之间的随机数(不包含100)
let secret_number = rand::thread_rng().gen_range(1..100);

5.3 输入输出详解

use std::io;

// 读取一行输入
let mut input = String::new();
io::stdin()
    .read_line(&mut input)  // 读取到String
    .expect("读取失败");

// 打印(不换行)
print!("请输入: ");
io::Write::flush(&mut io::stdout());  // 立即显示

// 打印(换行)
println!("结果: {}", value);

5.4 字符串处理

let input = "42\n";
let trimmed = input.trim();  // 去除首尾空白字符 -> "42"
let number: u32 = trimmed.parse().unwrap();  // 字符串转数字

6. 常见问题与解决方案

问题1:输入时程序卡住

原因:没有刷新输出缓冲区

解决

print!("请输入: ");
io::Write::flush(&mut io::stdout()).expect("刷新失败");

问题2:无法解析输入为数字

原因:输入的字符串包含换行符或空白字符

解决

let guess: u32 = guess.trim().parse()?;

问题3:数字超出范围

解决:添加范围验证

if guess < min || guess > max {
    println!("数字超出范围!");
    continue;
}

7. 扩展练习

练习1:添加计时功能

记录玩家完成游戏所用的时间。

练习2:保存历史记录

将每次游戏的结果保存到文件中。

练习3:多人模式

实现两个玩家轮流猜测的功能。

练习4:提示系统

添加"很接近"的提示(如相差5以内)。

练习5:排行榜

根据得分和尝试次数创建排行榜。

8. 总结

核心知识点回顾

  1. 循环控制:使用 loopbreakcontinue
  2. 条件判断:使用 ifmatchOrdering
  3. 输入输出io::stdin()read_line()print!println!
  4. 错误处理Resultmatchexpect
  5. 随机数rand::thread_rng().gen_range()
  6. 数据结构Vec、结构体、枚举

项目收获

  • 综合应用:将多个知识点融会贯通
  • 错误处理:学会处理用户输入错误
  • 用户体验:关注程序的可用性
  • 代码组织:使用函数和结构体组织代码
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐