You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言Wordle克隆:连续游戏中不修改单词文件避免重复选词

Wordle克隆程序的单词去重优化及代码疑问解答

需求背景

我用C语言开发了一个简易Wordle克隆程序,基础功能已完成。目前程序通过.txt文件获取随机单词,支持最多连续8局游戏并累计分数,但存在同一轮游戏中重复抽到同一单词的小概率问题。需要实现:单词被选中后标记为已使用,后续不再被选中,且不删除或修改原文件,保证下次运行时文件状态不变。

当前代码

void randomPalabra(char palabra[]) {

    int lineaActual = 0, lineaElegida = 0;
    bool seguirLeyendo = true;

    srand(time(NULL));
    lineaElegida = rand() % 30 + 1;


    FILE* archivoTxt;

    archivoTxt = fopen("palabras.txt", "r");
    if (archivoTxt == NULL) {

        printf("El archivo no existe.");
    }
    else {

        do {

            fgets(palabra, 6, archivoTxt);

            if (_stricmp(palabra, "\n") != 0) {

                lineaActual++;
            }

        } while (lineaActual != lineaElegida); // 疑问1:为什么这样写能正常运行并停止,但如果写成if(fgets..etc == NULL)就不行?这两者在技术上不是写法不同但效果一样吗?
        // 疑问2:顺便说一下,我不明白为什么fgets突然把字符串和\n当成两行,之前不是这样的,之前它总是会把单词和末尾的符号一起读取,比如"gatos\n"。 

    }

    fclose(archivoTxt);
}

核心解决方案:内存中标记已使用单词

因为仅需在单次运行的8局游戏内避免重复,无需永久修改文件,只需在内存中记录已使用的单词行号即可,步骤如下:

  1. 添加全局/静态标记数组:定义一个布尔数组,记录每一行单词是否被使用过(假设文件共30个单词):

    // 全局变量,或放在函数内用static修饰,保证多次调用时状态保留
    bool used_lines[30] = {false};
    
  2. 修正随机行号生成逻辑:循环生成随机行号,直到找到未被使用的行:

    do {
        lineaElegida = rand() % 30 + 1;
    } while (used_lines[lineaElegida - 1] == true); // 数组下标从0开始,行号从1开始,所以减1
    used_lines[lineaElegida - 1] = true; // 标记为已使用
    
  3. 优化srand调用时机:srand(time(NULL))只需要在程序启动时调用一次,不要每次调用randomPalabra都调用,否则短时间内多次调用会生成相同的随机数。

  4. 处理fgets的换行符问题:读取到单词后去掉末尾的换行符,避免干扰后续逻辑:

    fgets(palabra, 6, archivoTxt);
    // 去掉换行符
    size_t len = strlen(palabra);
    if (len > 0 && palabra[len-1] == '\n') {
        palabra[len-1] = '\0';
    }
    
  5. 修改后的完整函数:

    #include <string.h>
    #include <stdbool.h>
    #include <stdlib.h>
    #include <time.h>
    #include <stdio.h>
    
    // 全局标记数组,记录30行单词的使用状态
    bool used_lines[30] = {false};
    
    void randomPalabra(char palabra[]) {
        int lineaActual = 0, lineaElegida = 0;
    
        // 仅初始化一次随机种子
        static bool seed_initialized = false;
        if (!seed_initialized) {
            srand(time(NULL));
            seed_initialized = true;
        }
    
        // 生成未使用过的随机行号
        do {
            lineaElegida = rand() % 30 + 1;
        } while (used_lines[lineaElegida - 1]);
    
        // 标记该行已使用
        used_lines[lineaElegida - 1] = true;
    
        FILE* archivoTxt = fopen("palabras.txt", "r");
        if (archivoTxt == NULL) {
            printf("El archivo no existe.");
            return;
        }
    
        do {
            fgets(palabra, 6, archivoTxt);
            if (palabra[0] == '\n') { // 跳过空行
                continue;
            }
            // 去掉换行符
            size_t len = strlen(palabra);
            if (len > 0 && palabra[len-1] == '\n') {
                palabra[len-1] = '\0';
            }
            lineaActual++;
        } while (lineaActual != lineaElegida);
    
        fclose(archivoTxt);
    }
    

疑问解答

疑问1:为什么原do-while循环能停止,换成判断fgets==NULL不行?

原循环的逻辑是定位到第lineaElegida行就停止,只要找到目标行就结束循环;而如果换成while(fgets(...) != NULL),逻辑变成一直读到文件末尾才停止,不管有没有找到目标行,两者的终止条件完全不同,所以不能替换。

疑问2:为什么fgets现在把字符串和\n当成两行?

大概率是你的palabras.txt文件中出现了空行(只有换行符的行)。当fgets读取到空行时,会得到"\n",此时你的代码会跳过计数(_stricmp(palabra, "\n") != 0不成立,lineaActual不增加),相当于把空行当成了单独的一行,但不会计入有效单词行。之前文件里没有空行,所以不会出现这种情况。另外可以直接通过判断palabra[0] == '\n'来跳过空行,比_stricmp更高效。

内容的提问来源于stack exchange,提问作者Valentín Sanabria

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 06:54:48