文章目錄
- 前言
- 一、問題描述
- 二、基本流程
- 三、步驟
- 1.構建程式主體框架以及選單的實作
- 2.良好的宏定義增強代碼可讀性
- 3.構建游戲入口PlayersGame()函式
- 4.實作列印棋盤函式
- 5.實作玩家落子函式
- 6.實作判斷輸贏函式
- 四、結果演示
- 五、具體代碼
- 總結
前言
初學C語言,了解基本語法后,可以用來練練手,一、問題描述
用C語言實作玩家對戰的五子棋,
二、基本流程
在具體寫代碼之前,先來確定要實作的功能和實作流程,
- 創建選單界面并選擇開始或者退出游戲——選單函式,
- 若選擇開始則列印棋盤——列印棋盤的函式,
- 玩家1,玩家2,分別開始落子——落子函式,
- 每當玩家落子就需要判斷輸贏——判斷是否結束的函式,
- 若結束則再次列印棋盤,并輸出結果,
- 否則回傳第二步開始回圈,
三、步驟
為了讓代碼和邏輯更清晰,方便修改,除錯,我們采用多檔案協作,可創建三個檔案,gobang.h用來存放頭檔案,函式以及宏定義的宣告;gobang.c用于存放函式的定義;gobang_test.c用來測驗程式,分別在gobang.c和gobang_test.c中引入頭檔案gobang.h即可,
1.構建程式主體框架以及選單的實作
此步驟放在gobang_test.c中,是程式的開始,
包括選單的實作,以及使用一個do while回圈控制是否退出游戲,其中用switch陳述句實作用戶的選擇,
#include"gobang.h"
//游戲選單
void Menu()
{
printf("****************************************\n");
printf("**** 1.玩家PK ****\n");
printf("**** 0.退出 ****\n");
printf("****************************************\n");
}
int main()
{
int choice;
do
{
Menu();
printf("請選擇:>");
scanf("%d", &choice);
switch (choice)
{
case 1:
PlayersGame();
break;
case 0:
break;
default:
printf("輸入錯誤,請重新選擇:>");
break;
}
} while (choice);
return 0;
}
2.良好的宏定義增強代碼可讀性
宏定義可以讓代碼的可讀性更好,也可以讓我們寫代碼時減少出錯,
#define ROW 20 //陣列行數,可以按照需求調整
#define COL 20 //陣列列數,可以按照需求調整
#define PLAYER1 1 //玩家1編號,默認棋盤資料是0,玩家1落子,該位置被改成1
#define PLAYER2 2 //玩家2編號,默認棋盤資料是0,玩家2落子,該位置被改成2
//落子之后的四種情況
#define NEXT 0 //游戲繼續
#define PLAYER1_WIN 1 //玩家1贏了
#define PLAYER2_WIN 2 //玩家2贏了
#define DRAW 3 //平局
這些定義后面都會用到,
3.構建游戲入口PlayersGame()函式
這是主要的五子棋框架,將會呼叫具體函式來完成,
可以將需要的函式先寫出來,再具體去實作,
這里需要落一次子,就判斷一次,
//玩家對戰
void PlayersGame()
{
int board[ROW][COL];//創建一個二維陣列作為棋盤
memset(board, 0, sizeof(board));//將陣列每個元素賦值為零
int row = ROW;
int col = COL;
int ret = NEXT;//獲取每落一個子的結果
while (1)
{
ShowBoard(board, row, col);
PlayerMove(board, row, col, PLAYER1);
ret = IsOver(board, row, col); //判斷是否結束
//如果不是繼續就退出
if (ret != NEXT)
break;
//玩家2
ShowBoard(board, row, col);
PlayerMove(board, row, col, PLAYER2);
ret = IsOver(board, row, col); //判斷是否結束
//如果不是繼續就退出
if (ret != NEXT)
break;
}
ShowBoard(board, row, col);
switch (ret)
{
case PLAYER1_WIN:
printf("恭喜玩家1獲勝\n");
break;
case PLAYER2_WIN:
printf("恭喜玩家1獲勝\n");
break;
case DRAW:
printf("平局,再戰一次?\n");
break;
default:
break;
}
}
4.實作列印棋盤函式
為了更加真實的模擬棋盤,我們可以選取一些特殊符號,
默認符號比較丑陋,我們需要使用美化后的符號,下面鏈接里面可找到所需的黑白棋,復制粘貼即可,
特殊符號大全.
列印的時候,要根據元素的內容分別列印對印的符號,
我這里的列和行都是以0開始,
//列印棋盤
void ShowBoard(int board[][COL], int row, int col)
{
system("cls");
printf(" ");
for (int i = 0; i < col; i++)
{
printf("%-3d", i);//列印列號
}
printf("\n");
for (int i = 0; i < row; i++)
{
printf("%2d", i);//列印行號
for (int j = 0; j < col; j++)
{
if (board[i][j] == PLAYER1)
{
//玩家1落子
printf(" ●");
}
else
{
if (board[i][j] == PLAYER2)
printf(" ○"); //玩家2落子
else
printf(" ·"); //沒有玩家落子
}
}
printf("\n");
}
printf("\n");
}
5.實作玩家落子函式
要實作玩家落子,可以用兩個整形變數來獲取坐標,但是后面判斷輸贏函式我們也會用到輸入的坐標,所以需要設定成全域變數,
//因為判定結果時需要用到玩家落子的坐標,所有設定兩個全域變數方便使用
int x, y;//x是行,y是列
引數player是用來記錄當前是哪個玩家落子,
//玩家下棋
void PlayerMove(int board[][COL], int row, int col , int player)
{
printf("請玩家%d輸入落子的坐標:>",player);
while (1)
{
scanf("%d%d", &x, &y);
if (x<0 || x>=row || y<0 || y>=col)
{
printf("輸入坐標錯誤,請重新輸入:>");
}
else
{
if (board[x][y] != 0)
{
printf("此坐標已有子,請重新輸入:>");
}
else
{
board[x][y] = player;//將該點改為落子的玩家的值
break;
}
}
}
}
6.實作判斷輸贏函式
這是實作五子棋的難點,

任何落子位置都有八個方向,所以判定五子連珠,本質是判定1,5方向之和,2,6方向之和,3,7方向之和,4,8方向之和,其中任意一個出現相同的連續五個棋子,即游戲結束
為了記錄這八個方向我們采用列舉來記錄,增加代碼的可讀性
// 列舉八個方向
//左右,上下,左上,左下,右上,右下
enum Dir {
LEFT,
RIGHT,
UP,
DOWN,
LEFT_UP,
RIGHT_DOWN,
RIGHT_UP,
LEFT_DOWN
};
這里就需要再創建一個用來記錄相連棋子數量的函式,
引數enum dir d是傳入的方向,
我們采用while回圈和switch陳述句,用來回圈筆記該方向上相同棋子的數量,
我們可以參照上圖,對不同的方向,坐標做相應的加減,
//棋子計數
int ChessCount(int board[][COL], int row, int col, enum dir d)
{
int count = 0;
int _x = x;
int _y = y;
while (1)
{
switch (d)
{
case UP:
//上方
_x--;
break;
case DOWN:
_x++;
break;
case LEFT:
_y--;
break;
case RIGHT:
_y++;
break;
case LEFT_UP:
_x--; _y--;
break;
case LEFT_DOWN:
_x++; _y--;
break;
case RIGHT_UP:
_x--; _y++;
break;
case RIGHT_DOWN:
_x++; _y++;
break;
default:
break;
}
//移動坐標之后,保證是否越界
if (x < 0 || x >= row || y < 0 || y >= col)
{
break;
}
//判斷是否和原棋子相同,相同則計數
if (board[_x][_y] == board[x][y])
{
count++;
}
else
{
break;
}
}
return count;
}
四、結果演示
這里使用了清屏函式system(“cls”)使得界面更加整潔,

五、具體代碼
gobang.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#define ROW 20 //陣列行數,可以按照需求調整
#define COL 20 //陣列列數,可以按照需求調整
#define PLAYER1 1 //玩家1編號,默認棋盤資料是0,玩家1落子,該位置被改成1
#define PLAYER2 2 //玩家2編號,默認棋盤資料是0,玩家2落子,該位置被改成2
//落子之后的四種情況
#define NEXT 0 //游戲繼續
#define PLAYER1_WIN 1 //玩家1贏了
#define PLAYER2_WIN 2 //玩家2贏了
#define DRAW 3 //平局
//因為判定結果時需要用到玩家落子的坐標,所有設定兩個全域變數方便使用
int x, y;//x是行,y是列
// 列舉八個方向
//左右,上下,左上,左下,右上,右下
enum Dir {
LEFT,
RIGHT,
UP,
DOWN,
LEFT_UP,
RIGHT_DOWN,
RIGHT_UP,
LEFT_DOWN
};
//玩家對戰
void PlayersGame();
//列印棋盤
void ShowBoard(int board[][COL], int row, int col);
//玩家下棋
void PlayerMove(int board[][COL], int row, int col, int player);
//判斷是否結束
int IsOver(int board[][COL], int row, int col);
//棋子計數
int ChessCount(int board[][COL], int row, int col, enum dir d);
//判斷棋盤是否滿
//滿了回傳1
int BoardIsFull(int board[][COL], int row, int col);
gobang.c
#include"gobang.h"
//玩家對戰
void PlayersGame()
{
int board[ROW][COL];//創建一個二維陣列作為棋盤
memset(board, 0, sizeof(board));//將陣列每個元素賦值為零
int row = ROW;
int col = COL;
int ret = NEXT;//獲取每落一個子的結果
while (1)
{
ShowBoard(board, row, col);
PlayerMove(board, row, col, PLAYER1);
ret = IsOver(board, row, col); //判斷是否結束
//如果不是繼續就退出
if (ret != NEXT)
break;
//玩家2
ShowBoard(board, row, col);
PlayerMove(board, row, col, PLAYER2);
ret = IsOver(board, row, col); //判斷是否結束
//如果不是繼續就退出
if (ret != NEXT)
break;
}
ShowBoard(board, row, col);
switch (ret)
{
case PLAYER1_WIN:
printf("恭喜玩家1獲勝\n");
break;
case PLAYER2_WIN:
printf("恭喜玩家1獲勝\n");
break;
case DRAW:
printf("平局,再戰一次?\n");
break;
default:
break;
}
}
//列印棋盤
void ShowBoard(int board[][COL], int row, int col)
{
system("cls");
printf(" ");
for (int i = 0; i < col; i++)
{
printf("%-3d", i);//列印列號
}
printf("\n");
for (int i = 0; i < row; i++)
{
printf("%2d", i);//列印行號
for (int j = 0; j < col; j++)
{
if (board[i][j] == PLAYER1)
{
//玩家1落子
printf(" ●");
}
else
{
if (board[i][j] == PLAYER2)
printf(" ○"); //玩家2落子
else
printf(" ·"); //沒有玩家落子
}
}
printf("\n");
}
printf("\n");
}
//玩家下棋
void PlayerMove(int board[][COL], int row, int col , int player)
{
printf("請玩家%d輸入落子的坐標:>",player);
while (1)
{
scanf("%d%d", &x, &y);
if (x<0 || x>=row || y<0 || y>=col)
{
printf("輸入坐標錯誤,請重新輸入:>");
}
else
{
if (board[x][y] != 0)
{
printf("此坐標已有子,請重新輸入:>");
}
else
{
board[x][y] = player;//將該點改為落子的玩家的值
break;
}
}
}
}
//判斷是否結束
int IsOver(int board[][COL], int row, int col)
{
//分別統計四條線上的棋子數量
int count1 = ChessCount(board, row, col, LEFT) + ChessCount(board, row, col, RIGHT) + 1;
int count2 = ChessCount(board, row, col, UP) + ChessCount(board, row, col, DOWN) + 1;
int count3 = ChessCount(board, row, col, LEFT_UP) + ChessCount(board, row, col, RIGHT_DOWN) + 1;
int count4 = ChessCount(board, row, col, LEFT_DOWN) + ChessCount(board, row, col, RIGHT_UP) + 1;
if (count1 >= 5 || count2 >= 5 || count3 >= 5 || count4 >= 5)
{
if (board[x][y] == PLAYER1)
return PLAYER1_WIN;
else
return PLAYER2_WIN;
}
//如果沒有五子聯珠,判斷棋盤是否滿了
if (BoardIsFull(board, row, col))
return DRAW;//滿了就平局
else
return NEXT;//沒滿就繼續
}
//棋子計數
int ChessCount(int board[][COL], int row, int col, enum dir d)
{
int count = 0;
int _x = x;
int _y = y;
while (1)
{
switch (d)
{
case UP:
//上方
_x--;
break;
case DOWN:
_x++;
break;
case LEFT:
_y--;
break;
case RIGHT:
_y++;
break;
case LEFT_UP:
_x--; _y--;
break;
case LEFT_DOWN:
_x++; _y--;
break;
case RIGHT_UP:
_x--; _y++;
break;
case RIGHT_DOWN:
_x++; _y++;
break;
default:
break;
}
//移動坐標之后,保證是否越界
if (x < 0 || x >= row || y < 0 || y >= col)
{
break;
}
//判斷是否和原棋子相同,相同則計數
if (board[_x][_y] == board[x][y])
{
count++;
}
else
{
break;
}
}
return count;
}
//判斷棋盤是否滿
//滿了回傳1
int BoardIsFull(int board[][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (board[i][j] == 0)
return 0;
}
}
return 1;
}
gobang_test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"gobang.h"
//游戲選單
void Menu()
{
printf("****************************************\n");
printf("**** 1.玩家PK ****\n");
printf("**** 0.退出 ****\n");
printf("****************************************\n");
}
int main()
{
int choice;
do
{
Menu();
printf("請選擇:>");
scanf("%d", &choice);
switch (choice)
{
case 1:
PlayersGame();
break;
case 0:
break;
default:
printf("輸入錯誤,請重新選擇:>");
break;
}
} while (choice);
return 0;
}
總結
這里僅僅是玩家之間的對戰,歡迎各位大佬實作在線對戰,以及人機對戰,轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/301570.html
標籤:其他
