猜拳游戏(石头 - 剪刀 - 布)是 C 语言入门阶段的经典实践项目,它涵盖了条件判断、循环控制、随机数生成、用户交互等核心知识点。通过开发这个小游戏,不仅能巩固 C 语言基础语法,还能培养逻辑思维和代码组织能力。本文将带大家从零开始,完成一个功能完整、体验流畅的 C 语言猜拳游戏,包含详细的逻辑分析和可直接运行的代码。
一、项目需求与功能规划
在编写代码前,先明确游戏的核心需求和功能模块,避免开发过程中思路混乱。本次开发的猜拳游戏需满足以下需求:
1. 核心玩法
支持玩家与电脑对战,玩家通过输入数字选择 “石头”“剪刀”“布”
电脑随机生成选择,遵循 “石头克剪刀、剪刀克布、布克石头” 的规则判断胜负
每次对战后显示结果(玩家胜、电脑胜、平局),并统计双方得分
提供多次对战功能,玩家可选择继续游戏或退出
2. 交互体验优化
清晰的菜单提示,告知玩家输入规则(如 “1 = 石头,2 = 剪刀,3 = 布”)
输入错误时给出提示(如输入非 1-3 的数字),并允许重新输入
游戏结束时显示最终得分和胜负结果
3. 技术要点
使用rand()函数生成电脑的随机选择,配合srand()设置随机种子
用if-else或switch语句实现胜负判断逻辑
用while循环实现多轮游戏功能
通过变量记录玩家和电脑的得分
二、游戏逻辑拆解与流程设计
将复杂的游戏功能拆解为多个小模块,按流程逐步实现,是高效开发的关键。猜拳游戏的核心流程可分为以下 5 步:
初始化:设置随机种子(确保每次运行电脑选择不同),初始化玩家和电脑的得分(均为 0)
玩家选择:显示游戏菜单,获取玩家输入的选择(1-3),并处理无效输入
电脑选择:通过随机数生成 1-3 的整数,分别对应 “石头”“剪刀”“布”
胜负判断:根据双方选择对比规则,判断结果并更新得分
循环与退出:询问玩家是否继续游戏,若选择 “是” 则回到步骤 2,否则显示最终得分并退出
为了让逻辑更清晰,我们可以用流程图简化这个过程:
开始 → 初始化得分与随机种子 → 显示菜单 → 玩家输入选择 → 电脑生成随机选择 → 对比判断胜负 → 更新得分 → 询问是否继续 → 是(循环)/ 否(显示最终得分 → 结束)
三、完整代码实现与逐段解析
下面将按照上述流程,编写完整的 C 语言猜拳游戏代码,并对关键部分进行详细解析。代码基于标准 C 语言编写,可在 Dev-C++、VS Code(配置 MinGW)等编译器中直接运行。
1. 完整代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// 函数声明:显示游戏规则
void showRules();
// 函数声明:根据选择(1-3)返回对应的字符串(石头/剪刀/布)
const char* getChoiceName(int choice);
// 函数声明:判断胜负,返回结果(1=玩家胜,0=平局,-1=电脑胜)
int judgeWinner(int playerChoice, int computerChoice);
int main() {
int playerScore = 0; // 玩家得分
int computerScore = 0; // 电脑得分
int playerChoice; // 玩家选择(1-3)
int computerChoice; // 电脑选择(1-3)
char continueGame; // 是否继续游戏(y/n)
// 1. 初始化:设置随机种子(以当前时间为种子,确保每次运行随机数不同)
srand((unsigned int)time(NULL));
// 欢迎界面
printf("===================== C语言猜拳游戏 =====================\n");
showRules(); // 调用函数显示游戏规则
// 2. 循环游戏:直到玩家选择退出
do {
// 3. 玩家选择:获取有效输入(1-3)
printf("\n请输入你的选择(1=石头,2=剪刀,3=布):");
// 处理无效输入(如输入字母、负数等)
while (scanf("%d", &playerChoice) != 1 || playerChoice < 1 || playerChoice > 3) {
// 清除输入缓冲区的无效字符,避免死循环
while (getchar() != '\n');
printf("输入错误!请重新输入1-3之间的数字:");
}
// 4. 电脑选择:生成1-3的随机数(rand()%3生成0-2,加1后变为1-3)
computerChoice = rand() % 3 + 1;
// 5. 显示双方选择
printf("你选择了:%s\n", getChoiceName(playerChoice));
printf("电脑选择了:%s\n", getChoiceName(computerChoice));
// 6. 判断胜负并更新得分
int result = judgeWinner(playerChoice, computerChoice);
switch (result) {
case 1:
printf("恭喜!这一轮你赢了!\n");
playerScore++;
break;
case 0:
printf("平局!再来一局吧~\n");
break;
case -1:
printf("很遗憾,这一轮电脑赢了!\n");
computerScore++;
break;
}
// 7. 显示当前得分
printf("当前得分:你 %d 分 | 电脑 %d 分\n", playerScore, computerScore);
// 8. 询问是否继续游戏
printf("是否继续游戏?(y=继续,n=退出):");
// 清除输入缓冲区的换行符,避免读取到上一次输入的回车
while (getchar() != '\n');
scanf("%c", &continueGame);
// 统一转换为小写,方便判断(无论玩家输入Y还是y都视为继续)
continueGame = tolower(continueGame);
} while (continueGame == 'y'); // 若输入y则继续循环,否则退出
// 9. 游戏结束:显示最终得分
printf("\n===================== 游戏结束 =====================\n");
printf("最终得分:你 %d 分 | 电脑 %d 分\n", playerScore, computerScore);
if (playerScore > computerScore) {
printf("🎉 恭喜你获得最终胜利!\n");
} else if (playerScore < computerScore) {
printf("💪 下次加油,争取打败电脑!\n");
} else {
printf("🤝 最终平局,再来一局说不定能赢哦!\n");
}
return 0;
}
// 函数定义:显示游戏规则
void showRules() {
printf("游戏规则:\n");
printf("1. 玩家选择石头(1)、剪刀(2)或布(3),电脑随机选择\n");
printf("2. 胜负规则:石头克剪刀,剪刀克布,布克石头\n");
printf("3. 每轮结束后显示得分,可选择继续或退出游戏\n");
printf("=====================================================\n");
}
// 函数定义:根据选择返回对应名称
const char* getChoiceName(int choice) {
switch (choice) {
case 1: return "石头";
case 2: return "剪刀";
case 3: return "布";
default: return "未知选择"; // 理论上不会触发(输入已校验)
}
}
// 函数定义:判断胜负
int judgeWinner(int playerChoice, int computerChoice) {
// 若双方选择相同,返回平局(0)
if (playerChoice == computerChoice) {
return 0;
}
// 玩家胜的情况:1(石头)克2(剪刀),2(剪刀)克3(布),3(布)克1(石头)
if ((playerChoice == 1 && computerChoice == 2) ||
(playerChoice == 2 && computerChoice == 3) ||
(playerChoice == 3 && computerChoice == 1)) {
return 1;
}
// 其他情况(电脑胜),返回-1
return -1;
}
2. 关键代码解析
(1)随机数生成:确保电脑选择不固定
// 初始化随机种子:以当前时间为种子
srand((unsigned int)time(NULL));
// 生成1-3的随机数
computerChoice = rand() % 3 + 1;
srand():设置随机数种子,若不设置,每次运行rand()会生成相同的随机序列(电脑选择固定)。用time(NULL)获取当前系统时间(秒级)作为种子,确保每次运行种子不同,随机数也不同。
rand() % 3 + 1:rand()生成 0 到RAND_MAX(约 32767)的整数,%3后得到 0-2,加 1 后变为 1-3,正好对应玩家的选择范围。
(2)无效输入处理:提升用户体验
// 循环直到获取有效输入(1-3的整数)
while (scanf("%d", &playerChoice) != 1 || playerChoice < 1 || playerChoice > 3) {
// 清除输入缓冲区的无效字符(如字母、符号)
while (getchar() != '\n');
printf("输入错误!请重新输入1-3之间的数字:");
}
scanf("%d", &playerChoice) != 1:scanf返回成功读取的变量个数,若玩家输入字母(如a),scanf会返回 0,触发循环。
getchar() != '\n':清除输入缓冲区中残留的无效字符(如输入abc后,缓冲区会留下abc\n),避免下一次scanf直接读取这些无效字符,导致死循环。
(3)函数封装:让代码更易维护
代码中封装了 3 个自定义函数:
showRules():负责显示游戏规则,避免主函数中代码冗余。
getChoiceName(int choice):将数字选择(1-3)转换为字符串(石头 / 剪刀 / 布),减少重复代码(如无需在主函数中多次写if-else判断名称)。
judgeWinner(int playerChoice, int computerChoice):专注于胜负判断逻辑,主函数只需调用该函数即可获取结果,符合 “单一职责” 原则,后续修改规则(如增加 “蜥蜴”“斯波克” 扩展玩法)时,只需修改此函数。
四、功能测试与运行效果
1. 编译与运行
将代码复制到编译器中,直接编译运行,会看到以下交互流程:
===================== C语言猜拳游戏 =====================
游戏规则:
1. 玩家选择石头(1)、剪刀(2)或布(3),电脑随机选择
2. 胜负规则:石头克剪刀,剪刀克布,布克石头
3. 每轮结束后显示得分,可选择继续或退出游戏
=====================================================
请输入你的选择(1=石头,2=剪刀,3=布):2
你选择了:剪刀
电脑选择了:布
恭喜!这一轮你赢了!
当前得分:你 1 分 | 电脑 0 分
是否继续游戏?(y=继续,n=退出):y
请输入你的选择(1=石头,2=剪刀,3=布):4
输入错误!请重新输入1-3之间的数字:3
你选择了:布
电脑选择了:石头
恭喜!这一轮你赢了!
当前得分:你 2 分 | 电脑 0 分
是否继续游戏?(y=继续,n=退出):n
===================== 游戏结束 =====================
最终得分:你 2 分 | 电脑 0 分
🎉 恭喜你获得最终胜利!
2. 常见测试场景验证
无效输入测试:输入字母(如a)、负数(如-1)、大于 3 的数字(如4),应提示错误并允许重新输入。
平局测试:玩家和电脑选择相同(如都选 1),应显示平局且得分不变。
随机数测试:多次运行游戏,电脑选择应不固定(如第一次选 2,第二次选 3)。
五、项目扩展与优化建议
基础版猜拳游戏完成后,可根据需求进行扩展,提升游戏的趣味性和复杂度。以下是几个常见的扩展方向:
1. 增加多轮对战模式
当前版本支持无限循环对战,可改为 “3 局 2 胜”“5 局 3 胜” 模式:
增加round变量记录当前局数,maxRounds变量设置总局数(如 3)。
当一方先达到(m.guiguiws.com+1)/2胜时(如 3 局 2 胜),直接结束游戏并宣布最终胜者。
2. 添加历史记录功能
用数组存储每一轮的对战结果(玩家选择、电脑选择、胜负),游戏结束时显示历史记录:
// 定义历史记录结构体
typedef struct {
int playerChoice;
int computerChoice;
int result; // 1=玩家胜,0=平局,-1=电脑胜
} RoundRecord;
RoundRecord records[100]; // 存储最多100轮记录
int wap.guiguiws.com= 0; // 实际记录数
3. 优化电脑 AI(非完全随机)
基础版电脑选择完全随机,可优化为 “根据玩家历史选择调整策略”:
统计玩家最近几次的选择(如石头选得最多),电脑优先选择克制该选项的类型(如玩家常选石头,电脑多选布)。
4. 图形化界面(进阶)
若想提升视觉体验,可使用 EasyX 图形库(Windows 平台)绘制猜拳游戏的图形界面,用图片代替文字显示 “石头”“剪刀”“布”。
六、总结
通过开发 C 语言猜拳游戏,我们掌握了以下核心知识点:
随机数生成:srand()和rand()的配合使用,解决 “电脑选择固定” 的问题。
输入处理:用循环和getchar(www.guiguiws.com)清除缓冲区,处理无效输入,提升用户体验。
函数封装:将重复逻辑(如显示规则、判断胜负)封装为函数,让代码更易维护。
逻辑判断:用if-else和switch实现胜负规则,培养 “拆解问题” 的思维。
这个项目虽然简单,但包含了 C 语言开发的基本流程:需求分析→逻辑设计→代码实现→测试优化。如果能完成扩展功能(如多轮对战、AI 优化),不仅能巩固基础,还能为后续学习更复杂的项目(如贪吃蛇、扫雷)打下基础。
如果在开发过程中遇到问题(如随机数不生效、输入死循环),可以对照本文的代码解析逐步排查,也可以尝试自己修改代码(如调整胜负规则),通过 “实践 + 调试” 提升 C 语言能力。