我們用 C 語言代碼實現了一個簡單的控制臺版三子棋游戲,代碼分為三個部分,分別是頭文件game.h
中定義的函數聲明以及兩個源文件game.c
和test.c、
game.c
文件。
1.頭文件(game.h
)部分
首先包含了<stdio.h>
(用于標準輸入輸出操作,像printf
、scanf
函數的使用)、<stdlib.h>
(包含了如rand
、srand
等函數,用于生成隨機數以及內存分配等相關操作)和<time.h>
(獲取時間相關信息,用于初始化隨機數生成器)這幾個常用的標準庫頭文件。
接著通過#define
定義了ROW
和COL
兩個宏常量,都賦值為 3,代表三子棋棋盤的行數和列數。
最后聲明了五個函數,這些函數分別用于初始化棋盤、打印棋盤、玩家下棋、電腦下棋以及判斷游戲的輸贏狀態,它們的具體實現在game.c
文件中。
#include<stdio.h>
#include<stdlib.h>
#include<time.h>#define ROW 3
#define COL 3//初始化棋盤
void InitBoard(char board[ROW][COL], int row, int col);//打印棋盤
void DispalyBoard(char board[ROW][COL], int row, int col);//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);//電腦下棋
void ComputerMove(char board[ROW][COL], int row, int col);//'X'表示玩家獲勝
//'O'表示電腦獲勝
//'Q'表示平局
//'C'表示游戲未結束
char IsWin(char board[ROW][COL], int row, int col);
2.源文件(game.c
)部分
(InitBoard
函數)
這個函數的作用是初始化棋盤,通過兩層嵌套的for
循環遍歷整個二維字符數組(即棋盤),將每個元素都設置為空字符' '
,表示棋盤初始狀態下各個格子都是空白的,沒有棋子。
(DispalyBoard
函數)
該函數用于在控制臺打印出三子棋棋盤的樣子。它通過兩層循環來實現,外層循環控制行數,內層循環在每行中先打印出每個格子對應的字符(棋子符號或者空白),并且在格子之間用|
進行分隔,每一行數據打印完后,若不是最后一行,還會打印出一行分割線(由---
和|
組成),這樣就能清晰地展示出棋盤的布局和當前棋子的放置情況。
(PlayerMove
函數)
這個函數實現了玩家下棋的操作邏輯。首先提示玩家輸入下棋的坐標,然后通過while
循環不斷接收玩家輸入的坐標,并進行合法性檢查:一是檢查坐標是否在棋盤的有效范圍內(1 到row
或col
之間),二是檢查輸入坐標對應的格子是否為空(沒有被其他棋子占據),只有當坐標合法且對應格子為空時,才會將玩家的棋子(用'X'
表示)放置到該格子中,隨后跳出循環;如果坐標不合法或者格子已被占用,則會相應地提示玩家重新輸入。
(ComputerMove
函數)
該函數用于實現電腦下棋的操作邏輯。首先會提示 “電腦下棋:>”,然后通過while
循環不斷生成隨機坐標(利用rand
函數生成 0 到row - 1
以及 0 到col - 1
范圍內的隨機數作為棋盤的行列索引),接著檢查該隨機坐標對應的格子是否為空,如果為空,就將電腦的棋子(用'O'
表示)放置到該格子中,然后跳出循環;這樣電腦就能隨機地在棋盤上的空白格子中放置棋子了。需要注意的是,這里使用rand
函數前一般需要在main
函數中通過srand((unsigned int)time(NULL));
初始化隨機數生成器,以保證每次游戲電腦下棋的隨機性。
(IsFull
函數)
這個函數用于判斷棋盤是否已滿,也就是判斷棋盤上是否還有空白的格子。通過兩層嵌套的for
循環遍歷整個棋盤,如果發現有任意一個格子的值為' '
(即空白),就返回 0,表示棋盤未滿;如果整個棋盤遍歷完都沒有發現空白格子,那就返回 1,表示棋盤已滿。
(IsWin
函數)
此函數用于判斷游戲的輸贏狀態。它首先按照行、列、對角線的順序來檢查是否有一方的棋子連成了三子一線的情況:
在檢查行時,通過外層循環遍歷每一行,然后比較該行的三個格子的棋子是否相同且不為空白,如果滿足條件,就返回該行棋子對應的字符('X'
或者'O'
),表示該行對應的玩家或電腦獲勝。
檢查列的邏輯類似,通過內層循環遍歷每一列,比較每列三個格子的棋子情況來判斷是否有一方獲勝。
對于對角線,分別檢查兩條對角線上的格子棋子是否相同且不為空白來確定是否有一方獲勝。
如果經過上述檢查都沒有發現有一方獲勝,那么接著調用IsFull
函數來判斷棋盤是否已滿,如果已滿則返回'Q'
,表示平局;如果棋盤未滿,則返回'C'
,表示游戲還未結束,需要繼續進行。
#include"game.h"void InitBoard(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;for (i = 0;i < row;i++){for (j = 0;j < col;j++){board[i][j] = ' ';}}
}void DispalyBoard(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0;i < row;i ++ ){//打印數據int j = 0;for (j = 0;j < col;j++){printf(" %c ", board[i][j]);if (j < col - 1)printf("|");}printf("\n");//打印分割信息if (i < row - 1){int j = 0;for (j = 0;j < col;j++){printf("---");if (j < col - 1)printf("|");}printf("\n");}}
}void PlayerMove(char board[ROW][COL], int row, int col)
{int x = 0;int y = 0;printf("玩家下棋:>\n");while (1){printf("請輸入坐標:>");scanf("%d %d", &x, &y);//坐標范圍合法判度if (x >= 1 && x <= row && y >= 1 && y <= col){if (board[x - 1][y - 1] == ' '){board[x - 1][y - 1] = 'X';break;}else{printf("坐標被占用,請選擇其他位置\n");}}else{printf("坐標非法,請重新輸入\n");}}
}void ComputerMove(char board[ROW][COL], int row, int col)
{printf("電腦下棋:>\n");int x = 0;int y = 0;while (1){x = rand() % row;//0~2y = rand() % col;//0~2if (board[x][y] == ' '){board[x][y] = 'O';break;}}}//判斷棋盤狀態,返回值含義://'X'表示玩家獲勝//'O'表示電腦獲勝//'Q'表示平局//'C'表示游戲未結束//滿了就返回1
//不滿就返回0
int IsFull(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;for (i = 0;i < row;i++){for (j = 0;j < col;j++){if (board[i][j] == ' '){return 0;}}}return 1;
}char IsWin(char board[ROW][COL], int row, int col)
{//行int i = 0;for (i = 0;i < row;i++){if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' '){return board[i][1];}}//列int j = 0;for (j = 0;j< col;j++){if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j]!= ' '){return board[1][j];}}//對角線if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' '){return board[1][1];}if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' '){return board[1][1];}//沒有人贏,判斷平局if (IsFull(board, row, col)){return 'Q';}//游戲繼續return 'C';}
2.源文件(test.c
)部分
(menu
函數)
這個函數很簡單,就是在控制臺打印出游戲的菜單,顯示了兩個選項,分別是1.play
用于開始游戲和0.exit
用于退出游戲,使玩家能清晰地看到可操作的選擇。
(game
函數)
game
函數實現了一局三子棋游戲的完整流程。首先定義了一個字符變量ret
用于記錄游戲的輸贏狀態,以及一個二維字符數組board
來表示棋盤,接著調用InitBoard
函數初始化棋盤,并通過DispalyBoard
函數顯示初始棋盤。然后進入一個while
循環,在循環中先讓玩家下棋(調用PlayerMove
函數),之后調用IsWin
函數判斷此時游戲是否有輸贏結果,如果游戲未結束(返回值為'C'
),則顯示當前棋盤狀態(再次調用DispalyBoard
函數),接著讓電腦下棋(調用ComputerMove
函數),同樣再判斷游戲輸贏情況,如此反復,直到游戲有了輸贏或者平局的結果(IsWin
函數返回值不為'C'
),最后根據ret
的值輸出相應的游戲結果(玩家贏、電腦贏或者平局),并再次顯示棋盤。
(main
函數)
main
函數是整個程序的入口點。首先通過srand((unsigned int)time(NULL));
初始化隨機數生成器,確保電腦下棋的隨機性。然后進入一個do-while
循環,在循環中先調用menu
函數顯示游戲菜單,接著讓用戶輸入選擇,根據用戶輸入的不同選項(通過switch
語句判斷),如果輸入1
則調用game
函數開始一局游戲,如果輸入0
則退出游戲,其他輸入則提示選擇錯誤,循環繼續,直到用戶選擇退出游戲為止,最后返回 0 表示程序正常結束。
#include"game.h"void menu()
{printf("****************************************\n");printf("************ 1.play **************\n");printf("************ 0.exit **************\n");printf("****************************************\n");
}void game()
{char ret = 0;char board[ROW][COL] = { 0 };//初始化棋盤的函數InitBoard(board, ROW, COL);DispalyBoard(board, ROW, COL);//下棋while (1){PlayerMove(board, ROW, COL);//判斷輸贏ret = IsWin(board, ROW, COL);if (ret != 'C'){break;}//顯示棋盤DispalyBoard(board, ROW, COL);ComputerMove(board, ROW, COL);//判斷輸贏ret = IsWin(board, ROW, COL);if (ret != 'C'){break;}//顯示棋盤DispalyBoard(board, ROW, COL);}if (ret == 'X'){printf("玩家贏了\n");}else if (ret == 'O'){printf("電腦贏了\n");}else{printf("平局\n");}DispalyBoard(board, ROW, COL);} int main()
{srand((unsigned int)time(NULL));//設置隨機數生成起點的int input = 0;do{menu();//打印菜單printf("請選擇:>");scanf("%d", &input);switch (input){case 1:game();break;case 0:printf("退出游戲\n");break;default:printf("選擇錯誤\n");break;}} while (input);return 0;
}