前言
本篇文章記錄了用C語言實作三子棋小游戲,主要目的是對之前C語言知識學習的鞏固,聯系各個知識,以及怎么樣實際使用各個知識,
一、三子棋的游戲規則
玩家將會看到一個3X3的網格棋盤,默認玩家先下棋,電腦后下棋,
規定:先連成一條直線(3個棋子)的玩家獲勝,行,列,對角線均可,若在棋盤下滿時仍未分出勝負,則為平局
二、使用到的頭檔案
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
定義3*3的棋盤
#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 player_move(char board[ROW][COL], int row, int col);
電腦下棋:
void computer_move(char board[ROW][COL], int row, int col);
判斷輸贏:
//玩家贏 - '*'
//電腦贏 - '#'
//平局 - 'Q'
//繼續 - 'C'
char is_win(char board[ROW][COL],int row,int col);
三、游戲的測驗
主函式:
int main() {
test();//呼叫test測驗函式
return 0;
}
test函式:
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);
}
決議test函式主要構成:
i>選單menu()函式:
void menu() {
printf("******************************\n");
printf("***** 1. play ********\n");
printf("***** 0. exit ********\n");
printf("******************************\n");
}
實作效果:

ii>switch case 陳述句:實作了玩家通過不同輸入選擇實作不同的功能,
效果顯示:


iii>game()函式:當玩家輸入1時,跳入到游戲的實作代碼之中,
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");
}
}
game()函式里面包括了:列印棋盤函式,初始化棋盤函式,玩家下棋函式,電腦下棋函式,判斷輸贏函式的呼叫,已經規定輸贏的條件,具體每個函式的實作代碼和問題如下,
注:最后有game()函式的邏輯順序,
四、游戲的實作
游戲的實作邏輯:
(一)列印棋盤
我們首先實作對棋盤的列印:
我們在頭檔案中規定了 3行3列
#define ROW 3
#define COL 3
自定義函式DisplayBoard實作對棋盤的列印:
預期實作的棋盤模樣:

我們發現里面包含了數據和分割行,實作代碼:
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");
}
}
(二)初始化棋盤函式
定義InitBoard()函式,主要實作代碼如下:
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++) { //3行
for (j = 0; j < col; j++) { // 3列
board[i][j] = ' ';
}
}
}
初始化棋盤全為空格
(三)玩家下棋
自定義函式 player_move 實作,代碼如下:
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("該坐標被占用,請重新輸入!\n");
}
}
else {
printf("坐標非法,請重新輸入!\n");
}
}
}
這里面包含了很多小問題和解決方案:
1:輸入的坐標不在棋盤的范圍內怎么辦?
答:使用if else 選擇陳述句,如果輸入的坐標合法,則輸入成功,如果輸入的坐標不合法則會提醒“坐標非法,請重新輸入!”字樣,
if (x >= 1 && x <= row && y >= 1 && y <= col)
2:輸入的坐標在棋盤上已被占用怎么辦?
這個問題我們需要轉化成這個問題解決:
i>怎么判斷輸入坐標被占用?
答:我們通過對選擇陳述句if else 對改坐標進行判斷,由于我們在初始化棋盤時,初始化棋盤全部為空格,如果輸入坐標所對應的格子是空格,則說明未被占用,如果輸入坐標所對應的格子不是空格,則說明該格子已經被占用,
if (board[x - 1][y - 1] == ' ')
ii>如何正確輸入棋子?
如果輸入坐標所對應的格子是空格,則可以成功輸入,如果不是空格,則會else提醒玩家“該坐標被占用,請重新輸入!”字樣,利用while回圈陳述句可再次輸入正確的未被占用的坐標,
(四)電腦下棋
自定義函式 computer_move 實作,代碼如下:
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;
}
}
}
這里也包含了幾個小問題和解決方案:
1:這里電腦的輸入是隨機輸入,如何實作?
答:這里我們并非是真的隨機值,只是利用了時間戳,由于時間在每時每刻的改變,所以在不同的時間,經過處理后的時間我們也無法知道,可當做隨機值使用,這里呼叫了time函式,實作代碼如下:
#include <time.h>
srand((unsigned int)time(NULL));
由于在這里強制轉化為正整數,
2:如何控制電腦隨機輸入的值合法?
答:這個問題的意思是,如果不對這個隨機值進行處理,那么這個值很可能超出棋盤,所以要對這個值進行控制,由于棋盤規定3行3列,陣列下標由0開始,所以隨機值只要在0~2之內即可,所以我們對這個隨機值進行取模(取余數)處理即可解決此問題:
x = rand() % ROW; // 0 ~ 2
y = rand() % COL; // 0 ~ 2
為了區分玩家和電腦的棋子不同,在此規定
玩家使用 ' * ' , 電腦使用 ' # '
因為這里電腦輸入坐標均為隨機值,所以不具有判斷的能力,讀者可以思考一種優化演算法,寫出一種最快贏下比賽的演算法,而不至于出現可能都已經一行出現2個棋子了,隨機值可能再下到別處的情況(哈哈 人工“智障”),
(五)判斷輸贏
實作代碼:
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';
}
游戲規定,首先完成一行或者一列或者對角線的玩家獲勝,在此我們只需要進行三行,三列,對角線的判斷以及平局的判斷,游戲繼續判斷,具體是玩家贏還是電腦贏,我們在測驗函式test中有規定,不同的玩家對應不同的字符,在此規定:

三行的判斷:
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];
}
平局的判斷:
游戲規定,當棋盤下滿時,還未分出勝負時,則為平局,
因此首先我們思考如何判斷棋盤是否下滿?
答:在這里我們通過回傳值的數字來判斷是否下滿
自定義函式 is_full函式,具體代碼:
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;
}
代碼邏輯:我們通過回傳值來判斷棋盤是否下滿,
如果未被下滿,則回傳值是0(return 0 )
如果下滿我們將回傳1(return 1)
小問題:我們如何判斷是否下滿?
我們遍歷陣列的程序中,如果發現還有空格存在,說明棋盤還沒有被下滿,我們立刻回傳0,否則說明棋盤沒有空格存在,說明已經下滿,回傳1.
所以我們就可以通過回傳值判斷平局,代碼如下:
if (1 == is_full(board, row, col)) {
return 'Q';
}
如果回傳值 == 1 說明 棋盤被下滿 我們回傳字符 ' Q ' (我們剛剛已經規定Q為平局)
五、game()函式
我們再次將game()函式調出進行詳解:
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");
}
}
根據代碼發現主要邏輯順序:

這是game()函式內部的主要邏輯順序,每一步所對應的函式呼叫和判斷也均在圖中顯示,
六、演示
完整代碼: 完整代碼我放在我的Gitee倉庫內,鏈接如下:
https://gitee.com/Yaulixingyu/c-language/tree/master/三子棋/三子棋
游戲演示:


總結
以上就是我對三棋子小游戲的實作,有興趣的小伙伴可以對代碼進行優化,可實作五子棋等,也可以對電腦下棋記性優化,使電腦不再人工“智障”,
哈哈哈~由于文章較長,感謝各位小伙伴的觀看,由于本人的技術水平有待提高,如果遇到錯誤也請及時指正,如果各位小伙伴覺得不錯的話,可以點贊關注一波~多謝多謝
下期預告:掃雷 小游戲
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/356199.html
標籤:其他
