// 原書片段 (簡化)
void findWinner(int total[]) {
if (total[DEALER] == 21) { printf("The house wins.\n"); return; }
if (total[DEALER] > 21) {
if (total[PLAYER] > 21) { printf("Nobody wins.\n"); return; }
}
if (total[DEALER] < 21) {
if (total[PLAYER] > 21) { printf("The house wins.\n"); return; }
if (total[DEALER] >= total[PLAYER]) { printf("The house wins.\n"); return; }
}
printf("You win!\n");
}
缺陷分析:
DEALER > 21 且 PLAYER <= 21 時未顯式處理 (落入最後 You win),雖結果正確但邏輯隱晦total[DEALER], total[PLAYER] 依賴全域常數定義printf,無法複用於 GUI/網路版
typedef enum {
PLAYER_WIN,
DEALER_WIN,
PUSH // 平局
} GAME_RESULT;
GAME_RESULT judge_winner(int dealer_score, int player_score) {
// 1. 莊家黑傑克 (21點) - 規則通常莊家黑傑克直接贏,除非玩家也有
if (dealer_score == 21) {
return (player_score == 21) ? PUSH : DEALER_WIN;
}
// 2. 莊家爆牌
if (dealer_score > 21) {
if (player_score > 21) return PUSH; // 雙爆平局
return PLAYER_WIN; // 莊家爆、玩家未爆 -> 玩家贏
}
// 3. 莊家未爆 (< 21)
if (player_score > 21) return DEALER_WIN; // 玩家爆 -> 莊家贏
if (dealer_score > player_score) return DEALER_WIN; // 莊家點大 -> 莊家贏
if (dealer_score < player_score) return PLAYER_WIN; // 玩家點大 -> 玩家贏
return PUSH; // 點數相同 -> 平局
}
優勢:
assert(judge_winner(22, 19) == PLAYER_WIN))
void declare_winner(GAME_RESULT result) {
switch(result) {
case PLAYER_WIN: puts("你贏了!\a"); break; // \a 蜂鳴器
case DEALER_WIN: puts("你輸了。"); break;
case PUSH: puts("平局。"); break;
}
}
主流程調用:
GAME_RESULT result = judge_winner(dealer.score, player.score);
declare_winner(result);
若需區分「天生 21」(兩張牌 A+10點) 與「多張牌 21」,擴充 GAMER 結構體:
typedef struct {
int low, high, score;
int num_cards; // 手牌數
bool is_blackjack; // 是否天生 21
} GAMER;
判斷邏輯相應調整:
if (dealer.is_blackjack) {
return player.is_blackjack ? PUSH : DEALER_WIN;
}
if (player.is_blackjack) return PLAYER_WIN; // 玩家黑傑克通常賠 3:2
| 莊家點數 | 玩家點數 | 原始輸出 | 重構結果 | 備註 |
|---|---|---|---|---|
| 21 | 20 | The house wins | DEALER_WIN | 莊家 21 直接贏 |
| 21 | 21 | The house wins | PUSH | 重構修正:雙方 21 平局 |
| 22 | 20 | You win | PLAYER_WIN | 莊家爆牌 |
| 22 | 23 | Nobody wins | PUSH | 雙爆平局 |
| 19 | 22 | The house wins | DEALER_WIN | 玩家爆牌 |
| 19 | 20 | You win | PLAYER_WIN | 玩家點大 |
| 20 | 19 | The house wins | DEALER_WIN | 莊家點大 (含平局莊家贏規則) |
| 18 | 18 | The house wins | PUSH | 重構修正:點數相同平局 |
關鍵修正:原始代碼在「莊家 < 21 且玩家 ≤ 莊家」時一律莊家贏,包含平局。標準 21 點規則通常點數相同為平局 (Push),重構版修正此點。
#include
void test_judge_winner() {
assert(judge_winner(21, 20) == DEALER_WIN);
assert(judge_winner(21, 21) == PUSH);
assert(judge_winner(22, 19) == PLAYER_WIN);
assert(judge_winner(22, 23) == PUSH);
assert(judge_winner(19, 22) == DEALER_WIN);
assert(judge_winner(19, 20) == PLAYER_WIN);
assert(judge_winner(20, 19) == DEALER_WIN);
assert(judge_winner(18, 18) == PUSH);
assert(judge_winner(17, 21) == PLAYER_WIN); // 玩家 21
printf("All judge_winner tests passed.\n");
}
編譯時加 -DNDEBUG 可移除斷言,零成本生產環境部署。
勝負判斷是遊戲核心規則,重構關鍵在於:
printf,語意明確judge_winner() 無副作用,輸入輸出確定,可測試declare_winner() 只負責呈現,支援多介面這樣的設計,不僅修復了原始代碼的邏輯漏洞 (平局判斷錯誤),更為未來擴展 (側注、保險、分牌、多副牌、AI 對戰) 奠定堅實基礎。下一篇將呈現完整最終版代碼。