用C语言分模块写出三子棋代码
游戏头文件 game.h
游戏源文件 game.c
主源文件 test.c
我们要实现一个如下三子棋(简单逻辑):
1.游戏开始,进入游戏界面,玩家自行选择是否开始游戏
2.创建一个空棋盘,让玩家可以下棋
3.创建过后,打印棋盘让玩家可以观察棋盘大小
4.玩家进行落子(以坐标形式进行落子)--(x y)
其中x(1<=x<=3) y(1<=y<=3)
5.玩家进行落子后,须判断玩家落子是否正确
6.电脑下棋
7.每一轮落子过后判断是否游戏输赢决定是否结束游戏或继续下棋
1.玩家需要一个可视化的菜单
菜单应该实现可以让玩家自行选择是否游戏
在主源文件 test.c 中 代码如下:
void menu() //菜单界面
{
printf("***********************\n");
printf("**1.play***0.退出游戏**\n");
printf("***********************\n");
}
#include
int main()
{
int input = 0;//存储玩家的选择输入
printf(" 欢迎来到三子棋\n");
menu();
scanf("%d", input);//玩家进行选择
}
该处菜单代码可根据自行要求美化
效果展示
菜单完成后,考虑玩家的选择,若玩家选择 1. 进行游戏则进入游戏代码模块
若选择 2.则选择退出游戏结束进程。考虑到这个游戏至少需要完成一次,因此我们写一个do while();循环来完成。由于棋盘是一个三行三列的数组,因此我们创建一个二维数组棋盘。
在主源文件 test.c 中 代码如下:
void game()
{
//创建棋盘
char board[ROW][COL] = { 0 };//存储棋盘
}
int main()
{
int input = 0;
srand((unsigned)time(NULL));
do
{
printf(" 欢迎来到三子棋游戏\n");
menu();
printf("请选择:\n");
scanf("%d", &input);
switch (input)
{
case 1:
printf("三子棋游戏开始\n");
game();
break;
case 0:
printf("即将退出游戏\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
其中,合理运用玩家的输入input的值来控制了这个循环。可以达到玩家输错后为一个非0的数字也可以继续选择,同时结束一局游戏后,又可以选择继续重新开始。
代码展示
#define ROW 3 //三行
#define COL 3 //三列
在这使用自定义的行列有如下好处
1.提高代码可读性,防止与后续代码中出现的值歧义
2.方便后续创建一个新棋盘,更改自定义大小即可获得一个几行几列的数组
在创建了一个三行三列的数组后,这个棋盘里应该没有任何东西,因此须将棋盘进行初始化。
由于棋盘的初始化属于游戏模块,因此将其分装到 game.h 的头文件中进行声明,在 game.c 中进行实现(下述游戏模块的函数均如此分装不在赘述)
test.c 中代码如下:
void game()
{
//创建棋盘
char board[ROW][COL] = { 0 };//存储棋盘
//棋盘初始化
Initboard(board, ROW, COL);
}
game.h中进行函数体声明,代码如下:
#define ROW 3 //三行
#define COL 3 //三列
//初始化棋盘
void Initboard(char board[ROW][COL], int row, int col);
game.c中进行函数体的实现,代码如下:
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] = ' ';//表示这个二维数组中均初始化为空白
}
}
}
在进行初始化后,玩家无法看见棋盘,因此我们需要写一个函数将棋盘打印出来,让玩家观察棋盘
test.c中代码如下:
void game()
{
//创建棋盘
char board[ROW][COL] = { 0 };//存储棋盘
//棋盘初始化
Initboard(board, ROW, COL);
//打印初始化后的棋盘
print_board(board, ROW, COL);
}
game.h中代码如下:
#define ROW 3 //三行
#define COL 3 //三列
//初始化棋盘
void Initboard(char board[ROW][COL], int row, int col);
//打印棋盘
print_board(char board[ROW][COL], int row, int col);
在实现这个打印棋盘的函数时,进行逻辑分析
我们想要获得一下形式的棋盘
观察这个棋盘,我们可以将其分为:
1.数据行:玩家以及电脑落子后每一行中的数据
让每一组数据都落在中间那个空格上
3.每一列的分割行 _ _ _ 第三列没有分割行 _ _ _
game.c中代码如下:
print_board(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)//控制第三列没有分割行 _ _ _
{
int j = 0;
for (j = 0; j < col; j++)
printf("--- ");
}
printf("\n");//打印每一列数据后换行
}
}
由于test.c 以及game.c 中都需要引用一些头文件,为更方便和简介,将这些头文件都放入game.h中,test.c 以及 game.c只需 #include“game.h” 即可使用
//函数体申明
#include
#include
#include
玩家落子之后,应当判断是否落子正确(包括落子位置即坐标是否正确,以及落子位置是否被占用)在落子。由于下棋时一个人和电脑反复下直至分出胜负,因此为一个循环过程。
test.c 中代码如下:
while (1)
{
//玩家下棋
playmove(board, ROW, COL);
print_board(board, ROW, COL);//每一次下棋后打印出棋盘目以便观察
}
game.h 中代码如下:
//函数体申明
#include
#include
#include
#define ROW 3 //三行
#define COL 3 //三列
//初始化棋盘
void Initboard(char board[ROW][COL], int row, int col);
//打印棋盘
print_board(char board[ROW][COL], int row, int col);
//玩家下棋
playmove(char board[ROW][COL], int row, int col);
game.c 中代码如下:
playmove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1) //玩家下棋由于存在落子问题,因此采用循环直至这一次落子成功
{
printf("当前为玩家下棋\n");
printf("玩家请落子: ");
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");
}
}
效果展示:
test.c 中代码如下:
computermove(board, ROW, COL);
print_board(board, ROW, COL);
game.c 中代码如下:
computermove(char board[ROW][COL], int row, int col)
{
//电脑随机下棋,须产生一个随机坐标
int x = 0;
int y = 0;
printf("电脑落子:\n");
while (1)
{
//必须在循环里面,限制范围
x = rand() % row; //限定电脑随机生成的坐标范围
y = rand() % col; //rand需设置随机数的生成起点
if (board[x][y] == ' ')
{
board[x][y] = '#';//电脑落子成功用 # 表明
break;
}
}
}
game.h 中代码如下:
//函数体申明
#include
#include //rand的头文件
#include //随机数起点设置以时间为起点
#define ROW 3 //三行
#define COL 3 //三列
//初始化棋盘
void Initboard(char board[ROW][COL], int row, int col);
//打印棋盘
print_board(char board[ROW][COL], int row, int col);
//玩家下棋
playmove(char board[ROW][COL], int row, int col);
//电脑下棋
computermove(char board[ROW][COL], int row, int col);
电脑不够按照随机下棋不够灵活,可以将其完善,例如第一步先判断玩家是否下在中间位置(2 2),若不在电脑可将第一步先下在此处,会更有利,由于我们这是一个非常初阶的三子棋,就暂时不考虑这些,有兴趣的朋友们可以考虑一下。
在判断输赢时,做如下约定:
判断输赢情况
1.电脑赢 #三连--返回 1
2.玩家赢 *三连--返回 2
3.棋盘未满,且双方都未赢,接着下--返回 3
4.棋盘满了,双方未赢,判定平局--返回 4
test.c 中代码如下:
void game()
{
//创建棋盘
char board[ROW][COL] = { 0 };//存储棋盘
//棋盘初始化
Initboard(board,ROW,COL);
//打印初始化后的棋盘
print_board(board, ROW, COL);
int m = 0;
while (1)
{
//玩家下棋
playmove(board, ROW, COL);
print_board(board, ROW, COL);
m = Iswin(board, ROW, COL);
if (m != 3)//棋盘未满且双方都还未分出胜负下才有意义判断输赢
{
break; //还未分出胜负并且需要继续下棋时,及时跳出判断继续下棋
}
//判断输赢
//m = Iswin(board, ROW, COL);
//电脑下棋
computermove(board, ROW, COL);
print_board(board, ROW, COL);
m = Iswin(board, ROW, COL);
if (m !=3)//在不是平局的前提下
{
break; //还未分出胜负并且需要继续下棋时,及时跳出判断继续下棋
}
}
if (m == 1)
printf("电脑获胜\n");
else if (m == 2)
printf("玩家获胜!\n");
else
printf("平局\n");
}
game.h 中代码如下:
//函数体申明
#include
#include
#include
#define ROW 3 //三行
#define COL 3 //三列
//初始化棋盘
void Initboard(char board[ROW][COL], int row, int col);
//打印棋盘
print_board(char board[ROW][COL], int row, int col);
//玩家下棋
playmove(char board[ROW][COL], int row, int col);
//电脑下棋
computermove(char board[ROW][COL], int row, int col);
//判断输赢
int Iswin(char board[ROW][COL], int row, int col);
game.c 中代码如下:
int IsFull(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ') //说明还有空间可以继续下棋
return 0; //一旦找到有一个空格,说明棋盘未满,立即返回
}
}
return 1; //前面循环结束都未找到空格,说明棋盘满了
}
//从此处开始阅读
int Iswin(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] != ' ')
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] == '*')
return 2;
else
return 1;
}
}
//竖三列
//前提不为初始化的0三子
int j = 0;
for (j = 0; j < col; j++)
{
if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ' )
{
if(board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] =='*')
return 2;
else
return 1;
}
}
//对角线判断
//前提不为初始化0的三子
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
{
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] == '*')
return 2;
else
return 1;
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
{
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] == '*')
return 2;
else
return 1;
}
//若前面均为分出输赢,此时则有可能为平局或者继续
//若平局 此时棋盘应该已经下满,没有空位可以继续下
if (IsFull(board, row, col) ) //返回1则条件成立进入
{
return 4;
}
//前面没有人赢,棋盘也没有满,此时应该处于需要继续下棋
return 3;
}
以上判断输赢时,可考虑将电脑胜利以 ‘ #’返回,玩家胜利以‘ *’返回,可以省去在判断时该处是电脑三子还是玩家三子,即可将下面代码替换:
替换为:
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] != ' ')
{
/*if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] == '*')
return 2;
else
return 1;*/
return board[i][1];//这个三子后期中一个子返回的值无论是‘#’还是‘*’,均可以直接判断输赢
}
}
在上述判断输赢时,采取的是最原始的方法,将行列的三个数组坐标列出来进行判断,此处优化空间很大,可以试着去优化它。
最后,无论是电脑还是玩家赢了等情况,我们均需要让玩家观察最后的棋盘情况,因此我们在这还需要给判断输赢后的结果打印出来
test.c 中加入这个之前的打印函数即可:
void game()
{
//创建棋盘
char board[ROW][COL] = { 0 };//存储棋盘
//棋盘初始化
Initboard(board,ROW,COL);
//打印初始化后的棋盘
print_board(board, ROW, COL);
int m = 0;
while (1)
{
//玩家下棋
playmove(board, ROW, COL);
print_board(board, ROW, COL);
m = Iswin(board, ROW, COL);
if (m != 3)//棋盘未满且双方都还未分出胜负下才有意义判断输赢
{
break; //还未分出胜负并且需要继续下棋时,及时跳出判断继续下棋
}
//判断输赢
//m = Iswin(board, ROW, COL);
//电脑下棋
computermove(board, ROW, COL);
print_board(board, ROW, COL);
m = Iswin(board, ROW, COL);
if (m !=3)//棋盘未满且双方都还未分出胜负下才有意义判断输赢
{
break; //还未分出胜负并且需要继续下棋时,及时跳出判断继续下棋
}
}
if (m == 1)
printf("电脑获胜\n");
else if (m == 2)
printf("玩家获胜!\n");
else
printf("平局\n");
//游戏结束后,打印出棋盘让玩家观察输赢情况
print_board(board, ROW, COL);
}