掌握了前面学习的C语言基础知识后,我们现在可以编写一些更有趣的程序了。让我们来实现一个经典的猜数字游戏。

目录

一、游戏设计需求

二、随机数生成实现

1、rand函数基础

测试代码:

rand函数的工作原理是:

2、设置随机种子(srand)

3、使用时间作为随机种子

4、控制随机数范围

1. 基础原理

2. 分解表达式

3. 数学验证

4. 为什么是 200-100+1?

5. 通用公式

三、猜数字游戏完整实现

基础版本

进阶版本(增加尝试次数限制)

四、代码优化建议


一、游戏设计需求

  1. 电脑自动生成1~100之间的随机数

  2. 玩家输入猜测的数字

  3. 根据玩家的猜测,程序给出"大了"或"小了"的提示

  4. 直到玩家猜中数字,游戏结束


二、随机数生成实现

1、rand函数基础

C语言提供了rand()函数来生成伪随机数,其函数原型为:

int rand(void);

    rand()函数会返回一个0到RAND_MAX之间的伪随机数。RAND_MAX的值取决于编译器实现,通常是32767。使用rand()需要包含stdlib.h头文件。

测试代码:

#include <stdio.h>
#include <stdlib.h>

int main() {
    printf("%d\n", rand());
    printf("%d\n", rand());
    printf("%d\n", rand());
    printf("%d\n", rand());
    printf("%d\n", rand());
    return 0;
}

        虽然在单次运行中产生的5个数字看似随机,但每次重新运行程序都会生成相同的数字序列,这显然存在问题。深入探究就会发现,rand函数生成的实际上是伪随机数——这些数字并非真正的随机数,而是基于特定算法产生的。真正的随机数应该是完全不可预测的。

rand函数的工作原理是:

        它基于一个称为"种子"的基准值进行计算来生成随机数序列。程序每次运行时产生相同随机数序列的原因在于,rand函数的默认种子值固定为1。要获得不同的随机数序列,就需要让种子值动态变化。

2、设置随机种子(srand)

        为了让程序每次运行时生成不同的随机数序列,我们需要使用srand()函数来设置随机数种子,用来初始化随机数的生成器的:

void srand(unsigned int seed);

        在调用 rand 函数前需要先调用 srand 函数,通过 srand 的参数 seed 来设置随机数种子。只要种子发生变化,每次生成的随机数序列也会随之改变。

        但这里存在一个矛盾:为了让 rand 生成随机数,需要给 srand 提供一个随机种子;而生成随机数时又需要依赖另一个随机数,这就形成了一个循环依赖的问题。

3、使用时间作为随机种子

        为了使种子值每次运行都不同,我们可以使用程序运行的时间作为种子。C语言提供了time()函数来获取当前时间:

time_t time(time_t* timer);

        使用time函数时需要包含头文件time.htime函数用于获取当前日历时间,返回的是从1970年1月1日0时0分0秒(UTC)到当前程序运行时刻的时间差(以秒为单位)。该函数返回的time_t类型本质上是一个32位或64位的整型数值。

        如果参数timer是非空指针,函数会同时将时间差值存入timer指向的内存空间;若timer为NULL,则仅返回时间差值而不进行存储。这个由time函数返回的时间差值通常被称为"时间戳"。

// VS2022中time_t类型的定义说明
#ifndef _CRT_NO_TIME_T
  #ifdef _USE_32BIT_TIME_T
    typedef __time32_t time_t;
  #else
    typedef __time64_t time_t;
  #endif
#endif

typedef long __time32_t;
typedef __int64 __time64_t;

如果只是让time函数返回时间戳,我们就可以这样写:

time(NULL);//调⽤time函数返回时间戳,这⾥没有接收返回值

那我们就可以让生成随机数的代码改写成如下:

// 基于此,我们可以改写随机数生成代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    // 使用time函数的返回值作为随机种子
    // 注意将返回值强制转换为unsigned int类型以匹配srand参数
    srand((unsigned int)time(NULL));
    
    printf("%d\n", rand());
    printf("%d\n", rand());
    printf("%d\n", rand());
    printf("%d\n", rand());
    printf("%d\n", rand());
    
    return 0;
}

多次运行观察,每次结果都会有所不同:

程序中只需在初始化时调用一次srand函数即可,无需重复调用。

4、控制随机数范围

生成特定范围的随机数:

  • 0~99: rand() % 100

  • 1~100: rand() % 100 + 1

  • a~b: a + rand() % (b - a + 1)

        要理解为什么 100 + rand() % (200 - 100 + 1) 可以生成 100~200 的随机数,我们需要拆解这个表达式:

1. 基础原理

rand() % N 会生成 0 到 N-1 的随机整数

2. 分解表达式

100 + rand() % (200 - 100 + 1) 可以分解为:

  • 200 - 100 + 1 = 101 → 这是模数

  • rand() % 101 → 生成 0 到 100 的随机数

  • 100 + (0到100) → 得到 100 到 200 的随机数

3. 数学验证

  • 当 rand() % 101 得到最小值 0 时:100 + 0 = 100(范围下限)

  • 当 rand() % 101 得到最大值 100 时:100 + 100 = 200(范围上限)

4. 为什么是 200-100+1?

  • 200 - 100 得到的是范围跨度(100)

  • +1 是因为模运算的特性:

    • % N 产生 0 到 N-1 的结果

    • 我们需要 0 到 100(共101个数)才能保证:加100后覆盖100到200(共101个数)

5. 通用公式

对于任意范围 a 到 b 的随机数:

a + rand() % (b - a + 1)
  • b - a + 1 计算范围大小(包含两端点)

  • rand() % (b-a+1) 得到 0 到 (b-a) 的随机数

  • 加 a 后得到 a 到 b 的随机数


三、猜数字游戏完整实现

基础版本

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void game() {
    int r = rand() % 100 + 1; // 生成1-100的随机数
    int guess = 0;
    
    while(1) {
        printf("请猜数字>:");
        scanf("%d", &guess);
        
        if(guess < r) {
            printf("猜小了\n");
        }
        else if(guess > r) {
            printf("猜大了\n");
        }
        else {
            printf("恭喜你,猜对了\n");
            break;
        }
    }
}

void menu() {
    printf("***********************\n");
    printf("****** 1. play ******\n");
    printf("****** 0. exit ******\n");
    printf("***********************\n");
}

int main() {
    int input = 0;
    srand((unsigned int)time(NULL)); // 初始化随机种子
    
    do {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        
        switch(input) {
            case 1:
                game();
                break;
            case 0:
                printf("游戏结束\n");
                break;
            default:
                printf("选择错误,重新选择\n");
                break;
        }
    } while(input);
    
    return 0;
}

进阶版本(增加尝试次数限制)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void game() {
    int r = rand() % 100 + 1;
    int guess = 0;
    int count = 5; // 最多尝试5次
    
    while(count) {
        printf("\n你还有%d次机会\n", count);
        printf("请猜数字>:");
        scanf("%d", &guess);
        
        if(guess < r) {
            printf("猜小了\n");
        }
        else if(guess > r) {
            printf("猜大了\n");
        }
        else {
            printf("恭喜你,猜对了\n");
            return;
        }
        count--;
    }
    
    printf("你失败了,正确值是:%d\n", r);
}

// menu()和main()函数与基础版本相同
// ...

四、代码优化建议

  1. 增加输入验证,防止用户输入非数字导致程序异常

  2. 可以添加游戏难度选择(不同范围或不同尝试次数)

  3. 记录玩家最佳成绩(最少猜测次数)

  4. 添加颜色输出使界面更友好

  5. 可以增加提示功能(如质数提示、奇偶提示等)

        这个猜数字游戏很好地结合了C语言的基础知识,包括函数、循环、条件判断、随机数生成等,是一个很好的练习项目。通过不断扩展功能,可以进一步巩固编程技能。

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐