重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
相信大家或多或少都听说过五子棋,三子棋也是类似,即达成每行或每列中有三颗连续棋子,亦或是在对角线上有三颗连续的棋子即为胜利。今天,我们就来实现一下。
目前创新互联建站已为1000多家的企业提供了网站建设、域名、虚拟主机、成都网站托管、企业网站设计、安岳网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。目录
一、文件的准备
二、功能的实现
1.菜单与用户的选择
2.游戏的函数
3.棋盘的初始化
4.打印棋盘
5.下棋->玩家下棋
6.下棋->电脑下棋
7.判断结果
a.当一行中三个相等时
b.当一列中三个相等时
c.当对角线上相等时
d.判断平局
8.程序补全
为了后期代码的封装,我们将测试代码,函数代码和函数的声明分开存放到不同的文件中。如下:
test.c —— 用于存放测试游戏功能实现的代码
game.c —— 用于存放游戏相关的函数的具体内容
game.h —— 用于存放游戏中功能函数的声明以及相关类型的定义
为了便于区分,将在后文的代码演示处首行以注释方式写明所属文件。
二、功能的实现 1.菜单与用户的选择市面上的游戏的选项有很多,我们只完成最基础的玩游戏和退出。
我们创建无返回值的menu作为菜单,由于只用来测试,写在test.c中。
// test.c
void menu()
{
printf("****************\n");
printf("**** 1.play ****\n");
printf("**** 0.exit ***\n");
printf("****************\n");
}
为了使用户有较好的游戏体验,我们使用循环来判断,只有当用户选择退出游戏才会退出,选择的不是玩游戏或者退出就提示错误并重新输入。
// test.c
int main()
{
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
Game();
break;
case 0:
printf(*"退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
2.游戏的函数当用户输入1选择进入游戏后,就需要进入具体的游戏程序中,我们在play的选项后创建Game函数用来存放游戏代码。Game的使用在上文的代码段中已有体现,接下来只讲声明与定义。
我们将Game函数也存放在test.c中,基础代码如下:
// test.c
void Game()
{
char board[ROW][COL] = { 0 };
InitBoard(board, ROW, COL);
PrintBoard(board, ROW, COL);
...
}
下棋,必不可少的就是棋盘。
我们创建一个二维数组来存放棋盘,棋盘的大小就设定为3X3,在二维数组中即为每行3个元素,共3行。
为了方便后期代码的维护,我们使用#define自定义行和列,并存放在我们编写的game.h中,方便调用。
// game.h
#include#include#include#include#define ROW 3
#define COL3
为了方便,已经后文需要引用的头文件一起添加,后文有关内容将仅做提示。
特别注意:由于自定义类型在别的文件中需要引用,也为了减少代码内容,另外两个.c需要引用头文件game.h,如下:
// test.c game.c
#include "game.h"
创建字符类型二维数组board作为棋盘,行和列分别使用已定义的ROW和COL,Game函数中已有体现。
3.棋盘的初始化在开始下棋之前,需要先清理棋盘,程序中也是一样,我们需要对棋盘进行初始化,创建InitBoard用于实现初始化。
由于自此之后绝大部分函数都涉及到具体的游戏内功能,故函数的使用均在Game函数中,有使用方式特别的会单独指出;函数的声明存放到头文件game.h中;函数的具体内容存放到game.c中,在此仅作演示,之后的声明和定义将省略。
声明:
// game.h
void InitBoard(char board[ROW][COL], int row, int col);
定义:
// game.c
void InitBoard(char board[ROW][COL], int row, int col)
{
;
}
具体的内容在之后会有体现。
我们将棋盘上的落棋点,也就是二维数组中的元素初始化为空格字符,即' ',可以使用嵌套的for循环来实现,具体如下:
// 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] = ' ';
}
}
}
4.打印棋盘在下棋之前,必须要能看到棋盘的内容,所以在初始化后就需要打印出来,方便观察。
都初始化为空格,直接打印肯定不方便观察,而且下棋也需要使用其他符号作为每个格子的分界,我们使用“-”和”|“作为分界,且分隔符与单元格并不是一一对应,而是只在两个各自之间才需要分割,所以循环的条件就是行数/列数 减去1,这里类似于数组元素和下标的关系。
代码如下:
// contact.c
void PrintBoard(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++)
{
printf(" %c ", board[i][j]);
if(j
打印出来的棋盘样式如图:
5.下棋->玩家下棋我们创建PlayMove函数用作玩家下棋,并将玩家可以出的棋子定义为'*':
给出玩家下棋的提示,使用printf打印。
在二维数组中行和列的下标均是从0开始,但是玩家并不一定知道下标这个概念,进而输入为从1开始的任意数值,我们就需要判断输入玩家输入的坐标是否在现有的棋盘内,使用if判断,由于有多个条件,我们使用C语言逻辑运算符中的与&&连接判断条件。
程序设计为人机对战,当用户输入的坐标是处于棋盘上的时候,我们还需要对所输入的坐标是否已有用户或者电脑使用过此坐标。若为我们初始化的' '就是没有被使用过,就将'*'置入该坐标中。代码如下:
// game.c
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家走:>\n");
while (1)
{
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");
}
}
}
6.下棋->电脑下棋我们创建ComMove函数来表示电脑下棋,使用'#'作为电脑所下的棋子:
电脑下棋无法像玩家一样输入自己要下的,我们需要使用随机数来获取行和列。
C语言中,一般使用rand()函数来生成随机数,但随机数的范围太大,且多次连续运行时产生的结果相近或为统一结果,不适用于直接生成想要的随机数。
我们使用srand()函数对随机范围进行指定,在srand中则使用time函数获取每时每刻都在变化的时间(精确到秒)。
用获取得到的随机数在求与行/列的模,求出的随机数即在二维数组的范围内,只需判断所随机得到的坐标是否有棋子即可,为空表示可以将棋子落下,有内容则重新进行随机。
关于time函数仅作使用,不做过多描述,可自行查阅。
srand即为以下内容:
// test.c
srand((unsigned int)time(NULL));
rand函数需要引用头文件
函数内容如下:
// game.c
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑走:>\n");
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
7.判断结果是比赛就有胜利、失败以及平局。
三子棋中当一方棋子连成三点一线(包括对角线)时,即为胜利,另一方失败;
当棋盘下满仍无三颗同一方棋子连线时,即为平局
我们创建IsFull函数用来判断期盼是否已满,并返回1或0作为满或未满。
只需要对二维数组进行遍历,逐个判断是否为' ',即可确定棋盘是否已满。
如下:
// game.c
int IsFull(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;
}
然后就是判断棋盘上的棋子是否有一方满足胜利条件,创建IsWin函数来进行判断
a.当一行中三个相等时使用for循环判断当棋盘中行数不变时,每一列的格子内的棋子都相同且不为空时,就返回相同的那种棋子;
b.当一列中三个相等时与上一种判定方法类似,不同之处在于列不变,每一行相同且不为空,返回相同的棋子
c.当对角线上相等时对角线相同有两种情况,一种是从左上到右下,即[0][0],[1][1],[2][2]相同且不为空;另一种是从右上到左下,即[0][2],[1][1],[2][0]相同且不为空。两种情况满足一种即返回相同的棋子。这里同样使用逻辑或&&对判断条件进行连接
d.判断平局在创建判断棋盘是否已满的函数时,我们定义为了int类型,设定了返回值为0或1,在IsWin中,我们创建变量ret对IsFull的结果进行判断,若为1(表示棋盘已满)就返回'D',表示平局;若为0(棋盘未满)就返回'N'
函数代码如下:
// game.c
char 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] != ' ')
{
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];
}
int ret = IsFull(board, row, col);
if (ret == 1)
{
return 'D';
}
return 'N';
}
8.程序补全函数的功能和定义已经完成,接下来回到测试文件对逻辑进行补全。
下棋需要多次的玩家和电脑进行落子,故使用while循环进行轮流下棋,逐步显示当前棋盘的内容并判断是否满足胜利或者平局,有一方胜利或者棋盘已满便退出循环,不再落子。
然后就可以对返回值进行判断:
如果返回的是表示玩家的棋子'*',就输出玩家胜利并打印棋盘;
如果返回的是表示电脑的棋子'#',就打印电脑胜利并打印棋盘;
若返回其他内容则表示棋盘已满,打印平局并打印棋盘。
代码如下:
// test.c
void Game()
{
// 一个存放棋盘的3x3的二维数组
char board[ROW][COL] = { 0 };
// 初始化棋盘
InitBoard(board, ROW, COL);
// 打印棋盘
PrintBoard(board, ROW, COL);
char ret = 0;
while (1)
{
//玩家下棋
PlayerMove(board, ROW, COL);
PrintBoard(board, ROW, COL);
//判断玩家是否赢得游戏
ret = IsWin(board, ROW, COL);
if (ret != 'N')
break;
//电脑下棋
ComMove(board, ROW, COL);
PrintBoard(board, ROW, COL);
//判断电脑是否赢得游戏
ret = IsWin(board, ROW, COL);
if (ret != 'N')
break;
}
if (ret == '*')
{
printf("玩家赢了\n");
}
else if (ret == '#')
{
printf("电脑赢了\n");
}
else
{
printf("平局\n");
}
PrintBoard(board, ROW, COL);
}
至此,简单的三子棋游戏已经完成,具体的代码文件已上传,可点击下方链接进行查看:
GitCode-shiwuqing-game1
Gitee-shiwuqing-game1
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧