本專案示範一個結構化的 C 語言小專案架構:玩家資料結構體、動態分配/釋放、選單控制、隨機出拳、勝負判定、戰績統計顯示。雖標註「未優化」,但已具備模組化雛形,適合作為專案架構學習範本。
guess_game/ ├── main.c // 程式入口 ├── player.h // 玩家結構體與介面宣告 ├── player.c // 玩家模組實作 ├── game.h // 遊戲邏輯介面 ├── game.c // 遊戲核心實作 └── Makefile // 建置腳本
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define SIZE 20
#define PASSWD_SIZE 20
#define DEFAULT_NAME "player1"
#define DEFAULT_PASS "123456"
#define TRUE 1
// 玩家結構體
typedef struct Player {
char name[SIZE];
char passwd[PASSWD_SIZE];
int total; // 總局數
int victory; // 勝利局數
} Player;
Player *g_player = NULL;
// 建立玩家 (動態分配)
Player *create_player() {
Player *p = (Player*)malloc(sizeof(Player));
if (!p) return NULL;
memset(p, 0, sizeof(Player));
strcpy(p->name, DEFAULT_NAME);
strcpy(p->passwd, DEFAULT_PASS);
p->total = 0;
p->victory = 0;
return p;
}
// 銷毀玩家
void destroy_player() {
if (g_player) {
free(g_player);
g_player = NULL;
}
}
// 選單顯示
void show_menu() {
printf("************************************************************\n");
printf("猜拳遊戲\n");
printf("1.石頭 2.剪刀 3.布 0.退出\n");
printf("************************************************************\n");
}
// 電腦隨機出拳
int computer_gesture() {
return rand() % 3 + 1; // 1=石頭, 2=剪刀, 3=布
}
// 遊戲主控制迴圈
void game_loop() {
int player_choice = 0;
int computer_choice = 0;
int result = 0; // 1=玩家贏, 0=平手, -1=電腦贏
while (TRUE) {
show_menu();
// 輸入驗證
do {
printf("請選擇: ");
if (scanf("%d", &player_choice) != 1) {
while (getchar() != '\n'); // 清理緩衝區
player_choice = -1;
}
} while (player_choice < 0 || player_choice > 3);
if (player_choice == 0) break;
computer_choice = computer_gesture();
// 勝負判定: (player - computer + 3) % 3
// 1=贏, 0=平, 2=輸
int diff = (player_choice - computer_choice + 3) % 3;
g_player->total++;
if (diff == 1) {
printf("恭喜你,你贏了!\n");
g_player->victory++;
} else if (diff == 0) {
printf("平局\n");
} else {
printf("你輸了,再來一局?\n");
}
}
}
// 顯示戰績
void show_stats() {
printf("\t\t排行榜\n\n");
printf("\t*********************\n");
printf("姓名\t總局數\t贏場\t勝率\n");
if (g_player->total > 0) {
double rate = (double)g_player->victory / g_player->total * 100;
printf("%s\t%d\t%d\t%.2f%%\n", g_player->name, g_player->total, g_player->victory, rate);
} else {
printf("%s\t0\t0\t0.00%%\n", g_player->name);
}
}
int main() {
srand((unsigned)time(NULL)); // 僅初始化一次
g_player = create_player();
if (!g_player) {
fprintf(stderr, "玩家建立失敗\n");
return 1;
}
game_loop();
show_stats();
destroy_player();
return 0;
}
| 模組 | 現狀 | 優化建議 |
|---|---|---|
| 玩家管理 | 單一全域指標 | 支援多玩家、鏈表/陣列管理、檔案持久化 |
| 遊戲邏輯 | 硬編碼勝負判定 | 資料驅動:結果查表、可配置規則、擴展手勢 |
| 隨機數 | rand() 低品質 | 使用 random()、/dev/urandom、C++11 <random> |
| 輸入處理 | 簡單 scanf | 完整輸入驗證、支援別名、非阻塞輸入 |
| 介面 | 終端機文字 | ncurses TUI、SDL2 GUI、WebAssembly 網頁版 |
| 建置系統 | 無 | CMake、單元測試 (Unity/CMock)、CI/CD |
g_player 耦合度高gets/scanf 安全性不足這個專案雖小,但包含了 C 專案開發的核心要素:資料建模、記憶體管理、模組邊界、隨機數、使用者互動、統計顯示。建議以此為基礎,逐步導入上述優化,體驗從「能跑」到「好維護、可擴展、可測試」的工程化演進過程。