前言
博客主頁:干脆面la的主頁
完成二維陣列的學習后,我們就可以實作一個簡易版三子棋游戲,實作這個三子棋游戲對我們來說是一個綜合的練習,我們會原原本本用到前面的知識:分支與回圈、陣列、函式的呼叫等等,這是為了檢測我們再先前學習的知識是否能夠學以致用,
- 如果認為博主的文章對你有所幫助
- 歡迎三連加關注
- 你們的支持是我最大的動力!
目錄
前言
1 下面是這個程式運行時用戶將看到的
2 檔案的規劃
2.1 game.h
2.2 test.c
2.3 game.c
3 游戲實作思路
3.1 選單界面
3.2 游戲的實作邏輯
3.2.1 棋盤的列印
3.2.2 下棋邏輯(回圈下面步驟)
3.2.3 判斷輸贏
4 完整代碼
4.1 test.c
4.2 game.h
4.3 game.c
5 實作效果
5.1 玩家贏
5.2 電腦贏?
5.3 平局
1 下面是這個程式運行時用戶將看到的
- 選單界面
- 用戶選擇1開始游戲or選擇0結束游戲
- 輸入坐標下棋(玩家為*,電腦為#)
- 誰先連續三子就顯示誰贏
2 檔案的規劃


2.1 game.h
將整個專案所需的頭檔案以及函式宣告都寫在此檔案中,而game.c和test.c檔案只需要在頭部宣告一下:#include "game.h" 就可以省略函式呼叫的宣告和包含頭檔案的步驟,
2.2 test.c
該專案寫的是游戲的測驗邏輯,將游戲的大框架寫在此檔案中,而游戲的具體實作內容都在此檔案中被呼叫,如下圖:
#include "game.h" void menu() { printf("********************************\n"); printf("****** 1.play ******\n"); printf("****** 0.exit ******\n"); printf("********************************\n"); } void game() { char board[ROW][COL] = { 0 }; //資料存盤:玩家下棋是'*',電腦下棋是'#'. InitBoard(board, ROW, COL);//初始化棋盤 //列印棋盤 DisplayBoard(board, ROW, COL); //下棋 char ret = 0; while (1) { //玩家下 player_move(board, ROW, COL); DisplayBoard(board, ROW, COL); ret = is_win(board, ROW, COL); //電腦下 computer_move(board, ROW, COL); DisplayBoard(board, ROW, COL); ret = is_win(board, ROW, COL); if (ret != 'C') { break; } } if (ret == '*') { printf("玩家贏\n"); } else if (ret == '#') { printf("電腦贏\n"); } else { printf("平局\n"); } } void test() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戲\n"); break; default: printf("輸入錯誤\n"); break; } } while (input); printf("退出游戲"); } int main() { test(); return 0; }
2.3 game.c
該檔案寫的就是游戲的具體實作邏輯,也就是test.c檔案里所呼叫的函式的具體實作
3 游戲實作思路
3.1 選單界面
- 選單界面的列印屬于游戲的測驗邏輯,因此我們選擇寫在test.c檔案中,
- 選單界面的列印并不困難,與猜數字游戲的選單界面(點這里)的實作邏輯是相同的,有興趣可以去看看,在這里就不過多解釋了,
- 在這里我們可以先不著急完成游戲的實作邏輯,先測驗邏輯是否能跑得通,這里先用printf("三子棋\n");來代替游戲的實作邏輯,
game.h需要先參考#include <stdio.h>
test.c包含game.h檔案
#include "game.h" void menu() { printf("************************\n"); printf("****** 1.play ******\n"); printf("****** 0.exit ******\n"); printf("************************\n"); } void test() { int input = 0; do { menu();//呼叫選單 printf("請選擇:>");//提示陳述句 scanf("%d", &input);//選擇開始或結束 switch (input) { case 1: printf("三子棋\n"); break; case 0: printf("退出游戲\n"); break; default: printf("選擇錯誤\n"); break; } } while (input);//1代表回圈繼續,0代表游戲結束 } int main() { test();//主函式通過呼叫函式使其變得簡潔 return 0; }測驗結果:
3.2 游戲的實作邏輯
這時候我們游戲的測驗邏輯就已經基本出來了,唯一不對的就是游戲的實作邏輯,這時我們應該把printf("三子棋\n");修改為game();通過分裝game函式來實作三子棋的實作邏輯,
void menu()
{
printf("************************\n");
printf("****** 1.play ******\n");
printf("****** 0.exit ******\n");
printf("************************\n");
}
void game()
{
}
void test()
{
int input = 0;
do
{
menu();//呼叫選單
printf("請選擇:>");//提示陳述句
scanf("%d", &input);//選擇開始或結束
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戲\n");
break;
default:
printf("選擇錯誤\n");
break;
}
} while (input);//1代表回圈繼續,0代表游戲結束
}
int main()
{
test();
return 0;
}
這時候我們需要對游戲有所思考:應該實作什么?怎么實作游戲?
- 首先得有個三行三列的棋盤(字符的二維陣列)
- 其次是玩家輸入一個坐標(如輸入1 1):就在第1行第1列下一個棋子(*),隨即電腦又隨機在棋盤上下一個棋子(#),
- 玩家和電腦走的棋要被記錄下來
3.2.1 棋盤的列印
3x3的二位陣列的內容是玩家下的棋*和電腦下的棋#,但為了棋盤更美觀,還添加了其他東西來修飾,如下圖:
觀察第一個棋盤,我們可以分析到需要創建一個3x3的字符二維陣列,我們發現初始的棋盤對應的二維陣列應該全初始化為空格,然而二維陣列的左右兩邊都有修飾一個空格來保持棋盤的美觀,而分割線則是以---(虛線)和|(實線)來分割棋盤的,
初始化棋盤本次游戲我們希望設定3x3的棋盤,而棋盤的大小,未來可能會頻繁大量的改變,因此我們選擇在game.h里面定義一個ROW(行)為3,定義一個COL(列)為3,并且本次專案中需要用到行數和列數的地方我們都用ROW和COL來代替,如下圖:
game.h
test.c
這樣處理,在未來如果我們希望把棋盤的大小改為10x10,那么只需要在game.h檔案中修改ROW和COL定義的值就可以實作,
game.c
#include "game.h" void InitBoard(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++) { board[i][j] = ' ';//二維陣列全初始化為空格 } } }
- 列印棋盤
game.c
void DisplayBoard(char board[ROW][COL], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { //資料 printf(" %c | %c | %c \n", board[i][0],board[i][1],board[i][2]);//列印[空格]內容[空格] //分割行 if(i < row - 1) printf("---|---|---\n"); } }這時我們再來運行一下程式:
但是這里我們又遇到一個問題,如果我們此時想列印10x10的棋盤,修改ROW和COL的值為10又會發生什么呢?
出現這種原因是因為我們把代碼寫死了,因此我們需要改進一下代碼:
void DisplayBoard(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++) { 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"); } } }這時候在運行一下就發現成功了:
3.2.2 下棋邏輯(回圈下面步驟)
玩家下棋:輸入坐標,將二位陣列對應的內容改變為*,再列印棋盤,以便于玩家知道每一步下在哪里
電腦下棋:為電腦設計演算法,將二維陣列對應的內容改變為#,再列印棋盤,以便于玩家知道電腦下在哪里
不管是玩家下棋還是電腦下棋都是對二位陣列的內容進行操作,因此傳輸的引數還是board,ROW,COL
- 玩家下棋
game.h
game.c
設計玩家下棋:先把電腦下棋的部分注釋掉,while(條件)條件暫時先寫為1,先實作玩家下棋的邏輯之后再來修改
1、首先需要先判斷玩家輸入的坐標是否符合游戲規則:
(1)輸入范圍是否在1~3之間
(2)選擇下棋的坐標是否已經有棋子
2、下棋的實作:將原本為<空格>的資料改為<*>
//玩家下棋 void player_move(char board[ROW][COL], int row, int col) { printf("玩家下棋:>\n"); //輸入坐標 int x = 0; int y = 0; while(1)//合法就進入判斷陳述句,不合法就重新輸入 { scanf("%d %d", &x, &y); //判斷玩家輸入是否合法 if (x >= 1 && x <= row && y >= 1 && y <= col) { } else { printf("坐標非法\n"); } } }
由于玩家眼中的行和列和實際上的是不相等的,因此在使用用戶輸入的坐標時必須各減去1,代碼如下圖:
//玩家下棋 void player_move(char board[ROW][COL], int row, int col) { printf("玩家下棋:>\n"); //輸入坐標 int x = 0; int 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;//用戶輸入成功的時候必須break,不然會進入死回圈 } else { printf("該坐標已被占用,請重新輸入!"); } } else { printf("坐標非法,請重新輸入\n"); } } }這樣一來玩家下棋的邏輯就完成啦
- 電腦下棋
對于電腦,這里我們并不涉及復雜的演算法,而是以隨機下棋替代,同樣的道理,我們需要通過傳參board,ROW,COL來修改二位陣列的資料,
隨機下棋的實作:通過設定亂數,用亂數%(取余)行數和列數,在這里行數和列數都是3,因此亂數的范圍是0~2,滿足二維陣列的要求
ps:亂數的使用的相關函式在猜數字游戲(點這里)里有詳細講到,有興趣可以了解
game.h
?
game.c
//電腦下棋 void computer_move(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; printf("電腦下棋\n"); while (1) { x = rand() % row;//0~2 y = rand() % col;//0~2 if(board[x][y] == ' ')//判斷下的地方是否為空 { board[x][y] = '#'; break; } } }test.c
?
這時我們玩家下棋和電腦下棋的邏輯都已經實作了,但是代碼并沒有完成,我們還缺少一個判斷輸贏的代碼,所以這個地方我們要注意:每當玩家下一次棋和電腦下一次棋之后都要判斷輸贏,不然就會進入死回圈...
?
3.2.3 判斷輸贏
每當玩家或者電腦下一次棋之后,會有下面這四種狀態,因此我們需要回傳四個值,來判斷屬于下面哪一種狀態
- 玩家贏 —— 回傳'*'
- 電腦贏 —— 回傳'#'
- 平局 —— 回傳'Q'
- 繼續 —— 回傳'C'
由于每下一次棋就得判斷輸贏,因此這樣寫入判斷輸贏的代碼:
- 由于除了is_win函式回傳<C>(繼續)的情況外,游戲都可以結束跳出回圈;
- 如果回傳<*>,列印玩家贏;
- 如果回傳<#>,列印電腦贏;
- 如果回傳<Q>,列印平局
game.h
?
test.c
void game() { //資料存盤到一個字符的二維陣列中,玩家是'*',電腦是'#' char board[ROW][COL] = { 0 };//陣列內容應該是全空格 InitBoard(board,ROW,COL);//初始化棋盤 //列印棋盤 DisplayBoard(board, ROW, COL); //下棋 char ret = 0; while (1) { //玩家下棋 player_move(board, ROW, COL); DisplayBoard(board, ROW, COL); //判斷輸贏 ret = is_win(board, ROW, COL); if (ret != 'C') break; //電腦下棋 computer_move(board, ROW, COL); DisplayBoard(board, ROW, COL); //判斷輸贏 char ret = is_win(board, ROW, COL); if (ret != 'C') break; } if (ret == '*') printf("玩家贏\n"); else if (ret == '#') printf("電腦贏\n"); else printf("平局\n"); }接下來就是對判斷輸贏函式代碼的具體實作了:
- 三行:三行中有一行三個元素相同,那么就回傳那一行其中一個元素,此代碼回傳中間的元素board[i][1],也就是說假設一行中三個元素都為'#',那么就回傳'#'代表電腦贏;一行中三個元素都為'*',那么便回傳'*',代表玩家贏
- 三列:三列中有一列三個元素相同,回傳值同上
- 對角線:兩條對角線有一條的三個元素相同,此代碼的回傳值都選擇兩條對角線的中間元素board[1][1]
game.c
char is_win(char board[ROW][COL], int row, int col) { int i; //行 for (i = 0; i < row; i++) { if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ') return board[i][1]; } //列 for (i = 0; i < col; i++) { if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ') return board[1][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]; } //平局 }行、列、對角線的判斷都已經寫完了,但是每下一棋,如果還沒有分出輸贏那么又該判斷剩下兩種情況:(1)平局 (2)繼續
那么是否平局該如何判斷呢?
其實很簡單:就是判斷這個陣列中有沒有<空格>
我們可以先寫一個判斷陳述句:
- 如果函式回傳1,那么就回傳Q(平局)
- 如果函式回傳0,那么就回傳C(繼續)
//判斷是否平局 { if (1 == is_full(board, row, col)) return 'Q'; } return 'C';再寫一個函式is_full()
- 如果還有空格,那么就回傳0
- 如果沒有空格,那么就回傳1
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; }
4 完整代碼
4.1 test.c
#include "game.h" void menu() { printf("************************\n"); printf("****** 1.play ******\n"); printf("****** 0.exit ******\n"); printf("************************\n"); } void game() { //資料存盤到一個字符的二維陣列中,玩家是'*',電腦是'#' char board[ROW][COL] = { 0 };//陣列內容應該是全空格 InitBoard(board,ROW,COL);//初始化棋盤 //列印棋盤 DisplayBoard(board, ROW, COL); //下棋 char ret = 0; while (1) { //玩家下棋 player_move(board, ROW, COL); DisplayBoard(board, ROW, COL); //判斷輸贏 ret = is_win(board, ROW, COL); if (ret != 'C') break; //電腦下棋 computer_move(board, ROW, COL); DisplayBoard(board, ROW, COL); //判斷輸贏 ret = is_win(board, ROW, COL); if (ret != 'C') { break; } } if (ret == '*') { printf("玩家贏\n"); } else if (ret == '#') { printf("電腦贏\n"); } else { printf("平局\n"); } } void test() { 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);//1代表回圈繼續,0代表游戲結束 } int main() { test(); return 0; }
4.2 game.h
#define ROW 3//定義行數 #define COL 3//定義列數 //包含頭檔案 #include<stdio.h> #include<stdlib.h> #include<time.h> //函式宣告 void InitBoard(char board[ROW][COL], int row, int col);//初始化棋盤 void player_move(char board[ROW][COL], int row, int col);//玩家下棋 void computer_move(char board[ROW][COL], int row, int col);//電腦下棋 char is_win(char board[ROW][COL], int row, int col);//判斷輸贏
4.3 game.c
#include "game.h" //初始化棋盤 void InitBoard(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++) { board[i][j] = ' '; } } } //列印棋盤 void DisplayBoard(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++) { 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"); } } } //玩家下棋 void player_move(char board[ROW][COL], int row, int col) { printf("玩家下棋:>"); //輸入坐標 int x = 0; int 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("該坐標已被占用,請重新輸入!"); } } else { printf("坐標非法,請重新輸入\n"); } } } //電腦下棋 void computer_move(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; printf("電腦下棋\n"); while (1) { x = rand() % row;//0~2 y = rand() % col;//0~2 if(board[x][y] == ' ')//判斷下的地方是否被占用 { board[x][y] = '#'; break; } } } //判斷輸贏 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][1] != ' ') return board[i][1]; } //列 for (i = 0; i < col; i++) { if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ') return board[1][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 (1 == is_full(board, row, col)) { return 'Q'; } return 'C'; }
5 實作效果
5.1 玩家贏
5.2 電腦贏
5.3 平局
(完)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/414006.html
標籤:其他
上一篇:mini《猜字》游戲,誰玩誰迷糊
下一篇:簡單的猜數字游戲



test.c包含game.h檔案












?
?
?
?

