寫在開頭:本案例代碼採用win10系統下 Visual Studio 2019 編譯器進行書寫編譯的。對於該編譯器「scanf()」編譯時沒法經過,解決方案在每一個須要該函數的文件的第一行加入以下代碼:數組
#define _CRT_SECURE_NO_WARNINGS 1
首先創建主程序文件main.c,函數文件game.c,頭文件game.h。其中函數文件用於存放對遊戲中各個部分功能實現的函數代碼;頭文件用於進行函數聲明。
ide
對於任何程序,都須要一個主體部分,在三子棋遊戲中也不例外。對於一個遊戲,基本包括遊戲界面和遊戲選項。遊戲運行,首先進行遊戲菜單打印,等待用戶輸入,根據用戶輸入內容進行下一步的操做。其中菜單部分可使用menu()函數進行實現,用戶輸入則用scanf()函數來接收。規定:菜單打印兩個選項,當用戶鍵入1時,則開始進行遊戲(遊戲部分由game()函數實現);當用戶鍵入0時,則退出程序;若用戶鍵入其餘字符,則提示用戶輸入有誤,需從新出入。所以可使用switch()函數來實現此部分功能。具體實現代碼以下:函數
int main(void) { int input; srand((unsigned int)time(NULL)); printf("****三子棋遊戲****\n"); printf("玩家執子:*;電腦執子:#\n"); do { menu(); printf("請選擇:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("遊戲結束。\n"); break; default: printf("輸入錯誤,請從新輸入!!!\n\n"); break; } } while (input); return 0; }
其中menu()函數的代碼以下:
spa
//打印菜單 void menu(void) { printf("*************************\n"); printf("**1.play*********0.exit**\n"); printf("*************************\n"); }
對於遊戲部分,首先是要生成棋盤,每次進入遊戲初始都要對棋盤內容進行初始化,而後對棋盤進行可視化。生成棋盤可使用一個2維數組進行,只需對玩家和電腦每次輸入的座標進行存放。初始化棋盤使用InitBoard()函數進行,可視化用DisplayBoard()函數進行。3d
其次是遊戲的主要部分,生成棋盤後,玩家和電腦分別落子,當有一方贏或者平局則退出遊戲。玩家落子使用PlayerMove()函數實現,電腦落子使用ComMove()函數實現,判斷輸贏則用Winner()函數實現。下邊則對上述的各個函數功能的邏輯進行分析並實現。code
棋盤部分
blog
爲了保證程序的健壯性,便可以方便改變棋盤的大小,在頭文件中定義三個常量來存放棋盤的大小和玩的大小(如能夠玩4子棋,5子棋等)。注意定義常量時最後不要加「;」,不然後邊代碼會出錯。遊戲
#define COL 5 //棋盤有5列 #define ROW 5 //棋盤有5行 #define COUNT 3 //三子棋
建立棋盤:
input
//建立一個棋盤 char board[ROW][COL] = { 0 };
對棋盤進行初始化:每開一次新的遊戲後都須要對棋盤進行初始化。對棋盤進行初始化即遍歷棋盤,把棋盤每一個位置元素都置空。所以該函數須要可以接收棋盤而且知道棋盤的大小。其代碼對應以下:編譯器
//初始化棋盤 void InitBoard(char board[ROW][COL],int row, int col) { int i; int j; 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; int j; //首先打印出橫座標 printf(" "); for (j = 0; j < col; j++) { printf("%2d ",j+1); } printf("\n\n"); //打印棋盤 for (i = 0; i < row; i++) { //棋盤的每一列開始打印縱座標 printf("%3d ",i+1); //打印棋盤每一行的第一部份內容 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"); } }
遊戲部分
生成棋盤後則玩家開始落子:
玩家落子:要求函數能夠接收玩家輸入的座標,而且每次對玩家輸入的座標進行判斷,若是玩家輸入的位置上已經有棋子,則提示玩家已經有棋子,而且從新輸入,同理若是玩家輸入的座標範圍超出了棋盤範圍,應該提示玩家輸入座標不在棋盤範圍內,從新輸入座標,此外,棋盤顯示的座標位置是從1開始,而二維數組下標是從0開始,函數內部還要對座標進行轉換。
電腦落子:電腦使用隨機數進行落子,同玩家落子同樣,須要進行一樣判斷,但不須要進行提示。此外,電腦進行落子後,須要對其座標進行記錄並輸出,提示玩家電腦落子的位置(若是棋盤過大,落子過多,不提示則不清楚電腦的落子位置),同理,函數也需對座標進行轉化。C語言中函數沒法返回兩個值,因此須要用一個長度爲2的整型數組對座標進行存放,以便後續屏幕輸出和判斷輸贏使用。
玩家落子函數代碼以下:
//玩家落子,函數形參中arr用於接收落子座標 void PlayerMove(char board[ROW][COL], int row, int col, int arr[]) { int i; int j; printf("該玩家落子\n"); printf("請輸入位置:"); printf("例如:1 1(該位置對應棋盤左上角第一格,輸入完畢按enter鍵結束)\n"); while (1) { //接收座標 scanf("%d %d", &i, &j); //將座標轉換爲棋盤對應的數組下標並對輸入座標的合法性進行判斷 //座標範圍判斷 if ((i - 1) >= 0 && (i - 1) < row && (j - 1) >= 0 && (j - 1) < row ) { //輸入座標內容判斷,若是沒有棋子則落子 if (board[i - 1][j - 1] == ' ') { board[i - 1][j - 1] = '*'; arr[0] = i-1; //存放落子橫座標對應的數組下標 arr[1] = j-1; //存放落子縱座標對應的數組下標 return arr; //將下標進行返回 } else { printf("該位置已有棋子,請從新輸入!!\n"); } } else { printf("輸入座標不在棋盤範圍內,請從新輸入!!\n"); } } }
電腦落子代碼以下:
//電腦落子 void ComMove(char board[ROW][COL], int row, int col,int arr[]) { printf("該電腦落子:\n"); int i; int j; while (1) { i = rand() % row; //在0-棋盤橫向長度的範圍內生成隨機數 j = rand() % col; //在0-棋盤縱向長度的範圍內生成隨機數 if (board[i][j ] == ' ') { board[i][j] = '#'; arr[0] = i; arr[1] = j; return arr; //將電腦該步的位置返回 } } }
輸贏判斷
每次玩家或電腦進行落子後,都須要進行判斷輸贏。以三子棋(頭文件中COUNT 設爲3)爲例,落子後以該子爲起點,在其橫向、縱向、斜向進行搜索,若是有三個連續相同的子,則提示本輪落子的獲勝,同時退出遊戲。若是棋盤最後一個位置落子後仍未有贏家,則提示平局,並退出遊戲。
由上邊落子代碼看出,落子時返回了落子時的座標,該座標在此處進行傳參。
以橫向搜索爲例:先向右進行搜索,使用一個計數器接收相同棋子的個數,若是碰到相同的棋子則計數器加一,碰到不一樣的棋子(棋盤爲空也算不一樣的棋子)則直接退出搜索;而後向左進行一樣的步驟。搜索時要注意不要超出棋盤範圍,能夠看出,橫向搜索時只有橫座標發生變化,而縱座標沒有發生變化。
其餘三個方向搜索同理(斜向有兩個)。總共有四個方向,因此使用一個長度爲4的一維數組來接收四個方向的計數器值。
若是任意一個方向的棋子數大於或者等於COUNT,則該函數返回落子位置對應的子(這裏就是字符:*或#)。
若是該次落子後尚未贏家,則判斷棋盤是否下滿,若是沒有下滿,則函數返回「C」(Continue,表示繼續下一回合),若是棋盤下滿,則函數返回「E」(End,表示遊戲結束)。
判斷輸贏代碼以下:
//判斷輸贏 char Winner(char board[ROW][COL], int row, int col,int arr[]) { int i; int j; int x = arr[0]; //落子橫座標對應棋盤數組的下標 int y = arr[1]; //落子縱座標對應棋盤數組的下標 int count[4] = {1,1,1,1}; //用於接收四個方向連續子長度的數組 //向左檢查 //縱座標不變,橫座標增長 for (i = x+1; i < row; i++) { if (board[i][y] == board[x][y] && board[x][y] != ' ') { (count[0])++; } else { break; } } //向右檢查 for (i = x-1; i >= 0; i--) { if (board[i][y] == board[x][y] && board[x][y] != ' ') { (count[0])++; } else { break; } } //向上檢查 for (j = y+1; j < col; j++) { if (board[x][j] == board[x][y] && board[x][y] != ' ') { (count[1])++; } else { break; } } //向下檢查 for (j = y-1; j >= 0; j--) { if (board[x][j] == board[x][y] && board[x][y] != ' ') { (count[1])++; } else { break; } } //向右上檢查 i = x + 1; j = y + 1; while (i < row && j < col) { if (board[i][j] == board[x][y] && board[x][y] != ' ') { (count[2])++; i++; j++; } else { break; } } //向左下檢查 i = x - 1; j = y - 1; while (i >= 0 && j >= 0) { if (board[i][j] == board[x][y] && board[x][y] != ' ') { (count[2])++; i--; j--; } else { break; } } //向左上檢查 i = x - 1; j = y + 1; while (i >= 0 && j < col) { if (board[i][j] == board[x][y] && board[x][y] != ' ') { (count[3])++; i--; j++; } else { break; } } //向右下檢查 i = x + 1; j = y - 1; while (i < row && j >= 0) { if (board[i][j] == board[x][y] && board[x][y] != ' ') { (count[3])++; i++; j--; } else { break; } } //任意方向的計數器超過COUNT,就返回落子時對應的子 for (i = 0; i < 4; i++) { if (count[i] >= COUNT) { return board[x][y]; } } //判斷棋盤是否下滿 int ret = IsFull(board, ROW, COL); if (ret) { return 'E'; } return 'C'; }
判斷棋盤是否下滿的函數:對棋盤進行遍歷,若是每一個位置都不爲空,就返回1;不然返回0.
//判斷棋盤是否落滿子 int IsFull(char board[ROW][COL], int row, int col) { int i; int j; for (i = 0; i < row; i++) { for (j = 0; j< row; j++) { if (board[i][j] == ' ') { return 0; } } } return 1; }
game()實現
上邊對該遊戲描述的每一個功能都進行了實現,下邊在game()函數裏對這些函數進行組裝
//遊戲主體 void game(void) { char ret; //用於接收落子座標 int arr[2]; //遊戲界面部分 //建立一個棋盤 char board[ROW][COL] = { 0 }; //初始化棋盤 InitBoard(board, ROW, COL); //打印棋盤 DisplayBoard(board, ROW, COL); //遊戲功能部分 while (1) { //玩家落子 PlayerMove(board, ROW, COL,arr); DisplayBoard(board, ROW, COL); \\落子後進行判斷,由Winner()函數返回值能夠看出,只要返回值是C就繼續進行遊戲 ret = Winner(board, ROW, COL,arr); if (ret !='C') { break; } //電腦落子 ComMove(board, ROW, COL,arr); printf("電腦落子位置是:(%d,%d)\n",arr[0]+1,arr[1]+1); DisplayBoard(board, ROW, COL,arr); ret = Winner(board, ROW, COL,arr); if (ret != 'C') { break; } } if (ret == '*') { printf("恭喜你,勝利!\n"); } else if (ret == '#') { printf("很遺憾,失敗了!\n"); } else { printf("旗鼓至關,打成平局\n"); } }
總結
main.c文件的代碼以下:
#define _CRT_SECURE_NO_WARNINGS 1 #include "game.h" int main(void) { int input; srand((unsigned int)time(NULL)); printf("****三子棋遊戲****\n"); printf("玩家執子:*;電腦執子:#\n"); do { menu(); printf("請選擇:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("遊戲結束。\n"); break; default: printf("輸入錯誤,請從新輸入!!!\n\n"); break; } } while (input); return 0; }
game.c文件的代碼以下:
#define _CRT_SECURE_NO_WARNINGS 1 #include "game.h" //打印菜單 void menu(void) { printf("*************************\n"); printf("**1.play*********0.exit**\n"); printf("*************************\n"); } //初始化棋盤 void InitBoard(char board[ROW][COL],int row, int col) { int i; int j; 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; int j; printf(" "); for (j = 0; j < col; j++) { printf("%2d ",j+1); } printf("\n\n"); for (i = 0; i < row; i++) { printf("%3d ",i+1); 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 PlayerMove(char board[ROW][COL], int row, int col, int arr[]) { int i; int j; printf("該玩家落子\n"); printf("請輸入位置:"); printf("例如:1 1(該位置對應棋盤左上角第一格,輸入完畢按enter鍵結束)\n"); while (1) { scanf("%d %d", &i, &j); if ((i - 1) >= 0 && (i - 1) < row && (j - 1) >= 0 && (j - 1) < row ) { if (board[i - 1][j - 1] == ' ') { board[i - 1][j - 1] = '*'; arr[0] = i-1; arr[1] = j-1; return arr; } else { printf("該位置已有棋子,請從新輸入!!\n"); } } else { printf("輸入座標不在棋盤範圍內,請從新輸入!!\n"); } } } //電腦落子 void ComMove(char board[ROW][COL], int row, int col,int arr[]) { printf("該電腦落子:\n"); int i; int j; while (1) { i = rand() % row; j = rand() % col; if (board[i][j ] == ' ') { board[i][j] = '#'; arr[0] = i; arr[1] = j; return arr; } } } //判斷輸贏 char Winner(char board[ROW][COL], int row, int col,int arr[]) { int i; int j; int x = arr[0]; int y = arr[1]; int count[4] = {1,1,1,1}; //向左檢查 for (i = x+1; i < row; i++) { if (board[i][y] == board[x][y] && board[x][y] != ' ') { (count[0])++; } else { break; } } //向右檢查 for (i = x-1; i >= 0; i--) { if (board[i][y] == board[x][y] && board[x][y] != ' ') { (count[0])++; } else { break; } } //向上檢查 for (j = y+1; j < col; j++) { if (board[x][j] == board[x][y] && board[x][y] != ' ') { (count[1])++; } else { break; } } //向下檢查 for (j = y-1; j >= 0; j--) { if (board[x][j] == board[x][y] && board[x][y] != ' ') { (count[1])++; } else { break; } } //向右上檢查 i = x + 1; j = y + 1; while (i < row && j < col) { if (board[i][j] == board[x][y] && board[x][y] != ' ') { (count[2])++; i++; j++; } else { break; } } //向左下檢查 i = x - 1; j = y - 1; while (i >= 0 && j >= 0) { if (board[i][j] == board[x][y] && board[x][y] != ' ') { (count[2])++; i--; j--; } else { break; } } //向左上檢查 i = x - 1; j = y + 1; while (i >= 0 && j < col) { if (board[i][j] == board[x][y] && board[x][y] != ' ') { (count[3])++; i--; j++; } else { break; } } //向右下檢查 i = x + 1; j = y - 1; while (i < row && j >= 0) { if (board[i][j] == board[x][y] && board[x][y] != ' ') { (count[3])++; i++; j--; } else { break; } } for (i = 0; i < 4; i++) { if (count[i] >= COUNT) { return board[x][y]; } } //棋盤是否滿? int ret = IsFull(board, ROW, COL); if (ret) { return 'E'; } return 'C'; } //判斷棋盤是否落滿子 int IsFull(char board[ROW][COL], int row, int col) { int i; int j; for (i = 0; i < row; i++) { for (j = 0; j< row; j++) { if (board[i][j] == ' ') { return 0; } } } return 1; } //遊戲主體 void game(void) { char ret; //用於接收落子座標 int arr[2]; //遊戲界面部分 //建立一個棋盤 char board[ROW][COL] = { 0 }; //初始化棋盤 InitBoard(board, ROW, COL); //打印棋盤 DisplayBoard(board, ROW, COL); //遊戲功能部分 while (1) { //玩家落子 PlayerMove(board, ROW, COL,arr); DisplayBoard(board, ROW, COL); ret = Winner(board, ROW, COL,arr); if (ret !='C') { break; } //電腦落子 ComMove(board, ROW, COL,arr); printf("電腦落子位置是:(%d,%d)\n",arr[0]+1,arr[1]+1); DisplayBoard(board, ROW, COL,arr); ret = Winner(board, ROW, COL,arr); if (ret != 'C') { break; } } if (ret == '*') { printf("恭喜你,勝利!\n"); } else if (ret == '#') { printf("很遺憾,失敗了!\n"); } else { printf("旗鼓至關,打成平局\n"); } }
game.h頭文件代碼以下:
#include <stdio.h> #include <stdlib.h> #include <time.h> #ifndef __GAME_H__ #define __GAME_H__ #define COL 5 #define ROW 5 #define COUNT 3 void menu(void); void game(void); 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,int arr[]); void ComMove(char board[ROW][COL], int row, int col,int arr[]); char Winner(char board[ROW][COL], int row, int col,int arr[]); int IsFull(char board[ROW][COL], int row, int col); #endif