C語(yǔ)言實(shí)現(xiàn)五子棋功能全解析
1、game.h
game.h:自定義頭文件,用于:
- 庫(kù)函數(shù)頭文件的包含
- 符號(hào)與結(jié)構(gòu)的聲明
- 函數(shù)的定義
//防止頭文件被重復(fù)包含 #pragma once //頭文件的包含 #include<stdio.h> #include<stdlib.h> #include<time.h> //符號(hào)的定義:使棋盤的大小可以跟著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:用于游戲邏輯的測(cè)試
#define _CRT_SECURE_NO_WARNINGS 1
//自定義頭文件的包含
#include"game.h"
void menu()
{
printf("================================\n");
printf("========= 1. play ==========\n");
printf("========= 0. exit ==========\n");
printf("================================\n");
}
//游戲邏輯的實(shí)現(xiàn)
void game()
{
//定義一個(gè)二維數(shù)組來(lái)存儲(chǔ)下棋的數(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("你竟然打不過(guò)人機(jī)!\n");
else
printf("平局,得加油?。n");
}
int main()
{
int input = 0;
//設(shè)置隨機(jī)數(shù)種子
srand((unsigned int)time(NULL));
do {
//菜單
menu();
printf("請(qǐng)選擇:>");
scanf("%d", &input);
switch (input) {
case 1:
//玩游戲
game();
break;
case 0:
printf("退出游戲\n");
break;
default:
printf("輸入錯(cuò)誤,請(qǐng)重新輸入!\n");
break;
}
} while (input);
return 0;
}3、game.c
game.c:游戲功能的實(shí)現(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)
{
//獲取玩家坐標(biāo)
int x = 0;
int y = 0;
printf("玩家下棋\n");
while (1)
{
printf("請(qǐng)輸入坐標(biāo):>");
scanf("%d %d", &x, &y);
//判斷坐標(biāo)合法性
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
//把玩家坐標(biāo)對(duì)應(yīng)數(shù)組下標(biāo)
x -= 1;
y -= 1;
//判斷坐標(biāo)是否被占用
if (arr[x][y] == ' ')
{
arr[x][y] = '*'; //假設(shè)玩家為*號(hào)
break;
}
else
{
printf("該坐標(biāo)已被占用\n");
}
}
else
{
printf("坐標(biāo)非法\n");
}
}
}
//電腦下棋
void ComputerMove(char arr[ROW][COL], int row, int col)
{
printf("電腦下棋\n");
while (1)
{
//在主函數(shù)生成種子srand
//隨機(jī)生成范圍內(nèi)的坐標(biāo)
int x = rand() % row;
int y = rand() % col;
//判斷坐標(biāo)是否被占用
if (arr[x][y] == ' ')
{
arr[x][y] = '#'; //假設(shè)電腦為#號(hào)
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; //標(biāo)記相同棋子的個(gè)數(shù)
for (j = 0; j < col - 1; j++)
{
if (board[i][j] == board[i][j + 1] && board[i][j] != ' ')
count++;
}
if (count == col - 1) //一次判斷有兩個(gè)棋子
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(易錯(cuò))
//注意:這里i+1,j-1,所以i小于row-1,j>0,而不是i<row,j>=0(易錯(cuò))
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';
}
//如果上述情況都沒(méi)有返回,游戲繼續(xù)
return 'C';
}
//判斷棋盤是否滿了
if (IsFull(board, row, col))
{
return 'D';
}
//如果上述情況都沒(méi)有返回,游戲繼續(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)
{
//獲取玩家坐標(biāo)
int x = 0;
int y = 0;
printf("玩家下棋\n");
while (1)
{
printf("請(qǐng)輸入坐標(biāo):>");
scanf("%d %d", &x, &y);
//判斷坐標(biāo)合法性
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
//把玩家坐標(biāo)對(duì)應(yīng)數(shù)組下標(biāo)
x -= 1;
y -= 1;
//判斷坐標(biāo)是否被占用
if (arr[x][y] == ' ')
{
arr[x][y] = '*'; //假設(shè)玩家為*號(hào)
break;
}
else
{
printf("該坐標(biāo)已被占用\n");
}
}
else
{
printf("坐標(biāo)非法\n");
}
}
}
(4)、電腦下棋
void ComputerMove(char arr[ROW][COL], int row, int col)
{
printf("電腦下棋\n");
while (1)
{
//在主函數(shù)生成種子srand
//隨機(jī)生成范圍內(nèi)的坐標(biāo)
int x = rand() % row;
int y = rand() % col;
//判斷坐標(biāo)是否被占用
if (arr[x][y] == ' ')
{
arr[x][y] = '#'; //假設(shè)電腦為#號(hào)
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; //標(biāo)記相同棋子的個(gè)數(shù)
for (j = 0; j < col - 1; j++)
{
if (board[i][j] == board[i][j + 1] && board[i][j] != ' ')
count++;
}
if (count == col - 1) //一次判斷有兩個(gè)棋子
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(易錯(cuò))
//注意:這里i+1,j-1,所以i小于row-1,j>0,而不是i<row,j>=0(易錯(cuò))
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';
}
//如果上述情況都沒(méi)有返回,游戲繼續(xù)
return 'C';
}
//判斷棋盤是否滿了
if (IsFull(board, row, col))
{
return 'D';
}
//如果上述情況都沒(méi)有返回,游戲繼續(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),在上面的代碼中,電腦下棋是非常笨拙的,因?yàn)殡娔X產(chǎn)生的坐標(biāo)是隨機(jī)的,即不會(huì)攔截玩家,也不會(huì)判斷自己,所以這里我們可以設(shè)計(jì)一個(gè)小小的算法來(lái)讓電腦變得聰明起來(lái),讓它擁有攔截和判斷功能。具體思路和代碼如下:
(1)、判斷自己是否會(huì)贏(CheckComputer)
//電腦檢查自己是否會(huì)贏
//約定如果在函數(shù)內(nèi)部成功判斷就返回1
//判斷失敗則返回0
int CheckComputer(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
//判斷每一行是否有兩個(gè)相連的棋子,如果有,且第三個(gè)棋格為空,則落棋
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;
}
}
//判斷每一列是否有兩個(gè)相連的棋子,如果有,且第三個(gè)棋格為空,則落棋
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;
}
}
//判斷兩條對(duì)角線是否有兩個(gè)相連的棋子,如果有,且第三個(gè)棋格為空,則落棋
{
//第一條
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;
}
//如果上面都沒(méi)返回,說(shuō)明不符合贏的條件,返回0
return 0;
}
}(2)、對(duì)玩家進(jìn)行攔截(CheckPlayer)
//電腦檢查玩家是否會(huì)贏(邏輯和CheckComputer完全相同)
//約定成功攔截返回1
//無(wú)需攔截或者攔截不了返回0
int CheckPlayer(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
//判斷每一行是否有兩個(gè)相連的棋子,如果有,且第三個(gè)棋格為空,則攔截
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;
}
}
//判斷每一列是否有兩個(gè)相連的棋子,如果有,且第三個(gè)棋格為空,則攔截
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;
}
}
//判斷兩條對(duì)角線是否有兩個(gè)相連的棋子,如果有,且第三個(gè)棋格為空,則攔截
{
//第一條
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;
}
//如果上面都沒(méi)返回,說(shuō)明不符合攔截的條件,返回0
return 0;
}
}注意:我這里采用的判斷方法是枚舉,由于五子棋的枚舉情況比較復(fù)雜,而我目前也沒(méi)想到更好的算法來(lái)進(jìn)行判斷,所以這里我只寫(xiě)了三子棋的AI判斷代碼,如果有大佬有更好的算法或者判斷思路,歡迎在評(píng)論區(qū)留言。
(3)、加入AI算法后game.c的改動(dòng)
上面我們已經(jīng)完成了CheckComputer和CheckPlayer這兩個(gè)函數(shù)的定義,現(xiàn)在我們只需要把這兩個(gè)函數(shù)實(shí)現(xiàn)放入到game.c中并且在在電腦下棋(ComputerMove)中調(diào)用這兩個(gè)函數(shù)即可。
//電腦下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("電腦下棋\n");
//定義兩個(gè)標(biāo)識(shí)符變量來(lái)接收兩個(gè)判斷函數(shù)的返回值
int flag1 = 0;
int flag2 = 0;
flag1 = CheckComputer(board, row, col);
//如果flag1 == 0 時(shí)才進(jìn)行flag2 的判斷,避免當(dāng)二者都為1時(shí)下兩步棋(易錯(cuò))
if (flag1 == 0)
{
flag2 = CheckPlayer(board, row, col);
}
if (flag1 == 0 && flag2 == 0) //當(dāng)CheckComputer和CheckPlayer都沒(méi)落棋時(shí),就隨機(jī)下
{
while (1)
{
//在主函數(shù)生成種子srand
//隨機(jī)生成范圍內(nèi)的坐標(biāo)
int x = rand() % row;
int y = rand() % col;
//判斷坐標(biāo)是否被占用
if (board[x][y] == ' ')
{
board[x][y] = '#'; //假設(shè)電腦為#號(hào)
break;
}
}
}
}注意:這里的AI算法只適用于三子棋,如果要使用的話需要把頭文件中的ROW和COL改為3,同時(shí)不要忘記在頭文件中對(duì)兩個(gè)判斷函數(shù)進(jìn)行聲明。
到此這篇關(guān)于C語(yǔ)言實(shí)現(xiàn)五子棋功能全解析的文章就介紹到這了,更多相關(guān)C語(yǔ)言五子棋內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C/C++可變參數(shù)函數(shù)的實(shí)現(xiàn)
這篇文章主要介紹了C/C++可變參數(shù)函數(shù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
VC使用編譯時(shí)間作為版本號(hào)標(biāo)識(shí)的方法
這篇文章主要介紹了VC使用編譯時(shí)間作為版本號(hào)標(biāo)識(shí)的方法,需要的朋友可以參考下2017-03-03
舉例講解C語(yǔ)言的fork()函數(shù)創(chuàng)建子進(jìn)程的用法
fork函數(shù)是Linux下一個(gè)近乎專有的C語(yǔ)言函數(shù),因?yàn)槭褂脮r(shí)需要調(diào)用unistd.h這個(gè)頭文件,這里我們就在Linux環(huán)境下舉例講解C語(yǔ)言的fork()函數(shù)創(chuàng)建子進(jìn)程的用法,需要的朋友可以參考下2016-06-06
C++基于人工智能搜索策略解決農(nóng)夫過(guò)河問(wèn)題示例
這篇文章主要介紹了C++基于人工智能搜索策略解決農(nóng)夫過(guò)河問(wèn)題,簡(jiǎn)單描述了農(nóng)夫過(guò)河問(wèn)題的概念、實(shí)現(xiàn)原理并結(jié)合具體實(shí)例形式給出了C++使用人工智能搜索策略解決農(nóng)夫過(guò)河問(wèn)題的相關(guān)操作技巧,需要的朋友可以參考下2017-12-12
C語(yǔ)言實(shí)現(xiàn)括號(hào)匹配的方法
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)括號(hào)匹配的方法,文中代碼簡(jiǎn)單易懂,方便大家更好的學(xué)習(xí),感興趣的朋友可以參考下2020-06-06
基于C++實(shí)現(xiàn)俄羅斯方塊游戲的示例代碼
俄羅斯方塊(Tetris)是一款風(fēng)靡全球的經(jīng)典益智游戲,自1984年首次發(fā)布以來(lái),便吸引了無(wú)數(shù)玩家,在這篇博文中,我們將深入探討如何用 C++ 編寫(xiě)一個(gè)簡(jiǎn)單的俄羅斯方塊游戲,我們將從游戲的基本概念和設(shè)計(jì)入手,逐步實(shí)現(xiàn)游戲的各個(gè)功能模塊,感興趣小伙伴快來(lái)看看吧2024-11-11
C語(yǔ)言實(shí)現(xiàn)家庭理財(cái)系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)家庭理財(cái)系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11

