今天要完成一個相對於之前學習更有挑戰性的小程序——三子棋。
相信我們大部分人都接觸過三子棋,這是一款操作簡單易上手的小游戲。
先簡單介紹一下三子棋的規則,方便我們接下來的編程和理解。規則如下:
在九宮格棋盤上,只要將自己的三個棋子走成一條線(橫、豎、對角線),對方就算輸了。
規則很簡單,但是我們應該從哪里入手完成這個三子棋程序呢?
首先,我們的游戲程序應該先有個菜單頁面,這個菜單頁面用來讓用戶操作選擇玩游戲,或是退出。
界面可以很簡單,只需要幾條 printf 語句即可。
這里我們盡可能用函數來完成游戲的功能:
void menu() { printf("***************************************\n"); printf("* 1.play 0.exit *\n"); printf("***************************************\n"); }
這就是一個簡單的主菜單界面,效果如下:
既然菜單中有選項,我們就必須要設計一個變量用來接收用戶的選擇。
而且游戲程序本身應該允許用戶反復進行玩耍,所以在主函數中,我們需要用一個循環來完成這個功能。
do-while循環在調用時會先運行循環體,而后在進行判斷,所以這里我們選擇使用do-while循環:
int main() { int choice; do { menu(); printf("Input your choice:"); scanf("%d", &choice); switch (choice) { case 1: game(); break; case 0: break; default: printf(" Input error!Please try again.\n"); break; } } while (choice); return 0; }
由於用戶選擇選項1時我們需要為用戶啟動游戲程序,所以這里我們使用一個switch語句來接受用戶的選擇。
選擇0時退出程序,所以我們只需要跳出循環,即可退出。
而輸入0、1之外的值時,我們應該向用戶報輸入錯誤,所以在default中輸出一條報錯信息。
接下來就是我們程序的重點——三子棋游戲本身了。
先創建一個game()函數,里面的內容我們一步步來填充。
void game() { }
在程序開始階段,我們首先要創建一個3*3的二維字符數組,
由於長和寬我們要經常用到,為了后續修改方便,我們采用宏定義方式:
#define ROWS 3 #define COLS 3
接下來在game()函數中創建數組:
char board[ROWS][COLS];
並將這個數組進行初始化,因為這個數組在后續需要輸出展示在用戶面前,所以我們將值初始化為空格“ ”。
初始化用函數進行完成:
void init_board(char board[ROWS][COLS], int rows, int cols) { int i, j; for (i = 0; i < rows; i++) { for (j = 0; j < cols; j++) { board[i][j] = ' '; } } }
接下來,我們應該輸出這個數組棋盤,來檢查一下是否初始化完成,
但我們沒有展示棋盤的函數,所以我們需要完成這個顯示棋盤函數:
void show_board(char board[ROWS][COLS], int rows, int cols) { int i; for (int i = 0; i < rows; i++) { printf(" %c | %c | %c \n",board[i][0],board[i][1],board[i][2]); if (i != rows - 1) printf(" ---|---|--- \n"); } }
這里需要利用循環來輸出我們棋盤的格式,格式不統一,可以根據自己的喜好進行修改。
下面展示一下我這個代碼所輸出的棋盤:
上面已經看出我的棋盤初始化已經完成,所以接下來我們需要考慮如何讓電腦或用戶進行下棋操作了。
電腦走需要讓電腦產生隨機坐標,然后在數組的這個坐標位置放入一個“X”作為電腦的棋子。
所以代碼如下:
void ComputerMove(char board[ROWS][COLS], int rows, int cols) { int x, y; printf("Compter's turn to move:\n"); while (1) { x = rand() % rows; y = rand() % cols; if (board[x][y] == ' ') { board[x][y] = 'X'; break; } } }
rand()是用來生成隨機數的,這里我們還需要在主函數main中加上一行代碼。
由於我們不希望他多次生成隨機數,生成一次就足夠用了,所以直接在創建choice變量的下一行加上下面這個代碼即可:
srand((unsigned int)time(NULL));
注意生成隨機數需要引用time.h這個頭文件。
電腦下完棋后,需要輪到我們的用戶進行下棋操作,這個函數和電腦下棋大同小異,
但是要注意人的習慣不會輸入(0,0)這個坐標,而是輸入(1,1)。
所以這里我們要給傳過去的參數-1。
解決這個問題后的代碼如下:
void player_move(char board[ROWS][COLS], int rows, int cols) { int x, y; printf("Player's turn to move:\n"); while (1) { printf("Input x and y(like x y):\n"); scanf("%d %d", &x, &y); if (x >= 1 && x <= rows && y >= 1 && y <= cols) { if (board[x - 1][y - 1] == ' ') { board[x - 1][y - 1] = 'O'; break; } else printf("The location has been taken!Please try again.\n"); } else printf(" Input error!Please try again.\n"); } }
在雙方走完后,我們需要判斷是否勝利,也就是判斷之前我們提到的規則,
這時我們需要編寫一個checkwin()函數來檢測並返回一個值表示誰獲勝。
代碼如下:
char check_win(char board[ROWS][COLS], int rows, int cols) { int i; for (i = 0; i < rows; 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 < cols; 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]; else if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ') return board[1][1]; else if (is_full(board, rows, cols)) return 'q'; return 0; }
由於可能出現棋盤下滿,卻沒人獲勝的情況,
所以我們需要在其中判斷一下平局的情況,
這里我們編寫了一個is_full()函數,用來判斷棋盤是否下滿:
static int is_full(char board[ROWS][COLS], int rows, int cols) { int i, j; for (i = 0; i < rows; i++) { for (j = 0; j < cols; j++) { if (board[i][j] == ' ') return 0; } } return 1; }
注意這個函數我們只在checkwin中會使用到,我們並不希望它在其他源文件的函數中被使用,所以我們加上了static。
這時程序的雛形已經基本完成,我們只需要按我們自己理解的游戲步驟來完成game()函數即可。
所以完成后的game()函數如下所示:
void game() { char win = 0; char arr[ROWS][COLS]; init_board(board, ROWS, COLS); do { computer_move(board, ROWS, COLS); show_board(board, ROWS, COLS); win = check_win(board, ROWS, COLS); if (win != 0) break; player_move(board, ROWS, COLS); show_board(board, ROWS, COLS); win = check_win(board, ROWS, COLS); } while (win == 0); if (win == 'X') printf("You lose!Good luck next time!\n"); if (win == 'O') printf("You win!Congratulations!\n"); if (win == 'q') printf("The score was tied!Good luck next time!\n"); }
這樣,將上面各個部分合在一起,這個程序就被我們完成了。
下面放上我自己寫的,比較丑陋的代碼:
我的代碼分為三部分,
第一部分是頭文件:
#ifndef __CHESS_H__ #define __CHESS_H__ #include<stdio.h> #include<time.h> #include<Windows.h> #define ROWS 3 #define COLS 3 void show_board(char board[ROWS][COLS], int rows, int cols); void init_board(char board[ROWS][COLS], int rows, int cols); void computer_move(char board[ROWS][COLS], int rows, int cols); void player_move(char board[ROWS][COLS], int rows, int cols); char check_win(char board[ROWS][COLS], int rows, int cols); # endif//__CHESS_H__
第二部分是源文件的函數部分:
#include"3chess.h" void show_board(char board[ROWS][COLS], int rows, int cols) { int i; for (int i = 0; i < rows; i++) { printf(" %c | %c | %c \n",board[i][0],board[i][1],board[i][2]); if (i != rows - 1) printf(" ---|---|--- \n"); } } void init_board(char board[ROWS][COLS], int rows, int cols) { int i, j; for (i = 0; i < rows; i++) { for (j = 0; j < cols; j++) { board[i][j] = ' '; } } } void computer_move(char board[ROWS][COLS], int rows, int cols) { int x, y, i; printf("Compter's turn to move:\n"); while (1) { x = rand() % rows; y = rand() % cols; for (i = 0; i < rows; i++) { if (board[i][0] == board[i][1] && board[i][0] == 'X' && board[i][2] == ' ') { board[i][2] = 'X'; goto flag1; } else if (board[i][1] == board[i][2] && board[i][1] == 'X' && board[i][0] == ' ') { board[i][0] = 'X'; goto flag1; } else if (board[i][0] == board[i][2] && board[i][2] == 'X' && board[i][1] == ' ') { board[i][1] = 'X'; goto flag1; } else if (board[0][i] == board[1][i] && board[0][i] == 'X' && board[2][i] == ' ') { board[2][i] = 'X'; goto flag1; } else if (board[1][i] == board[2][i] && board[1][i] == 'X' && board[0][i] == ' ') { board[0][i] = 'X'; goto flag1; } else if (board[0][i] == board[2][i] && board[2][i] == 'X' && board[1][i] == ' ') { board[1][i] = 'X'; goto flag1; } else if (board[0][0] == board[1][1] && board[0][0] == 'X' && board[2][2] == ' ') { board[2][2] = 'X'; goto flag1; } else if (board[1][1] == board[2][2] && board[1][1] == 'X' && board[0][0] == ' ') { board[0][0] = 'X'; goto flag1; } else if (board[0][0] == board[2][2] && board[0][0] == 'X' && board[1][1] == ' ') { board[1][1] = 'X'; goto flag1; } else if (board[0][2] == board[1][1] && board[1][1] == 'X' && board[2][0] == ' ') { board[2][0] = 'X'; goto flag1; } else if (board[1][1] == board[2][0] && board[1][1] == 'X' && board[0][2] == ' ') { board[0][2] = 'X'; goto flag1; } else if (board[2][0] == board[0][2] && board[2][0] == 'X' && board[1][1] == ' ') { board[1][1] = 'X'; goto flag1; } } for (i = 0; i < rows; i++) { if (board[i][0] == board[i][1] && board[i][0] == 'O' && board[i][2] == ' ') { board[i][2] = 'X'; goto flag1; } else if (board[i][1] == board[i][2] && board[i][1] == 'O' && board[i][0] == ' ') { board[i][0] = 'X'; goto flag1; } else if (board[i][0] == board[i][2] && board[i][2] == 'O' && board[i][1] == ' ') { board[i][1] = 'X'; goto flag1; } else if (board[0][i] == board[1][i] && board[0][i] == 'O' && board[2][i] == ' ') { board[2][i] = 'X'; goto flag1; } else if (board[1][i] == board[2][i] && board[1][i] == 'O' && board[0][i] == ' ') { board[0][i] = 'X'; goto flag1; } else if (board[0][i] == board[2][i] && board[2][i] == 'O' && board[1][i] == ' ') { board[1][i] = 'X'; goto flag1; } else if (board[0][0] == board[1][1] && board[0][0] == 'O' && board[2][2] == ' ') { board[2][2] = 'X'; goto flag1; } else if (board[1][1] == board[2][2] && board[1][1] == 'O' && board[0][0] == ' ') { board[0][0] = 'X'; goto flag1; } else if (board[0][0] == board[2][2] && board[0][0] == 'O' && board[1][1] == ' ') { board[1][1] = 'X'; goto flag1; } else if (board[0][2] == board[1][1] && board[1][1] == 'O' && board[2][0] == ' ') { board[2][0] = 'X'; goto flag1; } else if (board[1][1] == board[2][0] && board[1][1] == 'O' && board[0][2] == ' ') { board[0][2] = 'X'; goto flag1; } else if (board[2][0] == board[0][2] && board[2][0] == 'O' && board[1][1] == ' ') { board[1][1] = 'X'; goto flag1; } } if (board[x][y] == ' ') { board[x][y] = 'X'; goto flag1; } } flag1:; } void player_move(char board[ROWS][COLS], int rows, int cols) { int x, y; printf("Player's turn to move:\n"); while (1) { printf("Input x and y(like x y):\n"); scanf("%d %d", &x, &y); if (x >= 1 && x <= rows && y >= 1 && y <= cols) { if (board[x - 1][y - 1] == ' ') { board[x - 1][y - 1] = 'O'; break; } else printf("The location has been taken!Please try again.\n"); } else printf(" Input error!Please try again.\n"); } } static int is_full(char board[ROWS][COLS], int rows, int cols) { int i, j; for (i = 0; i < rows; i++) { for (j = 0; j < cols; j++) { if (board[i][j] == ' ') return 0; } } return 1; } char check_win(char board[ROWS][COLS], int rows, int cols) { int i; for (i = 0; i < rows; 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 < cols; 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]; else if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ') return board[1][1]; else if (is_full(board, rows, cols)) return 'q'; return 0; }
這一部分是經過發揮的,讓電腦更智能,但代碼部分我並沒有想出簡化的寫法,只好一步步的用if語句進行完成了。
第三部分是源文件的游戲測試部分:
#include"3chess.h" void menu() { printf("***************************************\n"); printf("* 1.play 0.exit *\n"); printf("***************************************\n"); } void first_move() { printf("***************************************\n"); printf("* 1.computer first 2.player first *\n"); printf("***************************************\n"); } void game() { int choice_1, win; char board[ROWS][COLS]; init_board(board, ROWS, COLS); first_move(); flag: printf("Input your choice;"); scanf("%d", &choice_1); switch (choice_1) { case 1: do { computer_move(board, ROWS, COLS); show_board(board, ROWS, COLS); win = check_win(board, ROWS, COLS); if (win != 0) break; player_move(board, ROWS, COLS); show_board(board, ROWS, COLS); win = check_win(board, ROWS, COLS); } while (win == 0); if (win == 'X') printf("You lose!Good luck next time!\n"); if (win == 'O') printf("You win!Congratulations!\n"); if (win == 'q') printf("The score was tied!Good luck next time!\n"); break; case 2: do { show_board(board, ROWS, COLS); player_move(board, ROWS, COLS); show_board(board, ROWS, COLS); win = check_win(board, ROWS, COLS); if (win != 0) break; computer_move(board, ROWS, COLS); win = check_win(board, ROWS, COLS); } while (win == 0); if (win == 'X') printf("You lose!Good luck next time!\n"); if (win == 'O') printf("You win!Congratulations!\n"); if (win == 'q') printf("The score was tied!Good luck next time!\n"); break; default: printf(" Input error!Please try again.\n"); goto flag; } } int main() { int choice; srand((unsigned int)time(NULL)); do { menu(); printf("Input your choice:"); scanf("%d", &choice); switch (choice) { case 1: game(); break; case 0: break; default: printf(" Input error!Please try again.\n"); break; } } while (choice); return 0; }
這一部分,我加入了讓用戶選擇電腦先走或者用戶先走的功能,使用戶的游戲體驗更好。
以上就是我的代碼,希望各位能幫助我進行優化改進。