1.功能构想
①.初始菜单功能
②.游戏功能
③.玩家端功能
2.功能实现
根据我上一篇博文链接: 玩了个游戏1猜数字。作为一名玩家我们打开程序后肯定首先要看到一个菜单或者叫开始界面,我们的功能设置的比较简易,①进入游戏②退出游戏。所以,我们可以用printf功能来实现我们的菜单展示
void menu()
{
printf("********************\n");
printf("****** 1.play ******\n");
printf("****** 0.exit ******\n");
printf("********************\n");
}
菜单不需要返回值,所以我们定性为void,同时,为了不重复引用头文件,我们这边将自定义一个game.h的头文件,“.h”后缀就是头文件的意思。可以联想英文单词head。
以下是目前game.h文件内的代码:
#include<stdio.h>
extern void menu();
之后我们需要应用一些头文件,只需要在game.h里面引用,并且在别的.c文件里引用game.h文件即可。
现在我们回到user.c的本体来完成用户端结构,为了选择性的方便,我们采用switch case语句,为了使用switch case语句的功能,我们要让用户端做选择,这里可以采用scanf。
int us = 0;
printf("press button>:");
scanf("%d", &us);
switch (us)
{
case 1:
{
game();
break;
}
case 0:
{
printf("退出游戏\n");
break;
}
}
玩家可能想多次重复游玩,我们考虑到用循环体系,同时,为了保障无论怎样,程序总会进行一次,所以,我们采用do while语句,达到目的。
为了使玩家的输入,可以作为do while 语句判断的条件,所以,我们要将us—>玩家的输入,定义到循环体外,作为全局变量,从而可以作为判断条件。
#include"game.h"
int main()
{
int us = 0;
do
{
menu();
printf("press button>:");
scanf("%d", &us);
switch (us)
{
case 1:
{
game();
break;
}
case 0:
{
printf("退出游戏\n");
break;
}
}
} while (us);
}
根据我们目前需要的功能来说,一定要记得每一个case语句,都需要加入break来终止循环,case如果不加入break,则会从上到下的执行,导致程序混乱。
现在,我们可以来实现我们的game()主体,game仍旧不需要返回值,此时,我们可以定义为:
void game()
{
}
重中之重!2.2功能实现-------->游戏主体
首先,作为三子棋,我们需要,3*3的棋盘。
那么,我们先构想一下这个棋盘
构想完之后,身为程序员,那么,我们就应该让电脑,做我们想要的东西。
①给你的展示棋盘的功能想个名字,然后引用到game()里面。
void game()
{
displaychessboard();
}
下面,我们根据需求来创造功能
void displaychessboard()
{
int row = 0;
int col = 0;
for (row = 0;row < ROW ; row++)
{
printf(" | | \n");
if (row != 2) //这个是为了避免打印最后的分隔行
{
printf("---|---|---\n");
}
}
}
来吧!展示!!
基本的雏形已经有了,但是我们又想要下棋,这就需要我们实现一个可以通过输入来改变的功能。单一个printf,肯定是不够用的。联想到我们所知的函数,用来储存字符的,并且还要是二维的,此时,我们联想到arr[ ][ ] 二维数组。
此时,我们可以创建我们的二维数组
①给你的二维数组想一个名字、下一个定义。
②把这个数组初始化,全部存放空格。
存放空格是为了初始化这个棋盘,而且在每次进入游戏的时候,我们都需要初始化棋盘,所以,这个时候,我们再创建一个初始化棋盘的函数名叫initchessboard(),我们也不需要它返回什么东西,所以,我们这样定义它:
void initchessboard(char chess[ROW][COL])
{
int col = 0;
int row = 0;
for (row = 0;row < ROW;row++)
{
for (col = 0;col < COL;col++)
{
chess[row][col] = ' ';//这是一个空格
}
}
}
这个就是用来初始化我们的二维数组的 ,也就是初始化下棋位置的。
接下来,我们为了能够将这个下棋位置的变量,能够在棋盘上展示,所以,刚刚我们设立的displaychessboard,需要修改,首先,我们综合如下的思路,将整个displaychessboard功能,达到一个完全支持自定义棋盘大小的一个功能,先考虑行,再考虑列。
void displaychessboard(char chess[ROW][COL])
{
int row = 0;
int col = 0;
for (row = 0;row < ROW ; row++)
{
for (col = 0;col < COL;col++)
{
printf(" %c ", chess[row][col]);
if (col != COL - 1)//这是为了不打印最后的|
{
printf("|");
}
}
printf("\n");
if (row != ROW - 1)//这个是为了不打印最后的分隔行
{
for (col = 0;col < COL;col++)
{
printf("---");
if (col != COL - 1)//这是为了不打印最后的|
printf("|");
}
printf("\n");
}
}
}
来吧!展示!!!!!(为了强调我以COL和ROW定义是有意义的,这时候,我们将define COL、ROW的值改成5来看效果。)
效果很成功!!!!!!然后我们再把COL 、ROW的值改回来。(毕竟这还是个三子棋)
棋盘都搞好了,这时候开始搞下棋,下棋是玩家和电脑的对弈,这个时候,我们需要创建两个函数,分别实现电脑和玩家的下棋功能,我们定义两个函数名,这两个函数,同样不需要返回值,那么先定义为void computermove( ) 、void playermove( ) ,我们先定玩家先走,那么game( )主体如下:
void game()
{
char chess[ROW][COL];
initchessboard(chess);
displaychessboard(chess);
playermove(chess);
computermove(chess);
}
走完还不行,我们必须要看到玩家走完之后的效果,所以,我们要展示 这个棋盘。game主体优化后如下:
void game()
{
char chess[ROW][COL];
initchessboard(chess);
displaychessboard(chess);
while (1)
{
playermove(chess);
displaychessboard(chess);
computermove(chess);
displaychessboard(chess);
}
}
为什么要while?因为这是一个循环,下三子棋不是一招制胜的。
体系想好了,接下来先是实现玩家下棋
首先,要传参,把数组传给playermove才能修改数组的内容。接下来,要接收一个坐标计算机才能知道要改二维数组的哪一个位置的 ’ 空格 ',所以我们选用scanf来录入玩家输入,接下来,我们要考虑到视觉效果 ,所以我们要提示玩家输入,我们采用printf来打印提示信息。同时,要考虑到,玩家规规矩矩的输入还好,如果玩家输入错了,那么很有可能导致我们不完善的函数模块崩溃,这个时候,我们不仅要考虑玩家输入的坐标是否合规,同时,也要考虑这个坐标是否被占用,当然,需要注意的一点是,玩家作为用户,是不会考虑二维数组的起始下标是0的,所以,我们要人为的-1,来使用户输入的和计算机执行的是一个效果。同时,在输入错误的条件下,这是一个循环体系,但输入正确之后,我们使循环体结束循环,代码如下:
void playermove(char chess[ROW][COL])
{
int x = 0;
int y = 0;
while (1)
{
printf("玩家下棋,请输入坐标下棋>:");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{
if (chess[x - 1][y - 1] == ' ')
{
chess[x - 1][y - 1] = '@';
break;
}
else
{
printf("坐标已被占用,请重新下棋");
}
}
else
{
printf("坐标输入不合规,请重新输入\n");
}
}
}
来吧!展示!!!!!!!!!!!!!
此时,我们发现,打印的效果是没问题的,但是,不太好观赏,所以,我们放一个\n来分隔玩家和电脑的displaychessboard,game主体更改如下:
void game()
{
char chess[ROW][COL];
initchessboard(chess);
displaychessboard(chess);
while (1)
{
playermove(chess);
displaychessboard(chess);
printf("\n");
computermove(chess);
displaychessboard(chess);
}
}
来吧!展示!!!!
这样就好看很多。
————————————————————————————————————————————————————————————————————————————————————————
接下来,我们要搞电脑下棋
模仿着玩家下棋来写。但是,电脑会乖很多,不会违反我们的规定,所以写起来很简单。但有一个重点,就是之前在玩了个游戏1猜数字.里面提到过的生成随机数。
内容回顾:
生成随机数,要用到rand,而用到rand之前,我们需要用到srand来生成随机数,srand里又需要一个变化的种子来使其生成的内容随机,此时,我们根据计算机的时间是不断变化的,来引入一个时间戳的观念,这里就需要time函数,此时我们知道,要用到time,就要知道它的标准格式,time_t time( time_t *timer ),在vs里,它的类型time_t是time64_t
如图又可知它的类型是int,而srand的格式为void srand( unsigned int seed ),所以,我们可以考虑,把time函数强制类型转换成unsigned int,同时,time里面需要一个指针,我们用空指针即可。
种植随机数生成器如下
srand((unsigned int)time(NULL));
此时要注意,为了尽可能的随机,我们的种植生成器的行为,要放在user的main函数里,种植一次即可。现在的user主体如下:
#include"game.h"
int main()
{
int us = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("press button>:");
scanf("%d", &us);
switch (us)
{
case 1:
{
game();
break;
}
case 0:
{
printf("退出游戏\n");
break;
}
}
} while (us);
}
同时,我们要注意,调用time的时候,需要引头文件time.h,我直接引在game.h头文件里,目前 game.h内代码如下:
#include<stdio.h>
#include<time.h>
void menu();
void game();
#define ROW 3 //行
#define COL 3 //列
接下来,我们在computermove里面,使用srand生成的随机数,放入到做表里面,代码如下:
void computermove(char chess[ROW][COL])
{
int x = 0;
int y = 0;
while (1)
{
printf("电脑下棋>:\n");
x = rand(time) % 3;
y = rand(time) % 3;
if (chess[x][y] == ' ')
{
chess[x][y] = '*';
break;
}
}
}
%3可以使其生成0-2的数字。
来吧!!!展示!!!!!!!
此时,我个人不喜欢程序打印老多,所以,我们调用清屏的功能
game()主体如下:
void game()
{
char chess[ROW][COL];
initchessboard(chess);
displaychessboard(chess);
while (1)
{
playermove(chess);
displaychessboard(chess);
printf("\n");
computermove(chess);
displaychessboard(chess);
system("cls");
displaychessboard(chess);
}
}
来吧!!!展示!!!!!!!!!!!!!!!
看吧!干干净净!!!!功能正常!!!!
接下来,这么一直下也是需要分输赢的,所以,我们要每下一个字都来判断一下,有没有人赢了,所以我们创建一个函数来判断胜负,我起名叫judgewinner
我们可能会需要winner返回一个东西来判断输赢,当然,下棋的结局不只是输赢,还有平局。下面,让我们实现这个函数。
char judgewinner(char chess[ROW][COL])
{
int flag = 0;
int row = 0;
int col = 0;
for (row = 0;row < ROW;row++)
{
if (chess[row][0] == chess[row][1] && chess[row][1] == chess[row][2] && chess[0][0] != ' ')
{
if (chess[row][0] == '@')
return '@';
else if (chess[row][0] == '*')
return '*';
}
}
for (col = 0;col < COL;col++)
{
if (chess[0][col] == chess[1][col] && chess[2][col] == chess[1][col] && chess[1][col] != ' ')
{
if (chess[0][col] == '@')
return '@';
else if (chess[0][col] == '*')
return '*';
}
}
if (chess[0][0] == chess[1][1] && chess[2][2] == chess[1][1] && chess[2][2] != ' ' || chess[0][2] == chess[1][1] && chess[1][1] == chess[2][0] && chess[2][0] != ' ')
{
if (chess[0][0] == '@')
return '@';
else if (chess[0][0] == '*')
return '*';
}
//判断棋盘有没有满
for (row = 0;row < ROW;row++)
{
for (col = 0;col < COL;col++)
{
if (chess[row][col] == ' ')
{
flag = 1;
break;
}
}
if (flag == 1)
{
return'c';
}
}
if (flag == 0)
{
return 'q';
}
}
这里注意一点,别写错了就行了。
然后函数主体这边,我们每走一步都要判断棋局状况,并且要保持屏幕整洁,所以 我们这么构造:
void game()
{
char ret;
char chess[ROW][COL];
initchessboard(chess);
displaychessboard(chess);
while (1)
{
playermove(chess);
displaychessboard(chess);
printf("\n");
ret = judgewinner(chess);
if (ret != 'c')
break;
computermove(chess);
displaychessboard(chess);
ret = judgewinner(chess);
if (ret != 'c')
break;
system("cls");
displaychessboard(chess);
}
system("cls");
if (ret == '@')
printf("恭喜你!!!赢啦!\n");
else if (ret == '*')
printf("电脑赢了,再接再厉\n");
else if (ret == 'q')
printf("平局,再来一次吧\n");
displaychessboard(chess);
}
终于结束了!!来吧!!最终展示!!!
其他结果我就不试了,给大家自己试一试,好了,现在三子棋已经完结了!