在學習二維陣列后我們加以應用能寫出三子棋這樣一個小游戲,具體實作并不復雜,
基本思路:
游戲首先列印選單,由我們選擇單人游戲,雙人游戲或退出游戲,
三子棋需要一個棋盤,我們可以通過回圈列印來構造一個簡單的3x3棋盤,

我們需要一個函式在每次下棋子后都列印一次來表示目前的棋盤,于是創建一個二維陣列接收棋子后方可遍歷列印,我們用 * 和 # 代表兩種棋子,
游戲程序中玩家輸入下標下棋后電腦下,直到決出勝負或平局,

直接看代碼,
我們在寫程式的時候要將函式模塊化,不把所有函式都堆在一個源檔案里,我們新建一個主體源檔案main.c用于呼叫所有函式,新建一個game1.c檔案用于函式的實作,即撰寫函式,新建一個game.h檔案用于宣告game1.c中的函式,
游戲選單
void menu()
{
printf("################################\n");
printf("######### 1.單人游戲 ##########\n");
printf("######### 2.雙人游戲 ##########\n");
printf("######### 3.退出游戲 ##########\n");
printf("################################\n");
}
假如進入單人游戲,我們首先創建并初始化一個棋盤:
#define ROW 3
#define COL 3
char board[ROW][COL] = { 0 };
Initboard(board, ROW, COL);
我們用宏定義兩個常量,ROW表示棋盤的行數,COL表示棋盤的列數,
這樣做是方便我們一次性修改行數和列數,
初始化:
void Initboard(char board[ROW][COL], int row, int col)
{
//char i = 0; char* p = board;
//while (i < ROW * COL)
//{
// *(p + i++) = ' ';
//}
//for (int i = 0;i < row;i++)
//{
// for (int j = 0;j < col;j++)
// {
// board[i][j] = ' ';
// }
//}
memset(board, ' ', row * col);
}
我們有三種初始化的方式:
- 是定義一個指向陣列的指標,這里因為筆者對指標不夠熟悉,因此只定義一個普通的指標指向陣列,但結果是可以實作的,因為二維陣列在記憶體中的存盤是連續的,
- 用回圈遍歷整個二維陣列,將陣列的每個元素賦值為空格,這是最直接的方法,
- 用memset函式一步到位,這里不對該函式多做介紹,讀者可自行查詢,
列印棋盤:
void Displayboard(char board[ROW][COL], int row, int col)
{
for (int j = 0;j < col;j++)
{
printf("---|");
}
printf("\n");
for (int i = 0;i < row;i++)
{
for (int j = 0;j < col;j++)
{
printf(" %c ", board[i][j]);
printf("|");
}
printf("\n");
for (int j = 0;j < col;j++)
{
printf("---");
printf("|");
}
printf("\n");
}
}
棋盤如圖
我們可以直接列印棋盤樣式,采用回圈是因為如果改變行和列數后棋盤無法正常顯示,具體實作較為簡易,
玩家下棋:
void Player_move(char board[ROW][COL], int row, int col)
{
int x = 0, y = 0;
while (1)
{
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] = '*';
break;
}
else
{
printf("該位置已有棋子,重新輸入\n");
continue;
}
}
else printf("輸入錯誤,重新輸入\n");
}
}
定義X和Y變數用于接收玩家輸入的下標數,之后進行回圈,假如成功下子駁出回圈,我們需要判斷坐標是否已經有棋子,坐標是否合法,如果輸入錯誤即再進行回圈直到輸入正確,需要注意的是在賦值時注意陣列下標為 X-1,Y-1,例如玩家輸入2 2,本意是在中間的位置下棋,但是陣列讀取的時候下標要-1,即傳入陣列中的數值應為1 1.
電腦下棋:
void Computer_move(char board[ROW][COL], int row, int col)
{
int x = 0, y = 0;
while (1)
{
x = rand() % row;
y = rand() % col;
simplealgorithm(board, row, col, &x, &y);
again: if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
else
{
x = rand() % row;
y = rand() % col;
goto again;
}
}
}
我們需要調用一個隨機值來生成電腦下子的位置,于是呼叫rand函式,且要在main.c檔案里先使用srand函式,將時間戳作為亂數種子,在主體函式里添加下列這句代碼即可,
srand((unsigned int)time(NULL));
得到亂數后,將亂數%ROW,確保這個值的范圍在0~ROW之間,不會越界后賦給X,作為隨機的行坐標,同理得到隨機的列坐標,之后呼叫一個極其簡單的攔截函式,避免電腦看起來跟個人工智障似的,goto again是避免修改后的坐標因為無法通過判斷陳述句后無法再取亂數后造成死回圈,
電腦攔截:
void simplealgorithm(char board[ROW][COL], int row, int col,int *x,int *y)
{
for (int i = 0;i <= row;i++)//行
{
if ((board[i][0] == board[i][1] && board[i][0] == '*'))
{
*x = i;
*y = 2;
}
else if ((board[i][1] == board[i][2] && board[i][1] == '*'))
{
*x = i;
*y = 0;
}
}
for (int i = 0;i <= col;i++)//列
{
if ((board[0][i] == board[1][i] && board[0][i] == '*'))
{
*x = 2;
*y = i;
}
else if ((board[1][i] == board[2][i] && board[1][i] == '*'))
{
*x = 0;
*y = i;
}
}
for (int i = 0;i <= col;i++)//對角線
{
if ((board[i][i] == board[i+1][i+1] && board[i][i] == '*'))
{
if (i == 0)
{
*x = 2;
*y = 2;
}
else if (i == 1)
{
*x = 0;
*y = 0;
}
}
else if ((board[i][2] == board[i+1][i+1] && board[i][2] == '*'))
{
if (i == 0)
{
*x = 2;
*y = 0;
}
else if (i == 1)
{
*x = 0;
*y = 2;
}
}
}
}
這個函式只是簡簡單單的判斷,并沒有什么演算法,功能也不齊全,在這不多作介紹,讀者可自行研究,進入函式后進行判斷,如果坐標需要修改即可修改坐標地址對應的值,所以我們要把函式里的X和Y傳址過去,
判斷勝負或平局:
char Win(char board[ROW][COL], int row, int col)
{
for (int i = 0;i < row;i++)//判斷行
{
if (board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] != ' ')
return board[i][0];
}
for (int i = 0;i < col;i++)//判斷列
{
if (board[0][i] == board[1][i] && board[2][i] == board[0][i] && board[0][i] != ' ')
return board[0][i];
}
//判斷對角線
if (board[0][0] == board[1][1] && board[2][2] == board[0][0] && board[0][0] != ' ')
return board[0][0];
if (board[0][2] == board[1][1] && board[2][0] == board[0][2] && board[0][2] != ' ')
return board[0][2];
if (Full(board, row, col))
{
return 'F'; //Full
}
else return 'C';
}
int 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,可讀性相較int更高,
C表示continue,對局尚未結束,繼續游戲,
F表示full,棋盤已滿,平局,
*表示 玩家勝,#表示電腦勝,
定義一個字符變數ret用于接收字符后進行判斷,
我們通過回圈判斷行或列或對角線,如果出現三子相等且均不為空格即回傳這一線上的任意一個棋子,即是 *或#
主體函式
void body()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1:system("cls");game1();
break;
case 2:system("cls");
printf("先手是* 后手是#\n");
game2();
break;
case 3:printf("退出\n");input = 0;
break;
default:printf("輸入錯誤,請重新選擇\n");
break;
}
} while (input);
}
主體函式要做的是通過玩家的輸入值判斷進入哪個選項,在回圈里,input作為判斷條件,如果玩家輸入的是3,即退出游戲,我們便把input設為0,方跳出回圈,游戲終止,接下來我們看2、雙人游戲
雙人游戲
void game2()
{
char ret = 0;
char board[ROW][COL] = { 0 };
Initboard(board, ROW, COL);
while (1)
{
Displayboard(board, ROW, COL);
Player_move(board, ROW, COL);
Displayboard(board, ROW, COL);
ret = Win(board, ROW, COL);
if (ret != 'C')
break;
Player2_move(board, ROW, COL);
ret = Win(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == '#')
{
printf("玩家2贏了\n");
}
else if (ret == '*')
{
printf("玩家1贏了\n");
}
else if (ret == 'F')
{
printf("平局\n");
}
}
void Player2_move(char board[ROW][COL], int row, int col)
{
int x = 0, y = 0;
while (1)
{
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] = '#';
break;
}
else
{
printf("該位置已有棋子,重新輸入\n");
continue;
}
}
else printf("輸入錯誤,重新輸入\n");
}
}
這里很簡單,我們只需要呼叫一個game2函式,再呼叫一個玩家2下子的函式后進行游戲即可,邏輯和單人游戲非常相似,
讀者可以自行嘗試優化代碼,因為如果改變了行數和列數,即擴大棋盤,游戲的勝負判斷函式和電腦的攔截函式將不可用,不過可以擴大棋盤后雙人對戰自行肉眼判斷,甚至還能把棋盤擴的更大,來一盤快樂的五子棋~
(只是找下標可能是一個令人頭疼的問題……)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/354771.html
標籤:其他
上一篇:C語言最簡單小游戲【猜數字】
