好了上两篇是讲的数组和函数的知识点,那么,现在尝试一下结合两部分的知识实现一下最简单版本的扫雷游戏吧!
什么?不知道扫雷?嘿嘿,不给你介绍。
首先,写一个游戏之前,我们应该知道这个游戏的基本逻辑,简易版的扫雷的游戏逻辑是在一个规格是9*9的棋盘里面排除雷,雷的数量可以自己输入,位置是随机的,当点某一个位置时,如果有雷你就噶了,如果没有就会显示这个点位周围有多少个雷,这个周围是指周围的8个格子,当成功点击除了雷的位置,你就win了!
游戏嘛,没有菜单怎么玩?所以需要一个菜单,最基本的框架,最好是能循环判断你是否离开游戏或者是继续游戏的。
#include <stdio.h>
void menu()
{
printf("******************\n");
printf("**1 play 0 exit**\n");
printf("******************\n");
}
int main()
{
int input = 0;
do
{
menu();
printf("来吗?扫雷哦\n");
scanf("%d", &input);
switch (input)
{
case 1:
case 0:
default:
}
} while (input);
return 0;
}
一个最基本的框架就好了,只是还没有判断不同情况下该怎么执行,为什么采用do-while呢?因为菜单总是最先打印的嘛,所以直接打印出来,而前面讲过,函数不仅是用来提高效率的,还是可以用来提高整体的简洁度,不然都放在主函数里面,看着多多少少有点不舒服。
循环的实现就是在while()里面,因为非0就是真,所以只要不输入0,就会循环输入。
当用户输入了1之后,就开始游戏了,那么我们应该是在case 1 情况下实现游戏代码的,但是游戏代码放在单独的源文件笔者认为好一点,不同的源文件干不同的事嘛。
现在,我们菜单有了,那么我们需要一个键盘吧?这个布局多大的呢?基础难度是9*9的,当然,一旦我们需要,9*9也能变成10*10,所以修改为了简单,我们就用define定义长和宽。
像这样,为什么又多了一个头文件?因为函数那篇讲过,函数的声明最好放在头文件里面,尤其是多文件的情况下,那么ROW COL长宽有了,还有个ROWS怎么回事?你猜猜呢?
因为多创建了一个头文件,那么我们不妨把所有的库函数需要的头文件,以及函数的声明都放在这个头文件里面,这样就避免的两个头文件引用许多相同的头文件。头文件还需要这些。
因为雷的位置是随机的,所以不妨会涉及到随机数的生成,自然需要这两个头文件了。
好了,现在应该讨论棋盘了,我们设置的是9*9棋盘,但是实际上当我们要实现点击一个位置会出现周围有几个雷的时候,边界的点位其实是不好计算的,如果在中间,我们可以通过ASCII值判断,但是边上的不行。
那么,怎么办呢?只需要把棋盘扩大一圈,最外面的一圈都不放炸弹,不就解决了吗?所以这就是ROWS COLS的由来。
棋盘肯定是二维数组,所以先创建一个二维数组,然后呢?数组前面提到数组里面的内容如果不初始化,值都是一些随机值的,那么只需要一个二维数组吗?倘若你被炸死了,想要知道雷的位置,是不是需要加一个棋盘呢?一个棋盘用来玩家玩游戏,一个用来给玩家看怎么被炸死的,而且多了一个雷数组,还有利于后期的调试。倘若给数组一个0,那么就全是0,可都是0谁分得清哪个数组是哪个数组啊,所以,初识的重要性来了。
第一步,初始化。
函数的声明调用都完成了,就差怎么定义了,你问,set有什么用,好问题,如果我调用两次函数,而初始化内容不变的话,那么最后两个数组的初始化内容是一样的,所以我们多传入一个参数,用这个参数完成每次数组的初始化。
那么这些参数是什么呢?二维数组要传吧?行,列要传吧?那你说ROWS COLS能省略吗?能啊,但是由数组的知识点来说,省行不胜列,所以还不如不省略,美观一点。
void Initboard(char arr[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
arr[i][j] = set;
}
}
}
这,初始化就完成了。
第二步,打印棋盘。
棋盘都初始化好了,你不想打印出来看看吗?我想。
void Printboard(char arr[ROWS][COLS], int row, int col)
{
for (int m = 0; m <= row; m++)
{
printf("%d ", m);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i);
for (int j = 1; j <= col; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
要注意的是,这里传参,传的不是ROWS,因为打印数组,只需要打印9*9的就行了,不需要打印多出来的那一圈。
效果图,0 1 2 3 4 5 6 7 8 9 打印出来好一点,这样用户就容易知道自己输入的点的坐标对应的是哪个位置了。当然,游戏开始的时候只需要打印arr1就行了,雷数组大可不必。
第三步,放雷。
初始化,打印都做好了,就差炸弹了,前文提及,炸弹的数量我们是可以自己设置的,所以炸弹的数量我们仍采用宏的方式。比如就定炸弹有10个好了。
#define MINE 10
那么现在就是怎么放置的问题了,我们需要考虑两个问题,一个是随机排放的问题,一个是如果放的位置上已经有了怎么办
1 随机数的问题交给srand,rand就行,坐标我们采用x,y,因为是9*9的棋盘,直接用rand对长宽取模加 1就行,坐标随机数的问题就解决了。
2 放的位置有了?有了循环次数就加1呗
代码如是
void Putmine(char arr[ROWS][COLS], int row, int col)
{
for (int i = 0; i < MINE; i++)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '*')
{
arr[x][y] = '1';
}
else
{
i--;
}
}
}
这是效果,藏雷前和藏雷后的棋盘。
OK了,雷藏好了,该是玩家进行排雷了。
第四步,排雷。
排雷,首先考虑的是坐标的合法性,然后要考虑的是输入坐标后要能成功的输出周围有几个雷。
因为我们设置的雷是字符1,那么只要周围的所有字符与字符0减,就可以返回周围有几个雷。
Ps:刚有点问题,所以在数组的初始化上进行了一些改动。
改动是因为如果把雷数组初始化为0字符的时候,计算雷的个数会出现问题,计算出来就不是雷的个数了,别问,问就是笔者花了挺久才发现。
1 坐标的合法性可以使用if语句进行判断
2 因为排雷,排一次,计算是在雷数组里面计算,而打印出来的是展现数组,所以这次传参需要传两个数组
3 判断胜利条件,只需要多创建一个变量,当这个变量 = 长 * 宽 - 雷的个数的时候就可以结束游戏了,因为你赢了!
//计算周围几个雷
int Comput(char arr[ROWS][COLS],int x,int y)
{
return arr[x - 1][y] + arr[x - 1][y - 1] +
arr[x][y - 1] + arr[x + 1][y - 1] +
arr[x + 1][y] + arr[x + 1][y + 1] +
arr[x][y + 1] + arr[x - 1][y + 1] - 8 * '0';
}
//排雷
void Excludemine(char arr1[ROWS][COLS],char arr2[ROWS][COLS], int row, int col)
{
int count = 0;
int x = 0, y = 0;
while (1)
{
printf("来,输入坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if ('1' == arr2[x][y])
{
printf("遭!\n");
Printboard(arr2, ROW, COL);
break;
}
else
{
count++;
int num = Comput(arr2, x, y);
arr1[x][y] = num + '0';
Printboard(arr1, ROW, COL);
}
}
else
{
printf("耶装疯哦,重新输入\n");
}
if (count == (ROW * COL - MINE))
{
printf("耶凶哦!!!\n");
Printboard(arr2, ROW, COL);
break;
}
}
}
好了,最简单的扫雷就这样匆忙结束了,,,来看看所有的代码吧。
主函数在的源文件的代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
printf("******************\n");
printf("**1 play 0 exit**\n");
printf("******************\n");
}
void game()
{
char arr1[ROWS][COLS] = { 0 };
char arr2[ROWS][COLS] = { 0 };
Initboard(arr1, ROWS, COLS, '*');//初始化展现的数组
Initboard(arr2, ROWS, COLS, '0');//初始化藏雷的数组
Printboard(arr1, ROW, COL);//打印展现的数组
//Printboard(arr2, ROW, COL);//打印藏雷的数组
Putmine(arr2, ROW, COL);//藏雷咯
//Printboard(arr2, ROW, COL);//打印藏雷的数组
Excludemine(arr1,arr2, ROW, COL);//排雷
}
int main()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();//打印菜单
printf("来吗?扫雷哦\n");
scanf("%d", &input);//用户输入
switch (input)//判断输入
{
case 1://用户想玩
game();
break;
case 0://用户不玩
printf("不玩?哦豁\n");
break;
default://用户装疯
printf("耶,输错了\n");
break;
}
} while (input);
return 0;
}
游戏代码块
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//初始化棋盘
void Initboard(char arr[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
arr[i][j] = set;
}
}
}
//打印棋盘
void Printboard(char arr[ROWS][COLS], int row, int col)
{
for (int m = 0; m <= row; m++)
{
printf("%d ", m);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i);
for (int j = 1; j <= col; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
//藏雷
void Putmine(char arr[ROWS][COLS], int row, int col)
{
for (int i = 0; i < MINE; i++)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
}
else
{
i--;
}
}
}
//计算周围几个雷
int Comput(char arr[ROWS][COLS],int x,int y)
{
return arr[x - 1][y] + arr[x - 1][y - 1] +
arr[x][y - 1] + arr[x + 1][y - 1] +
arr[x + 1][y] + arr[x + 1][y + 1] +
arr[x][y + 1] + arr[x - 1][y + 1] - 8 * '0';
}
//排雷
void Excludemine(char arr1[ROWS][COLS],char arr2[ROWS][COLS], int row, int col)
{
int count = 0;
int x = 0, y = 0;
while (1)
{
printf("来,输入坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if ('1' == arr2[x][y])
{
printf("遭!\n");
Printboard(arr2, ROW, COL);
break;
}
else
{
count++;
int num = Comput(arr2, x, y);
arr1[x][y] = num + '0';
Printboard(arr1, ROW, COL);
}
}
else
{
printf("耶装疯哦,重新输入\n");
}
if (count == (ROW * COL - MINE))
{
printf("耶凶哦!!!\n");
Printboard(arr2, ROW, COL);
break;
}
}
}
头文件
#pragma once
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define MINE 10
//函数的声明
void Initboard(char arr[ROWS][COLS],int rows,int cols,char set);//初始化
void Printboard(char arr[ROWS][COLS], int row, int col);//打印
void Putmine(char arr[ROWS][COLS], int row, int col);//藏雷
void Excludemine(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int row, int col);//排雷
好了看官们慢慢看吧,,博主累了哈哈哈。