游戏介绍以及分析
介绍:这个游戏是3*3个格子上实现的,由双方每次在一个格子上画一个图案,当有三个相同的图案连成一条线时该图案的一方就获得胜利,还有一种情况就是9个格子都下完了但是还没有三个相同的图案连成一条线,此时就是双方平局了,大家在小时候都应该玩过这个小游戏吧。
如:
分析:
1.进入游戏第一步就是打造界面吧
2.这小游戏要实现3 * 3棋盘,那是不是就要用到一个二维数组啊
3.我们要通过二维数组来来制作棋盘时,要将二维数组初始化为空格,那样就不会影响棋盘的观感了,同时再通过‘ | ’,和‘- - - -’将棋盘中的每个格子分开,这样棋盘就打造成功了。
4.打造完棋盘之后,就是下棋了,我们可以输入坐标来改变二维数组的元素,通过这样来下棋
5.我们要设置双人和电脑模式
我们先分析双人模式:假设小帅和小美在玩,那么它们两个人其中一个先下,再到另一个人,一直下去直到将3*3个格子都走完或者一方胜利,那么在走棋的过程我们是不是还要设置一个函数来判断是否胜利。如果没人胜利的话棋盘的格子清零,我们通过判断这个就能够知道是否平局了
电脑模式:想要电脑走棋的话我们就要给它设置2个随机数(具体设置随机数后面有详解)来代替坐标。这样你走一步电脑再走一步,这样就实现了和电脑对战了。
好,就分析到这里了,接下来我们去实现游戏吧
游戏实现
1.我们通过多文件将函数分为三个部分,第一主文件就是放主函数,一些界面,第二个放一些主要函数实现的模块,第三就是放一些头文件、定义之类的
2。就是建议大家做的时候实现一步就去验证是否有错误,再实现下一个模块,下面我就是分模块完成的
1.游戏界面
我们这里设置了一开始进入游戏的界面和选择模式的界面
进退游戏界面
一个游戏是不是应该有退出和开始的界面啊,那么我们现在就开始实现,这个游戏界面制作非常简单粗暴,就是过我们的打印函数printf将它直接打印出来啦
代码展示:
void jiemian(void) {//界面1
printf("*******************************************************\n");
printf("*******************************************************\n");
printf("******** 1 - 开始游戏 ********\n");
printf("******** 0 - 退出游戏 ********\n");
printf("*******************************************************\n");
printf("*******************************************************\n");
}
是不是非常朴实无华啊
我们打印结果看看
模式选择界面
这个模式选择界面和进退游戏界面是一样的道理,通过我们朴实无华的printf打印出来
代码展示:
void xuanze(void) {//界面2
printf("*******************************************************\n");
printf("*******************************************************\n");
printf("******** 2 - 双人对战 ********\n");
printf("******** 3 - 电脑玩家 ********\n");
printf("*******************************************************\n");
printf("*******************************************************\n");
}
打印结果:
到这里我们的界面就搞定啦,是不是觉得简简单单呢,接下来就是初始化了
2.初始化
我这里是设置了一个4*4的棋盘,因为那样我们就可以使坐标都是1~3了,并且这样设置也有利于后面棋盘的打印,因为是二维数组,所以我们这里直接用一个循环嵌套来给二维数组赋值
代码展示:
void chushihua(char arr[kuan][chang], int x, int y) {//用循环将x,y是1~3的数组初始化为空格
for (int i = 1; i < x; i++) {
for (int j = 1; j < y; j++) {
arr[i][j] = ' ';
}
}
}
因为我们要控制坐标在1~3,所以就从下标1开始初始化了
接下来就是我们的打印了
3. 打印棋盘
我们打印的棋盘尽量要美观,规矩是吧,我们尽量也将坐标位置也打印出来,如:
但是我们做不到像上图那样,输入一条横线这些,那我们尽量做的相似,如:
这图有点抽象哈,那这样是不是就可以实现了捏
我们在通过代码分析下:
void dayin(char arr[kuan][chang], int x, int y) {
for (int i = 0; i < x; i++) {
printf("%-2d", i);//打印时,将x的坐标打印
for (int j = 1; j < y; j++) {
if (i == 0) {//第一行将y坐标打印
printf("%-2d", j);
}
else {//
printf("%c", arr[i][j]);//打印空格
if(j<y-1)//控制‘|’,将每个格子隔开,保证只打印前两个
printf("|");
}
}
if (i != 0 && i != x - 1) {//打印下面的“-----”将每一行隔开,控制只打印中间两行
printf("\n");//换行再打印
printf(" - - - - ");
}
printf("\n");//在换行
}
}
先通过 i=0 将列的坐标打印出来,在通过每一次大循环就打印一个 i,这样就将行打印出来了,我们每列只需要两个 ‘ | ’所以当 j=3时就没必要打印了,打印的话会影响美观了,行也是如此,只要两个“- - - - ”即可 当i=3时也没必要打印了,这个代码要注意的点还有就是换行符的位置,一定要放到合适的位置
接下来看看结果:
按照我们的想法打印出来了
再接下来就是游戏的过程了
4.游戏过程
我们这里的思想就是一方下完就到另一方,直到一方胜利,或者棋盘的格子数被下满平局,
在这里补充一个库函数,system(“cls”),头文件为:#include<windows.h>//清屏函数头文件,它的作用是清除屏幕内容,我们在游戏的过程中将该函数放到合适的位置是可以提高游戏体验哦,如每走完一步就将上一次的清除,将这一次的打印出来,直到下一次清除
双人模式
我们先设置一个控制变量te=3*3,在往下就是通过while循环来实现,当te==0或者有一方胜利就打破这个循环,第一步就是A方下然后就进行判断(下面会解释判断代码),再到B方下然后就进行判断,每下一次te就减 1 。
代码(已经有详细的注释了哦):
void shuangren(char arr[kuan][chang]) {//双人模式
int te = (kuan-1) * (chang-1);//求全部格子数--9
while (te != 0) {//循环te=0时平局
int x = 0,y=0;
printf("A(+):");//A先下
scanf("%d %d", &x, &y);
system("cls");
arr[x][y] = '+';//‘+’代表标志
int r = panduan(arr);//进行判断
if (r == 1) {//返回1 则成功,打破
printf("恭喜A赢了\n");
dayin(arr, kuan, chang);//打印最终的棋盘看看
break;
}
else {
dayin(arr, kuan, chang);//打印目前的棋盘继续游戏
te--;//te减1
}
if (te >0) {//当te==0时说明棋盘已经满了,停下
printf("B(-):");
scanf("%d %d", &x, &y);
system("cls");
arr[x][y] = '-';
r = panduan(arr);
if (r == 1) {//返回1 则成功,打破
printf("恭喜B赢了\n");
dayin(arr, kuan, chang);//打印最终的棋盘看看
break;
}
else {
dayin(arr, kuan, chang);
te--;
}
}
}
if(te==0)//当te==0说明每份出胜负
printf("平局了\n");
jiemian();//打印游戏界面,看是否下把游戏
}
这里A>>‘+’,B>>’ - ’
电脑模式
在说电脑模式之前先来了解一了电脑如何设置随机数
**我们这里的范围1-100;**实现随机数字需要用到3个库函数:
C语⾔提供了⼀个函数叫 rand,这函数是可以⽣成随机数的,函数原型如下所⽰:
1 int rand (void);
rand函数会返回⼀个伪随机数,这个随机数的范围是在0~RAND_MAX之间,这个RAND_MAX的⼤⼩是
依赖编译器上实现的,但是⼤部分编译器上是32767。
rand函数的使⽤需要包含⼀个头⽂件是:stdlib.h
那我们就测试⼀下rand函数,这⾥多调⽤⼏次,产⽣5个随机数:
**#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
return 0;**
}
这里运行2次结果都一样(自己可以去试试,无论多少次都是一样的结果)
我们可以看到虽然⼀次运⾏中产⽣的5个数字是相对随机的,但是下⼀次运⾏程序⽣成的结果和上⼀次
⼀模⼀样,这就说明有点问题。
如果再深⼊了解⼀下,我们就不难发现,其实rand函数⽣成的随机数是伪随机的,伪随机数不是真正的随机数,是通过某种算法⽣成的随机数。真正的随机数的是⽆法预测下⼀个值是多少的。⽽rand函数是对⼀个叫**“种⼦**”的基准值进⾏运算⽣成的随机数。
之所以前⾯每次运⾏程序产⽣的随机数序列是⼀样的,那是因为rand函数⽣成随机数的默认种⼦是1。
如果要⽣成不同的随机数,就要让种⼦是变化的。
1.2 srand
C语⾔中⼜提供了⼀个函数叫 srand,⽤来初始化随机数的⽣成器的,srand的原型如下:
1 void srand (unsigned int seed);
程序中在调⽤ rand 函数之前先调⽤ srand 函数,通过 srand 函数的参数seed来设置rand函数⽣成随机数的时候的种⼦,只要种⼦在变化,每次⽣成的随机数序列也就变化起来了。
那也就是说给srand的种⼦是如果是随机的,rand就能⽣成随机数;在⽣成随机数的时候⼜需要⼀个随机数,这就⽭盾了。
1.3 time
在程序中我们⼀般是使⽤程序运⾏的时间作为种⼦的,因为时间时刻在发⽣变化的。
在C语⾔中有⼀个函数叫 time ,就可以获得这个时间,time函数原型如下:
1 time_t time (time_t timer);*
time 函数会返回当前的⽇历时间,其实返回的是1970年1⽉1⽇0时0分0秒到现在程序运⾏时间之间的
差值,单位是秒。返回的类型是time_t类型的,time_t 类型本质上其实就是32位或者64位的整型类
型。
time函数的参数 timer 如果是⾮NULL的指针的话,函数也会将这个返回的差值放在timer指向的内存
中带回去。
如果 timer 是NULL,就只返回这个时间的差值。time函数返回的这个时间差也被叫做:时间戳。
time函数的时候需要包含头⽂件:time.h
如果只是让time函数返回时间戳,我们就可以这样写:
1 time(NULL);//调⽤time函数返回时间戳,这⾥没有接收返回值
那我们就可以让⽣成随机数的代码改写成如下:
**#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
`
``
//使⽤time函数的返回值设置种⼦
//因为srand的参数是unsigned int类型,我们将time函数的返回值强制类型转换
srand((unsigned int)time(NULL));
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
return 0;**
多运⾏⼏次看看,每次的运⾏就有差异了。
第⼀次运⾏的结果
第⼆次运⾏的结果
(注:截图只是当时程序运⾏的结果,你的运⾏结果不⼀定和这个⼀样)
srand函数是不需要频繁调⽤的,⼀次运⾏的程序中调⽤⼀次就够了。
我们这里需要的范围是1~3,思考一下,任何数除以3的余数的范围是不是0 ~2啊,那我们在这个基础上加 1,不就实现了嘛。
电脑模式的分析:过程和双人模式的一样,但是电脑下棋是无意识的,通过我们上面的设置随机数方法来输入坐标的话可能会导致同一个位置下两次甚至更多,那么我们就要在双人模式下改进,如果是电脑下棋的话就进行一个判断,如果是空格的话就可以下,不是的话重新设置随机数,
代码展示:
void diannao(char arr[kuan][chang]) {//电脑对战
int te = (kuan - 1) * (chang - 1);//求全部格子数--9
while (te != 0) {//我先下,过程和双人的一样
int x = 0, y = 0;
printf("A(+):");
scanf("%d %d", &x, &y);
system("cls");
arr[x][y] = '+';
int r = panduan(arr);
if (r == 1) {
printf("恭喜A赢了\n");
dayin(arr, kuan, chang);
break;
}
else {
dayin(arr, kuan, chang);
te--;
}
if (te > 0) {
printf("B(-):");
do {//控制电脑在空格的地方下棋
x = rand() % 3 + 1;//随机数(%3+1控制这个数在1~3之间)
y = rand() % 3 + 1;
} while (arr[x][y] != ' ');//控制下的地方是空格
system("cls");//下面的过程就和上面的一样了
arr[x][y] = '-';
r = panduan(arr);
if (r == 1) {
printf("恭喜B赢了\n");
dayin(arr, kuan, chang);
break;
}
else {
dayin(arr, kuan, chang);
te--;
}
}
}
if (te == 0)
printf("平局了\n");
jiemian();
}
好,接下来就是最后一步了,如何判断是否胜利捏
5.判断输赢或者平局
我们观察棋盘可以分为三类就是 行,列 ,对角。
行的话就是用循环来控制行,再将列(1,2,3) 列举出来看看是否相等且不为空格
列的话就是用循环来控制行,再将行(1,2,3) 列举出来看看是否相等且不为空格
然后就是剩下两种对角直接列举出来即可
直接看代码:
int panduan(char arr[kuan][chang]) {
//返回 1 即成功
//行
for (int i = 1; i < kuan; i++) {//控制 x 不变,遍历 y ,要是都一样而且不为空格
if (arr[i][1] == arr[i][2] && arr[i][2] == arr[i][3] && arr[i][1] != ' ')
return 1;
}
//列
for (int i = 1; i < chang; i++) {//控制 y 不变,遍历 x ,要是都一样而且不为空格
if (arr[1][i] == arr[2][i] && arr[2][i] == arr[3][i] && arr[1][i] != ' ')
return 1;
}
//对角
if (arr[1][1] == arr[2][2] && arr[2][2] == arr[3][3] && arr[1][1] != ' ')//直接全部都列出来
return 1;
if (arr[3][1] == arr[2][2] && arr[2][2] == arr[1][3] && arr[2][2] != ' ')
return 1;
//都不返回则,返回 -1 ,代表还没成功
return -1;
}
这里返回 1 代表成功,返回-1 代表失败
看看运行的怎么样
游戏过程没问题
全部代码展示
fu.h
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<windows.h>//清屏函数头文件
#include<stdlib.h>//随机数的函数头文件
#include<time.h>//time()头文件
#define chang 4//将棋盘扩大一点,使x,y的范围为1~3
#define kuan 4//
void jiemian(void);//界面1
void xuanze(void);//界面2
void chushihua(char arr[kuan][chang], int x, int y);//初始化
void dayin(char arr[kuan][chang], int x, int y);//打印
void shuangren(char arr[kuan][chang]);//双人模式
int panduan(char arr[kuan][chang]);//判断
void diannao(char arr[kuan][chang]);//电脑对战
zhu.c
#define _CRT_SECURE_NO_WARNINGS
#include"fu.h"
void jiemian(void) {//界面1
printf("*******************************************************\n");
printf("*******************************************************\n");
printf("******** 1 - 开始游戏 ********\n");
printf("******** 0 - 退出游戏 ********\n");
printf("*******************************************************\n");
printf("*******************************************************\n");
}
void xuanze(void) {//界面2
printf("*******************************************************\n");
printf("*******************************************************\n");
printf("******** 2 - 双人对战 ********\n");
printf("******** 3 - 电脑玩家 ********\n");
printf("*******************************************************\n");
printf("*******************************************************\n");
}
void geme(void) {
char arr[kuan][chang];//二维数组
chushihua(arr,kuan,chang);//将数组初始化
int n=0;
xuanze();
printf("请输入:");
scanf("%d", &n);//选择
while (n > 3 || n < 2) {
printf("输入错误,请重新输入:");
scanf("%d", &n);//选择
}
system("cls");//清屏
if (n == 2) {//双人模式
dayin(arr, kuan, chang);//先打印初始化的棋盘
shuangren(arr);//开始游戏
}
else if (n == 3)//电脑模式
{
dayin(arr, kuan, chang); //先打印初始化的棋盘
diannao(arr);//开始游戏
}
}
int main() {
int i;
srand((unsigned int)time(NULL));//设置随机数的种子
jiemian();//打印界面1
do {//选择
printf("请输入:");
scanf("%d", &i);
system("cls");
switch (i) {
case 1: geme();
break;
case 0:
printf("退出成功\n");
break;
default:printf("输入错误,请重新输入:");
break;
}
} while (i);
return 0;
}
fu.c
#define _CRT_SECURE_NO_WARNINGS
#include"fu.h"
void chushihua(char arr[kuan][chang], int x, int y) {//用循环将x,y是1~3的数组初始化为空格
for (int i = 1; i < x; i++) {
for (int j = 1; j < y; j++) {
arr[i][j] = ' ';
}
}
}
void dayin(char arr[kuan][chang], int x, int y) {
for (int i = 0; i < x; i++) {
printf("%-2d", i);//打印时,将x的坐标打印
for (int j = 1; j < y; j++) {
if (i == 0) {//第一行将y坐标打印
printf("%-2d", j);
}
else {//
printf("%c", arr[i][j]);//打印空格
if(j<y-1)//控制‘|’,将每个格子隔开,保证只打印前两个
printf("|");
}
}
if (i != 0 && i != x - 1) {//打印下面的“-----”将每一行隔开,控制只打印中间两行
printf("\n");
printf(" - - - - ");
}
printf("\n");
}
}
int panduan(char arr[kuan][chang]) {
//返回 1 即成功
//行
for (int i = 1; i < kuan; i++) {//控制 x 不变,遍历 y ,要是都一样而且不为空格
if (arr[i][1] == arr[i][2] && arr[i][2] == arr[i][3] && arr[i][1] != ' ')
return 1;
}
//列
for (int i = 1; i < chang; i++) {//控制 y 不变,遍历 x ,要是都一样而且不为空格
if (arr[1][i] == arr[2][i] && arr[2][i] == arr[3][i] && arr[1][i] != ' ')
return 1;
}
//对角
if (arr[1][1] == arr[2][2] && arr[2][2] == arr[3][3] && arr[1][1] != ' ')//直接全部都列出来
return 1;
if (arr[3][1] == arr[2][2] && arr[2][2] == arr[1][3] && arr[2][2] != ' ')
return 1;
//都不返回则,返回 -1 ,代表还没成功
return -1;
}
void shuangren(char arr[kuan][chang]) {//双人模式
int te = (kuan-1) * (chang-1);//求全部格子数--9
while (te != 0) {//循环te=0时平局
int x = 0,y=0;
printf("A(+):");//A先下
scanf("%d %d", &x, &y);
system("cls");
arr[x][y] = '+';//‘+’代表标志
int r = panduan(arr);//进行判断
if (r == 1) {//返回1 则成功,打破
printf("恭喜A赢了\n");
dayin(arr, kuan, chang);//打印最终的棋盘看看
break;
}
else {
dayin(arr, kuan, chang);//打印目前的棋盘继续游戏
te--;//te减1
}
if (te >0) {//当te==0时说明棋盘已经满了,停下
printf("B(-):");
scanf("%d %d", &x, &y);
system("cls");
arr[x][y] = '-';
r = panduan(arr);
if (r == 1) {//返回1 则成功,打破
printf("恭喜B赢了\n");
dayin(arr, kuan, chang);//打印最终的棋盘看看
break;
}
else {
dayin(arr, kuan, chang);
te--;
}
}
}
if(te==0)//当te==0说明每份出胜负
printf("平局了\n");
jiemian();//打印游戏界面,看是否下把游戏
}
void diannao(char arr[kuan][chang]) {//电脑对战
int te = (kuan - 1) * (chang - 1);//求全部格子数--9
while (te != 0) {//我先下,过程和双人的一样
int x = 0, y = 0;
printf("A(+):");
scanf("%d %d", &x, &y);
system("cls");
arr[x][y] = '+';
int r = panduan(arr);
if (r == 1) {
printf("恭喜A赢了\n");
dayin(arr, kuan, chang);
break;
}
else {
dayin(arr, kuan, chang);
te--;
}
if (te > 0) {
printf("B(-):");
do {//控制电脑在空格的地方下棋
x = rand() % 3 + 1;//随机数(%3+1控制这个数在1~3之间)
y = rand() % 3 + 1;
} while (arr[x][y] != ' ');//下面的过程就和上面的一样了
system("cls");
arr[x][y] = '-';
r = panduan(arr);
if (r == 1) {
printf("恭喜B赢了\n");
dayin(arr, kuan, chang);
break;
}
else {
dayin(arr, kuan, chang);
te--;
}
}
}
if (te == 0)
printf("平局了\n");
jiemian();
}
以上就是全部代码了
总结
1.在实现过程要一个模块一个模块来,不要到最后在检查(我上次写扫雷就是这样,血淋淋的教训)
2.先想好了再实现
3.扫雷是和这个类似的,感兴趣的小伙伴可以去试试
最后谢谢大家的观看,要是对你有帮助的话记得三连哦(三连必回)