文章目錄
- 1. 三子棋實作的基本思路
- 2. 主函式
- 3. menu函式和game函式
- 4. game.c中的具體實作函式
- 4.1 初始化函式 InitBoard()
- 4.2 列印棋盤函式 DispayBoard()
- 4.3 玩家操作函式 PlayerMove()
- 4.4 電腦下棋函式 ComputerMove()
- 4.5 判斷勝負函式 IsWin()
- 4.6 判斷棋盤是否已滿函式 IsFull()
- 5. 頭檔案
- 6. 實作效果
1. 三子棋實作的基本思路
三子棋是我們在兒時玩過的最經典的游戲之一,游戲的規則很簡單,在一個 3×3 的棋盤中,兩人交替下棋,當某一方的三個棋子連成三點一線,那么即為勝利,
那么問題來了,我們怎樣巧妙的用編程的思維去完成這樣一個小專案呢?首先,我們清楚,如果我們在一個源檔案中將所有相關的代碼全部擠進去,那么寫到一半的時候你可能會窒息,“我寫到哪了?”“我在寫什么?”,一連串發自靈魂的自問可能會讓你明白這世界并不美好,既然這樣我們何不把寫的代碼按功能分類,分別放到不同的源檔案中去呢?因此我們就可以在主函式 main.c 、功能函式 game.c 、以及頭檔案 game.h 中進行編譯啦,
有了這樣的一個大前提,我們知道游戲一般都會有選單,讓你選擇玩游戲還是退出,當你進入了游戲,棋盤會出現在你的面前,等待你下的第一步棋,你下完了,電腦會自動下完第二步,這樣一直回圈,一直等到決出勝負,
這個時候你不會天真的以為事情就這么的順風順水吧,讓我們來看一看我們到底要實作幾個函式:初始化棋盤、列印棋盤、玩家下棋、電腦下棋、判斷勝負,況且我們如何存盤這個棋盤的棋子的資料呢?顯然我們可以用一個二維陣列來存盤每一個格子的資訊,接下來讓我們一步一步地來實作,
2. 主函式
int main()
{
int input = 0;//初始化輸入變數
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;
}
有了以上的主函式作為參考,接下來的實作就是要我們一步一個腳印寫出來了,首先我們觀察上述代碼是否有未知的需求,顯然 menu() 函式和 game() 函式我們都是未知的,接下來我們來實作,
3. menu函式和game函式
就像在第一點說的那樣,游戲的實作主要在這一部分,我們需要將各種功能都封裝成函式,我們設棋盤有 ROW 行,COL 列,而不是死板的將行和列都設定為 3 ,這樣我們把ROW和COL定義在頭檔案中 ,就優化了代碼的可更改性,另外,在所有要封裝的函式中,判斷勝負的函式是需要回傳值的,根據回傳值的不同,通過 if 陳述句的判斷從而列印出不同的結果,我們假設回傳字符 * 代表玩家勝利,回傳 # 代表電腦勝利,回傳 C 代表游戲繼續(沒有分出勝負),回傳 Q 代表平局,
void menu()//列印游戲選單界面
{
printf("*********************\n");
printf("**** 1.PLAY *****\n");
printf("**** 0.EXIT *****\n");
printf("*********************\n");
}
void game()
{
char ret;//初始化回傳變數
char board[ROW][COL];//定義棋盤陣列
InitBoard(board, ROW, COL);//將棋盤陣列內的元素都初始化為空格
DisplayBoard(board, ROW, COL);//列印出棋盤,讓棋盤可視化
while (1)//進入游戲回圈
{
PlayerMove(board, ROW, COL);//玩家下棋
DisplayBoard(board, ROW, COL);//列印出棋盤,讓棋盤可視化
ret = IsWin(board, ROW, COL);//判斷是否分出了勝負
if (ret != 'C')//只有回傳C時游戲才沒有分出勝負
{
break;
}
ComputerMove(board, ROW, COL);//電腦下棋
DisplayBoard(board, ROW, COL);//列印出棋盤,讓棋盤可視化
ret = IsWin(board, ROW, COL);//判斷是否分出了勝負
if (ret != 'C')//只有回傳C時游戲才沒有分出勝負
{
break;
}
}
//退出回圈則是已經分出勝負的情況
if (ret == '*')
{
printf("玩家贏!\n");
}
else if (ret == '#')
{
printf("電腦贏!\n");
}
else
{
printf("平局!\n");
}
4. game.c中的具體實作函式
4.1 初始化函式 InitBoard()
在游戲開始之前,我們需要將表示棋盤的陣列里面的元素全部初始化,大家想一想,在什么都沒有操作的棋盤上是不是什么都沒有,但是我們不能把陣列初始化為沒有呀,什么都沒有只是視覺上的表現,我們不妨將陣列每個元素初始化為空格,
//char board[ROW][COL] 用于接收棋盤資料的陣列
//row 用于接收棋盤的行數
//col 用于接收棋盤的列數
void InitBoard(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] = ' ';//將每一個陣列元素初始化為空格
}
}
}
4.2 列印棋盤函式 DispayBoard()
雖然我們已經將棋盤的每個元素初始化為了空格,但只是在計算機記憶體完成的操作,我們的肉眼是無法很好的觀察到的,既然要下棋,我們總要將整個棋盤可視化吧,我們計劃將棋盤設計成以下格式:

我們觀察到棋盤一共有五行,第一三五行放置了陣列元素與分割線,二四行放置了分隔線,這是一個 3×3 的棋盤,我們試想,如果我們僅僅將棋盤列印出只符合 3×3 棋盤的分隔線,你那未必也太挫了吧,我們想要修改頭檔案中的資料,而棋盤的大小也跟著改變,這樣也就方便了以后程式的優化升級,
我們觀察這個棋盤的規律:第一行是由九個空格兩條豎線組成的,我們不妨將三個空格和一條豎線看作一組,回圈列印,當列印到第三組的時候利用條件陳述句不列印不就可以了嗎?再繼續往下觀察,可見第一行和第二行的布局與第三行和第四行的布局完全一樣,我們也不妨將這兩行也看做一組回圈列印,至于最后一行,利用相同的方法,即列印到最后一次時不列印下面橫豎線分隔線的那一部分,
void DisplayBoard(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]);// "空格%c空格"
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");
}
}
}
列印出的結果如圖所示

4.3 玩家操作函式 PlayerMove()
玩家的操作無非就是將棋子放入想要放入的位置中,我們設玩家棋子為字符 * ,我們的目的就是將這顆星號放入陣列中,這就需要用到二維陣列的坐標作為傳參,讓玩家輸入坐標,從而將對應的二維陣列元素置為星號,此時我們就得考慮了,輸入的坐標大于了二維陣列本身的范圍怎么辦?下過棋子的地方還能不能繼續下呢?對應的輸入坐標和二維陣列的坐標時相同的嗎?
顯然我們需要給定一個輸入坐標的范圍;下過棋子的地方再下棋子就會報錯,利用回圈重新輸入;輸入的坐標減一才是對應的二維陣列的坐標,
void PlayerMove(char board[ROW][COL], int row, int col)
{
printf("玩家走:");
while (1)
{
printf("請輸入坐標:\n");
int x, y;
scanf("%d%d", &x, &y);
//給定輸入坐標的合法范圍
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//該坐標下元素不是空格,說明已經被占用
if (board[x - 1][y - 1] != ' ')
{
printf("該格子被占用,請重新輸入:\n");
}
else
{
//沒有被占用,將其置為 *
board[x - 1][y - 1] = '*';
break;
}
}
else
//其他不合法的輸入
{
printf("輸入錯誤,請重新輸入\n");
}
}
}
4.4 電腦下棋函式 ComputerMove()
不會吧不會吧,不會有人真的以為電腦會動腦子吧,這里的電腦下棋只不過是利用計算機產生兩個亂數作為坐標,然后將電腦的棋子放置到棋盤中,除了要生成隨機的坐標,其他的操作都與玩家下棋相似,這時的亂數就成問題了,亂數那么大,坐標那么小怎么辦?我們將亂數模上行數或者列數,假如行列都是 3 ,我們不就得到 0,1,2 了嗎?當然了,亂數函式 rand() 是需要 srand進行配置的,將時間戳作為變數,可以得到不同的亂數, srand 的配置在主函式中,
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("電腦走:\n");
while (1)
{
int x = rand() % row;//生成隨機的行坐標
int y = rand() % col;//生成隨機的列坐標
//坐標沒有被占用,電腦下棋
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
4.5 判斷勝負函式 IsWin()
既然要勝利,就要棋子連成三點一線,無非只有四種情況:三個在同一行;三個在同一列;三個在對角線上;三個在斜對角線上,如果沒有分出勝負呢?沒有分出勝負,要么是平局,要么還要繼續下棋直到分出勝負為止,那么我們怎么才能知道是否平局,是否要繼續呢?可以知道,如果連棋盤都滿了都沒有分出勝負,你還不服想耍賴想繼續下,可惜棋盤不允許,這時就是平局了;若棋盤還沒滿,也沒有哪一方贏,游戲就得繼續,看來有這么多的情況,這個函式是需要回傳值的,我們按照第三節所說的那樣來設定回傳值,在上面我們提到要判斷是否棋盤已滿,我們不妨將判斷棋盤是否已滿也封裝成一個函式,
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][0] == board[i][2] && board[i][0] != ' ')//當行上出現三點一線
{
return board[i][0];//誰贏就回傳什么
}
}
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[0][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[0][0] != ' ')//當對角線上出現三點一線
{
return board[0][0];
}
if (board[0][2] == board[1][1] && board[2][0] == board[1][1] && board[0][2] != ' ')//當斜對角線上出現三點一線
{
return board[0][2];
}
if (IsFull(board, ROW, COL) == 1)//棋盤滿了,平局
return 'Q';
if (IsFull(board, ROW, COL) == 0)//繼續
return 'C';
}
4.6 判斷棋盤是否已滿函式 IsFull()
我們遍歷整個陣列,如果出現了空格,則棋盤沒滿,用 0 和 1 作為回傳值,0代表沒滿,1代表已滿,
static 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;
}
5. 頭檔案
我們實作完了所有的具體功能函式,需要在頭檔案中添加函式宣告,并且在 game.c 和 main.c 中引入 頭檔案 game.h
#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 DisplayBoard(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);//電腦下棋
char IsWin(char board[ROW][COL], int row, int col);//判斷輸贏
6. 實作效果
我們來看一看玩家贏得效果


試一試退出游戲
平局

電腦贏

以上就是本篇博客得全部內容,感謝老爺們得觀看,記得贊贊點一手哦~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/258101.html
標籤:其他
上一篇:Codeforces Round #700 (Div. 2)-B. The Great Hero-題解-一行實作向上取整
