C语言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局游戏内避免重复,无需永久修改文件,只需在内存中记录已使用的单词行号即可,步骤如下:
添加全局/静态标记数组:定义一个布尔数组,记录每一行单词是否被使用过(假设文件共30个单词):
// 全局变量,或放在函数内用static修饰,保证多次调用时状态保留 bool used_lines[30] = {false};修正随机行号生成逻辑:循环生成随机行号,直到找到未被使用的行:
do { lineaElegida = rand() % 30 + 1; } while (used_lines[lineaElegida - 1] == true); // 数组下标从0开始,行号从1开始,所以减1 used_lines[lineaElegida - 1] = true; // 标记为已使用优化srand调用时机:
srand(time(NULL))只需要在程序启动时调用一次,不要每次调用randomPalabra都调用,否则短时间内多次调用会生成相同的随机数。处理fgets的换行符问题:读取到单词后去掉末尾的换行符,避免干扰后续逻辑:
fgets(palabra, 6, archivoTxt); // 去掉换行符 size_t len = strlen(palabra); if (len > 0 && palabra[len-1] == '\n') { palabra[len-1] = '\0'; }修改后的完整函数:
#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

