C語言實現(xiàn)五子棋功能全解析
1、game.h
game.h:自定義頭文件,用于:
- 庫函數(shù)頭文件的包含
- 符號與結(jié)構(gòu)的聲明
- 函數(shù)的定義
//防止頭文件被重復包含 #pragma once //頭文件的包含 #include<stdio.h> #include<stdlib.h> #include<time.h> //符號的定義:使棋盤的大小可以跟著row和col的改變而改變 #define ROW 5 #define COL 5 //函數(shù)的聲明 //棋盤初始化 void BoardInit(char arr[ROW][COL], int row, int col); //打印棋盤 void BoardPrint(char arr[ROW][COL], int row, int col); //玩家下棋 void PlayerMove(char arr[ROW][COL], int row, int col); //電腦下棋 void ComputerMove(char arr[ROW][COL], int row, int col); //判斷輸贏 char IsWin(char arr[ROW][COL], int row, int col); //判斷棋盤是否滿了 int IsFull(char board[ROW][COL], int row, int col);
2、test.c
test.c:用于游戲邏輯的測試
#define _CRT_SECURE_NO_WARNINGS 1 //自定義頭文件的包含 #include"game.h" void menu() { printf("================================\n"); printf("========= 1. play ==========\n"); printf("========= 0. exit ==========\n"); printf("================================\n"); } //游戲邏輯的實現(xiàn) void game() { //定義一個二維數(shù)組來存儲下棋的數(shù)據(jù) char arr[ROW][COL] = { 0 }; //棋盤初始化 BoardInit(arr, ROW, COL); //打印棋盤 BoardPrint(arr, ROW, COL); char ch = 0; while (1) { //玩家下棋 PlayerMove(arr, ROW, COL); //打印棋盤 BoardPrint(arr, ROW, COL); //判斷輸贏 ch = IsWin(arr, ROW, COL); if (ch != 'C') break; //電腦下棋 ComputerMove(arr, ROW, COL); //打印棋盤 BoardPrint(arr, ROW, COL); //判斷輸贏 ch = IsWin(arr, ROW, COL); if (ch != 'C') break; } if (ch == '*') printf("直接拿下!\n"); else if (ch == '#') printf("你竟然打不過人機!\n"); else printf("平局,得加油?。n"); } int main() { int input = 0; //設(shè)置隨機數(shù)種子 srand((unsigned int)time(NULL)); 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; }
3、game.c
game.c:游戲功能的實現(xiàn)
#define _CRT_SECURE_NO_WARNINGS 1 //自定義頭文件的包含 #include"game.h" //函數(shù)的定義 //棋盤初始化 void BoardInit(char arr[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { arr[i][j] = ' '; } } } //打印棋盤 void BoardPrint(char arr[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { //打印分割豎向分割 for (j = 0; j < col; j++) { printf(" %c ", arr[i][j]); if (j < col - 1) printf("|"); } //一行完畢之后打印分隔符 printf("\n"); //打印橫向分割 if (i < row - 1) //最后一行不打印橫線分隔符 { for (j = 0; j < col; j++) { printf("---"); if (j < col - 1) printf("|"); } } //一行完畢之后打印分隔符 printf("\n"); } } //玩家下棋 void PlayerMove(char arr[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)) { //把玩家坐標對應數(shù)組下標 x -= 1; y -= 1; //判斷坐標是否被占用 if (arr[x][y] == ' ') { arr[x][y] = '*'; //假設(shè)玩家為*號 break; } else { printf("該坐標已被占用\n"); } } else { printf("坐標非法\n"); } } } //電腦下棋 void ComputerMove(char arr[ROW][COL], int row, int col) { printf("電腦下棋\n"); while (1) { //在主函數(shù)生成種子srand //隨機生成范圍內(nèi)的坐標 int x = rand() % row; int y = rand() % col; //判斷坐標是否被占用 if (arr[x][y] == ' ') { arr[x][y] = '#'; //假設(shè)電腦為#號 break; } } } //判斷輸贏 char IsWin(char board[ROW][COL], int row, int col) { /* * 約定返回*代表玩家贏 * 返回#代表電腦贏 * 返回D代表平局 * 返回C代表繼續(xù) */ int i = 0; int j = 0; //判斷行 for (i = 0; i < row; i++) { int count = 0; //標記相同棋子的個數(shù) for (j = 0; j < col - 1; j++) { if (board[i][j] == board[i][j + 1] && board[i][j] != ' ') count++; } if (count == col - 1) //一次判斷有兩個棋子 return board[i][j]; } //判斷列 for (i = 0; i < col; i++) { int count = 0; for (j = 0; j < row - 1; j++) { if (board[j][i] == board[j + 1][i] && board[j][i] != ' ') { count++; } } if (count == row - 1) return board[j][i]; } //判斷兩條斜邊 //第一條 int count = 0; for (i = 0, j = 0; i < row - 1 && j < col - 1; i++, j++) { if (board[i][j] == board[i + 1][j + 1] && board[i][j] != ' ') count++; } if (count == row - 1) return board[i][j]; //第二條 count = 0; //把count重新置為0(易錯) //注意:這里i+1,j-1,所以i小于row-1,j>0,而不是i<row,j>=0(易錯) for (i = 0, j = col - 1; i < row - 1 && j > 0; i++, j--) { if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' ') count++; } if (count == row - 1) return board[i][j]; //判斷棋盤是否滿了 if (IsFull(board, row, col)) { return 'D'; } //如果上述情況都沒有返回,游戲繼續(xù) return 'C'; } //判斷棋盤是否滿了 if (IsFull(board, row, col)) { return 'D'; } //如果上述情況都沒有返回,游戲繼續(xù) return 'C'; } //判斷棋盤是否滿了 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; //有空格就返回0 } } return 1; }
4、游戲功能詳解
(1)、棋盤初始化
void BoardInit(char arr[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { arr[i][j] = ' '; } } }
(2)、棋盤的打印
void BoardPrint(char arr[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { //打印分割豎向分割 for (j = 0; j < col; j++) { printf(" %c ", arr[i][j]); if (j < col - 1) printf("|"); } //一行完畢之后打印分隔符 printf("\n"); //打印橫向分割 if (i < row - 1) //最后一行不打印橫線分隔符 { for (j = 0; j < col; j++) { printf("---"); if (j < col - 1) printf("|"); } } //一行完畢之后打印分隔符 printf("\n"); } }
(3)、玩家下棋
void PlayerMove(char arr[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)) { //把玩家坐標對應數(shù)組下標 x -= 1; y -= 1; //判斷坐標是否被占用 if (arr[x][y] == ' ') { arr[x][y] = '*'; //假設(shè)玩家為*號 break; } else { printf("該坐標已被占用\n"); } } else { printf("坐標非法\n"); } } }
(4)、電腦下棋
void ComputerMove(char arr[ROW][COL], int row, int col) { printf("電腦下棋\n"); while (1) { //在主函數(shù)生成種子srand //隨機生成范圍內(nèi)的坐標 int x = rand() % row; int y = rand() % col; //判斷坐標是否被占用 if (arr[x][y] == ' ') { arr[x][y] = '#'; //假設(shè)電腦為#號 break; } } }
(5)、判斷游戲輸贏
char IsWin(char board[ROW][COL], int row, int col) { /* * 約定返回*代表玩家贏 * 返回#代表電腦贏 * 返回D代表平局 * 返回C代表繼續(xù) */ int i = 0; int j = 0; //判斷行 for (i = 0; i < row; i++) { int count = 0; //標記相同棋子的個數(shù) for (j = 0; j < col - 1; j++) { if (board[i][j] == board[i][j + 1] && board[i][j] != ' ') count++; } if (count == col - 1) //一次判斷有兩個棋子 return board[i][j]; } //判斷列 for (i = 0; i < col; i++) { int count = 0; for (j = 0; j < row - 1; j++) { if (board[j][i] == board[j + 1][i] && board[j][i] != ' ') { count++; } } if (count == row - 1) return board[j][i]; } //判斷兩條斜邊 //第一條 int count = 0; for (i = 0, j = 0; i < row - 1 && j < col - 1; i++, j++) { if (board[i][j] == board[i + 1][j + 1] && board[i][j] != ' ') count++; } if (count == row - 1) return board[i][j]; //第二條 count = 0; //把count重新置為0(易錯) //注意:這里i+1,j-1,所以i小于row-1,j>0,而不是i<row,j>=0(易錯) for (i = 0, j = col - 1; i < row - 1 && j > 0; i++, j--) { if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' ') count++; } if (count == row - 1) return board[i][j]; //判斷棋盤是否滿了 if (IsFull(board, row, col)) { return 'D'; } //如果上述情況都沒有返回,游戲繼續(xù) return 'C'; } //判斷棋盤是否滿了 if (IsFull(board, row, col)) { return 'D'; } //如果上述情況都沒有返回,游戲繼續(xù) return 'C'; }
(6)、判斷棋盤是否滿了
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; //有空格就返回0 } } return 1; }
5、AI算法下棋
大家可以發(fā)現(xiàn),在上面的代碼中,電腦下棋是非常笨拙的,因為電腦產(chǎn)生的坐標是隨機的,即不會攔截玩家,也不會判斷自己,所以這里我們可以設(shè)計一個小小的算法來讓電腦變得聰明起來,讓它擁有攔截和判斷功能。具體思路和代碼如下:
(1)、判斷自己是否會贏(CheckComputer)
//電腦檢查自己是否會贏 //約定如果在函數(shù)內(nèi)部成功判斷就返回1 //判斷失敗則返回0 int CheckComputer(char board[ROW][COL], int row, int col) { int i = 0; int j = 0; //判斷每一行是否有兩個相連的棋子,如果有,且第三個棋格為空,則落棋 for (i = 0; i < row; i++) { if (board[i][0] == board[i][1] && board[i][0] == '#' && board[i][2] == ' ') { board[i][2] = '#'; return 1; //成功判斷,返回1 } if (board[i][0] == board[i][2] && board[i][0] == '#' && board[i][1] == ' ') { board[i][1] = '#'; return 1; } if (board[i][1] == board[i][2] && board[i][1] == '#' && board[i][0] == ' ') { board[i][0] = '#'; return 1; } } //判斷每一列是否有兩個相連的棋子,如果有,且第三個棋格為空,則落棋 for (j = 0; j < col; j++) { if (board[0][j] == board[1][j] && board[0][j] == '#' && board[2][j] == ' ') { board[2][j] = '#'; return 1; } if (board[0][j] == board[2][j] && board[0][j] == '#' && board[1][j] == ' ') { board[1][j] = '#'; return 1; }if (board[1][j] == board[2][j] && board[1][j] == '#' && board[0][j] == ' ') { board[0][j] = '#'; return 1; } } //判斷兩條對角線是否有兩個相連的棋子,如果有,且第三個棋格為空,則落棋 { //第一條 if (board[0][0] == board[1][1] && board[0][0] == '#' && board[2][2] == ' ') { board[2][2] = '#'; return 1; } if (board[0][0] == board[2][2] && board[0][0] == '#' && board[1][1] == ' ') { board[1][1] = '#'; return 1; } if (board[1][1] == board[2][2] && board[1][1] == '#' && board[0][0] == ' ') { board[0][0] = '#'; return 1; } //第二條 if (board[0][2] == board[1][1] && board[0][2] == '#' && board[2][0] == ' ') { board[2][0] = '#'; return 1; } if (board[0][2] == board[2][0] && board[0][2] == '#' && board[1][1] == ' ') { board[1][1] = '#'; return 1; } if (board[1][1] == board[2][0] && board[1][1] == '#' && board[0][2] == ' ') { board[0][2] = '#'; return 1; } //如果上面都沒返回,說明不符合贏的條件,返回0 return 0; } }
(2)、對玩家進行攔截(CheckPlayer)
//電腦檢查玩家是否會贏(邏輯和CheckComputer完全相同) //約定成功攔截返回1 //無需攔截或者攔截不了返回0 int CheckPlayer(char board[ROW][COL], int row, int col) { int i = 0; int j = 0; //判斷每一行是否有兩個相連的棋子,如果有,且第三個棋格為空,則攔截 for (i = 0; i < row; i++) { if (board[i][0] == board[i][1] && board[i][0] == '*' && board[i][2] == ' ') { board[i][2] = '#'; return 1; //成功攔截,返回1 } if (board[i][0] == board[i][2] && board[i][0] == '*' && board[i][1] == ' ') { board[i][1] = '#'; return 1; } if (board[i][1] == board[i][2] && board[i][1] == '*' && board[i][0] == ' ') { board[i][0] = '#'; return 1; } } //判斷每一列是否有兩個相連的棋子,如果有,且第三個棋格為空,則攔截 for (j = 0; j < col; j++) { if (board[0][j] == board[1][j] && board[0][j] == '*' && board[2][j] == ' ') { board[2][j] = '#'; return 1; } if (board[0][j] == board[2][j] && board[0][j] == '*' && board[1][j] == ' ') { board[1][j] = '#'; return 1; }if (board[1][j] == board[2][j] && board[1][j] == '*' && board[0][j] == ' ') { board[0][j] = '#'; return 1; } } //判斷兩條對角線是否有兩個相連的棋子,如果有,且第三個棋格為空,則攔截 { //第一條 if (board[0][0] == board[1][1] && board[0][0] == '*' && board[2][2] == ' ') { board[2][2] = '#'; return 1; } if (board[0][0] == board[2][2] && board[0][0] == '*' && board[1][1] == ' ') { board[1][1] = '#'; return 1; } if (board[1][1] == board[2][2] && board[1][1] == '*' && board[0][0] == ' ') { board[0][0] = '#'; return 1; } //第二條 if (board[0][2] == board[1][1] && board[0][2] == '*' && board[2][0] == ' ') { board[2][0] = '#'; return 1; } if (board[0][2] == board[2][0] && board[0][2] == '*' && board[1][1] == ' ') { board[1][1] = '#'; return 1; } if (board[1][1] == board[2][0] && board[1][1] == '*' && board[0][2] == ' ') { board[0][2] = '#'; return 1; } //如果上面都沒返回,說明不符合攔截的條件,返回0 return 0; } }
注意:我這里采用的判斷方法是枚舉,由于五子棋的枚舉情況比較復雜,而我目前也沒想到更好的算法來進行判斷,所以這里我只寫了三子棋的AI判斷代碼,如果有大佬有更好的算法或者判斷思路,歡迎在評論區(qū)留言。
(3)、加入AI算法后game.c的改動
上面我們已經(jīng)完成了CheckComputer和CheckPlayer這兩個函數(shù)的定義,現(xiàn)在我們只需要把這兩個函數(shù)實現(xiàn)放入到game.c中并且在在電腦下棋(ComputerMove)中調(diào)用這兩個函數(shù)即可。
//電腦下棋 void ComputerMove(char board[ROW][COL], int row, int col) { printf("電腦下棋\n"); //定義兩個標識符變量來接收兩個判斷函數(shù)的返回值 int flag1 = 0; int flag2 = 0; flag1 = CheckComputer(board, row, col); //如果flag1 == 0 時才進行flag2 的判斷,避免當二者都為1時下兩步棋(易錯) if (flag1 == 0) { flag2 = CheckPlayer(board, row, col); } if (flag1 == 0 && flag2 == 0) //當CheckComputer和CheckPlayer都沒落棋時,就隨機下 { while (1) { //在主函數(shù)生成種子srand //隨機生成范圍內(nèi)的坐標 int x = rand() % row; int y = rand() % col; //判斷坐標是否被占用 if (board[x][y] == ' ') { board[x][y] = '#'; //假設(shè)電腦為#號 break; } } } }
注意:這里的AI算法只適用于三子棋,如果要使用的話需要把頭文件中的ROW和COL改為3,同時不要忘記在頭文件中對兩個判斷函數(shù)進行聲明。
到此這篇關(guān)于C語言實現(xiàn)五子棋功能全解析的文章就介紹到這了,更多相關(guān)C語言五子棋內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實現(xiàn)LeetCode(10.正則表達式匹配)
這篇文章主要介紹了C++實現(xiàn)LeetCode(10.正則表達式匹配),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07C語言實現(xiàn)學生宿舍信息管理系統(tǒng)課程設(shè)計
這篇文章主要為大家詳細介紹了C語言實現(xiàn)學生宿舍信息管理系統(tǒng)課程設(shè)計,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03C語言靜態(tài)動態(tài)兩版本通訊錄實戰(zhàn)源碼
這篇文章主要為大家?guī)砹薈語言實現(xiàn)靜態(tài)動態(tài)兩版本的通訊錄實戰(zhàn)源碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2022-02-02