剪刀石頭布是條件判斷、隨機數、迴圈控制、字串比較的綜合練習題。本文提供功能完整的三局兩勝制版本,包含選單介面、輸入驗證、勝負判定、比分統計,並分析原始碼中常見的邏輯漏洞與優化方向。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
void menu() {
printf("************* 歡迎進入石頭剪刀布遊戲 ************\n");
printf("******************* 1.start > ********************\n");
printf("******************* 0.exit > ********************\n");
printf("************************************************\n");
}
int main() {
int input = 1;
char ch[10];
int n = 0; // 電腦出拳 0:石頭 1:剪刀 2:布
int b = 0; // 玩家出拳編碼
int i = 0; // 局數計數
int j = 0; // 玩家贏局數
int k = 0; // 平局數
int a = 0; // 電腦贏局數
while (input) {
menu();
printf("請選擇> ");
scanf("%d", &input);
switch (input) {
case 1: {
// 三局兩勝制
for (i = 0; i < 3; i++) {
srand((unsigned int)time(NULL)); // 注意:此處重複播種有缺陷
n = rand() % 3;
printf("請出石頭-剪刀-布\n");
scanf("%s", ch);
// 字串轉編碼
if (strcmp(ch, "石頭") == 0) b = 0;
else if (strcmp(ch, "剪刀") == 0) b = 1;
else if (strcmp(ch, "布") == 0) b = 2;
else {
printf("輸入錯誤,請重新輸入\n");
i--; continue;
}
// 勝負判定邏輯
if (b == n) {
printf("平局\n");
k++;
} else if ((b == 0 && n == 1) || // 石頭贏剪刀
(b == 1 && n == 2) || // 剪刀贏布
(b == 2 && n == 0)) { // 布贏石頭
printf("恭喜你贏了\n");
j++;
} else {
printf("很遺憾你輸了\n");
a++;
}
// 提前結束條件
if (j == 2 || a == 2) break;
}
// 最終結果判定
if (j == 2) printf("恭喜你這一局贏了\n");
else if (a == 2) printf("很遺憾這一局你輸了\n");
else if (k == 3) printf("這局是平局\n");
else if (k == 2 && a == 1) printf("很遺憾這一局你輸了\n");
else if (k == 2 && j == 1) printf("恭喜你這一局贏了\n");
break;
}
default:
printf("退出遊戲\n");
system("pause");
exit(0);
}
}
system("pause");
return 0;
}
| 問題 | 原始碼 | 優化方案 |
|---|---|---|
| 重複播種 | 迴圈內每次呼叫 srand(time(NULL)) | 程式啟動時只呼叫一次 srand() |
| 勝負判定冗長 | 6 條 else if 分支 | 使用 (b - n + 3) % 3 == 1 判定玩家贏 |
| 輸入緩衝殘留 | scanf("%s", ch) 遇空白截斷 | 使用 fgets + 去換行,或 scanf("%9s", ch) 限制長度 |
| 魔術數字 | 0,1,2 直接硬編碼 | 定義 enum {ROCK, SCISSORS, PAPER}; |
| 結果判定邏輯複雜 | 多重 if-else 組合 | 封裝 judgeResult(player, computer) 回傳枚舉 |
typedef enum { DRAW = 0, PLAYER_WIN = 1, COMPUTER_WIN = 2 } Result;
Result judge(int player, int computer) {
if (player == computer) return DRAW;
// (player - computer + 3) % 3 == 1 表示玩家贏
// 0-1=-1+3=2%3=2 電腦贏
// 0-2=-2+3=1%3=1 玩家贏
// 1-0=1%3=1 玩家贏
// 1-2=-1+3=2%3=2 電腦贏
// 2-0=2%3=2 電腦贏
// 2-1=1%3=1 玩家贏
return ((player - computer + 3) % 3 == 1) ? PLAYER_WIN : COMPUTER_WIN;
}
// rps.h
typedef enum { ROCK, SCISSORS, PAPER } Gesture;
typedef enum { DRAW, PLAYER_WIN, COMPUTER_WIN } Result;
Gesture getComputerGesture();
Gesture parsePlayerInput(const char *input);
Result judgeRound(Gesture player, Gesture computer);
void printGesture(Gesture g);
void printResult(Result r);
透過這個小專案,你將完整經歷「需求分析 → 邏輯建模 → 代碼實作 → 測試除錯 → 重構優化」的軟體開發週期。建議嘗試自行重寫優化版,並擴展上述進階功能,累積實戰經驗。