C語言也學(xué)習(xí)一段時(shí)間了,為了鞏固我們學(xué)習(xí)的知識,今天我們寫一個三子棋的小游戲。這對初學(xué)者是個大工程,跟著我一起開始吧。
一.邏輯思維梳理
我們今天寫的是三子棋小游戲,說到游戲肯定就有很多模塊組成,所以為了提升游戲的可維護(hù)性和移植性
,我們應(yīng)該采用模塊化編寫程序,將不同的版塊分裝在不同文件下。
上圖為不同文件的任務(wù),接下來我們就開始逐步講解我們?nèi)制宓膶?shí)現(xiàn)。
二.代碼板塊的實(shí)現(xiàn)
1.游戲主邏輯實(shí)現(xiàn)
在c語言程序中 最重要的就是main函數(shù),所以我們現(xiàn)在main函數(shù)中引用test函數(shù),實(shí)現(xiàn)代碼分裝 使得我們的編寫更有層次。
接下來我們開始實(shí)現(xiàn)test函數(shù)。 在游戲的開始界面我們應(yīng)該需要一個入口 可以選擇玩游戲 或者退出游戲。do while循環(huán)可以很好的實(shí)現(xiàn)我們的需求。
void test()
{
int i = 0;
do
{
menu();
printf("請選擇>");
scanf("%d", &i);
switch (i)
{
case 1:
game();
break;
case 0:
printf("退出游戲\n");
break;
default:
printf("選擇錯誤,請重新選擇:>\n");
break;
}
} while (i);
}
switch在游戲界面選擇中非常實(shí)用,在上圖的代碼邏輯實(shí)現(xiàn)中我們又引用了菜單meua函數(shù)和游戲主體函數(shù)game。
meua菜單需要實(shí)現(xiàn)一個界面 提供玩游戲和 退出游戲的人兩個選擇 代碼和效果圖如下
void menu()
{
printf("*****************************\n");
printf("******** 1.play *********\n");
printf("******** 0.exit *********\n");
printf("*****************************\n");
}
接下來我們著重講解game函數(shù)。
2.棋盤的打印的數(shù)組的創(chuàng)建
在菜單選擇開始游戲后我們進(jìn)入到游戲后,我們應(yīng)該可以看到三子棋的棋盤。在打印棋盤前,我們應(yīng)該創(chuàng)建一個二維數(shù)組對應(yīng)三子棋盤上的9個位置 接受棋子。
#define ROW 3
#define COL 3
首先我們.h文件中定義好數(shù)組的行和列,而不是直接對數(shù)組規(guī)定行和列,這樣有利于后續(xù)我們對三子棋升級優(yōu)化
char board[ROW][COL];//創(chuàng)建3*3數(shù)組存放字符
接著我們創(chuàng)建一個二維數(shù)組,用來接受存放旗子。
這是我們想要實(shí)現(xiàn)的棋盤效果,但是我們并沒有對數(shù)組board初始化,所以數(shù)組中默認(rèn)儲存的是0,這就達(dá)不到上圖的效果。
所以在打印棋盤之前,我們應(yīng)當(dāng)先對棋盤初始化,將數(shù)組的元素都定義為空格。
我們在test中引用函數(shù) Int_board(board, ROW, COL);
在game.h聲明函數(shù)后 在game.c文件中編寫這個函數(shù)。
void Int_board(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++)
{
board[i][j] = ' ';
}
}
}
這里只需要用一個簡單遍歷 就可以實(shí)現(xiàn)。
接下來我們就要打印棋盤。棋盤的每兩行可以看成一個部分,一共有3個部分
具體的解釋,我們依據(jù)代碼窺探``
void Print_board(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)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
上面我們吧棋盤分為了三個部分,所以在整個打印代碼段 都是被一個for套嵌的。之后只要編寫一部分的打印就可以了。
2.玩家下棋的實(shí)現(xiàn)
在編寫玩家下棋的代碼部分時(shí) 我們需要注意一下幾點(diǎn):
1.數(shù)組接受玩家下的棋子
2.判斷玩家坐標(biāo)輸入是否合法
3.玩家輸入坐標(biāo)后 將棋盤打印出來
void player_move(char board[ROW][COL], int row, int col)
{
printf("玩家下棋\n");
while (1)
{
printf("玩家輸入坐標(biāo):>");
int a, b;
scanf("%d %d", &a, &b);
if (a >= 1 && a <= row && b >= 1 && b <= col)
{
if (board[a - 1][b - 1] == ' ')
{
board[a - 1][b - 1] = '*';
break;
}
else
{
printf("該坐標(biāo)已被占用\n");
}
}
else
{
printf("坐標(biāo)非法\n");
}
}
}
當(dāng)玩家scanf輸入坐標(biāo)后,應(yīng)該要判斷坐標(biāo)輸入是否合法,如果玩家輸入的坐標(biāo)已經(jīng)被占用或者超出了二維數(shù)組的范圍,就應(yīng)該循環(huán)再次進(jìn)入玩家輸入坐標(biāo)的地方,知道坐標(biāo)輸入正確break跳出循環(huán)。
當(dāng)玩家正確輸入坐標(biāo)后,我們要講新的棋盤打印出來。所以我們需要再次在test.c文件中引用 Print_board(board, ROW, COL);
函數(shù)。
3.電腦隨機(jī)下棋的實(shí)現(xiàn)
void comper_move(char board[ROW][COL], int row, int col)
{
srand((unsigned int)time(NULL));
while (1)
{
printf("電腦下棋\n");
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
這里需要注意的是電腦隨機(jī)坐標(biāo)應(yīng)該如何生成,還是運(yùn)用我們熟知的rand函數(shù),srand函數(shù)他們需要引用頭文件**#include <stdio.h>**,rand函數(shù)借助srand種子由time強(qiáng)制轉(zhuǎn)化為unsigned int類型時(shí)間戳生成的隨機(jī)數(shù)應(yīng)該不能超過二維數(shù)組行列的范圍,所以我們寫了
int x = rand() % row;
int y = rand() % col;
這里我們有個結(jié)論可以 理解記憶一下
接著我們再將電腦隨機(jī)下的棋 再打印出來。
4.判斷輸贏
游戲的結(jié)果判斷 我們現(xiàn)在test.c文件中編寫出邏輯 再去編寫具體函數(shù)
在這里我們規(guī)定 輸贏平局的返回值
while (1)
{
player_move(board, ROW, COL);
print_board(board, ROW, COL);
//判斷輸贏
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
computer_move(board, ROW, COL);
print_board(board, ROW, COL);
//判斷輸贏
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == '#')
printf("電腦贏了\n");
else if (ret == '*')
printf("玩家贏了\n");
else if (ret == 'Q')
printf("平局\n");
用ret接受 is_win函數(shù)的返回值
接下來我們主要編寫is_win函數(shù) 以及對他的優(yōu)化升級
在三子棋中有這幾種贏法
static int is_full(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 is_win(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][0] != ' ')
{
return board[i][0];
}
}
//判斷三列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
{
return board[0][i];
}
}
//對角線
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 (is_full(board, row, col) == 1)
{
return 'Q';
}
//繼續(xù)
//沒有玩家或者電腦贏,也沒有平局,游戲繼續(xù)
return 'C';
}
我們將所有情況列舉出來 再寫出平局的情況將 字符返回到ret
判斷游戲是否需要繼續(xù)。
三.分析優(yōu)化和升級
在上面判斷三子棋是否輸贏時(shí),我們是將所有的情況都一一列舉出來。這樣就限制的游戲的升級 所以我們能不能優(yōu)化is_win函數(shù)呢?
char is_win(char board[ROW][COL], int row, int col)
{
int flag1 = 0;///玩家棋數(shù)
int flag2 = 0;//電腦棋數(shù)
int i, j;
for (i = 0; i < row; i++)///行 計(jì)算
{
flag1 = flag2 = 0;
for (j = 0; j < col; j++)
{
if (board[i][j] == '*')
{
flag1++;
}
if (board[i][j] == '#')
{
flag2++;
}
}
if (flag1 == col)///修改怎么應(yīng)該實(shí)現(xiàn)N子棋
{
return '*';
}
if (flag2 == col)///修改怎么應(yīng)該實(shí)現(xiàn)N子棋
{
return '#';
}
}
for (j = 0; j < col; j++)///列 計(jì)算
{
flag1 = flag2 = 0;
for (i = 0; i < row; i++)
{
if (board[i][j] == '*')
{
flag1++;
}
if (board[i][j] == '#')
{
flag2++;
}
}
if (flag1 == col)///修改怎么應(yīng)該實(shí)現(xiàn)N子棋
{
return '*';
}
if (flag2 == col)///修改怎么應(yīng)該實(shí)現(xiàn)N子棋
{
return '#';
}
}
flag1 = flag2 = 0;
for (i = 0, j = 0; i < row, j < col; i++, j++)//正對角線
{
if (board[i][j] == '*')
{
flag1++;
}
if (board[i][j] == '#')
{
flag2++;
}
}
if (flag1 == col)///修改怎么應(yīng)該實(shí)現(xiàn)N子棋
{
return '*';
}
if (flag2 == col)///修改怎么應(yīng)該實(shí)現(xiàn)N子棋
{
return '#';
}
flag1 = flag2 = 0;
for (i = 0, j = col-1; i<row, j >=0; i++, j--)//逆對角線
{
if (board[i][j] == '*')
{
flag1++;
}
if (board[i][j] == '#')
{
flag2++;
}
}
if (flag1 == col)///修改怎么應(yīng)該實(shí)現(xiàn)N子棋
{
return '*';
}
if (flag2 == col)///修改怎么應(yīng)該實(shí)現(xiàn)N子棋
{
return '#';
}
if (is_full(board, row, col) == 1)
{
return 'Q';
}
//繼續(xù)
//沒有玩家或者電腦贏,也沒有平局,游戲繼續(xù)
return 'C';
}
上圖我用計(jì)數(shù)器寫了 判斷棋盤輸贏的代碼,雖看起來沒有原來簡單,但是如果升級到N子棋時(shí),這種寫法就比較方便
在我玩了七七九十九局后 感覺這個電腦實(shí)在太蠢了,所以我們還有什么方法能將電腦下棋更智能些呢? 我們在下篇講解。文章來源:http://www.zghlxwxcb.cn/news/detail-808502.html
關(guān)于三子棋游戲的分享就到這里。
感謝相遇,共同進(jìn)步。文章來源地址http://www.zghlxwxcb.cn/news/detail-808502.html
到了這里,關(guān)于【入門級小游戲】C語言數(shù)組函數(shù):解析三(N)子棋的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!