扫雷游戏
介绍:回想年少时,在琢磨困难版扫雷(100 X 100),现在居然开始琢磨如何写出这样的代码来,真是时间岁月不饶人啊(微笑),好了,让我们继续吧;
- 现在我们需要从头梳理一遍实现过程。
- 首先我们要扫雷,首先需要两个棋盘,一个棋盘用来展示扫雷的页面,另一个棋盘用来存放“ 雷 ”,这两个棋盘间最好有联系,所以这两个棋盘用字符二维数组,棋盘初始化最好不用 (空格),这样打印的棋盘啥也看不到,所以封面的棋盘打印‘*’,另一个棋盘,不是雷用‘0’,是雷用‘1’表示;
如图:
这段时程序开始的设计:
int main()
{
//如果要埋雷,那肯定需要随机值的,随机埋雷。
srand((unsigned int)time(NULL));
int i = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &i);
switch (i)
{
case 1:
printf("进入游戏\n");
game();//进入扫雷的游戏
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (i);
return 0;
}
有menu()
void menu()
{
printf("***********************\n");
printf("***********************\n");
printf("**** 1.play 0.exit****\n");
printf("***********************\n");
}
创建出两个二维数组,设置棋盘
然后game()函数:
需要两组数组,注意:如果我们用简单模式的话也就是(9X9)的数组,我们可以直接#define定义出数组的行和列,如
#define BOW 9 //是定义行
#define COL 9 //是定义列
#define BOWS BOW+2 //这里在原有的行上向外在多打印一行
#define COLS COL+2 //在原来的列上向外在多打印一列,在这里的作用在后续慢慢讲解
char mine[BOWS][COLS] = { 0 };
char show[BOWS][COLS] = { 0 };
//初始化棋盘
Inboard(mine,BOWS,COLS,'0');
Inboard(show,BOWS,COLS,'*');//函数
初始化棋盘
Inboard()函数代码实现
void Inboard(char board[BOWS][COLS], int bows, int cols,char set)
{
int i = 0;
int j = 0;
for (i = 0; i < bows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;//直接将接收的字符进行初始化
}
}
}
既然初始化完成,在打印出两个棋盘;
用这个函数
打印出棋盘
Display();//函数
Display(show, BOW, COL);
Display(mine, BOW, COL);
代码实现:
void Display(char board[BOWS][COLS], int bow, int col)
{
int i = 0;
int j = 0;
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= bow; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
埋雷
既然棋盘已经初始化完成,那就开始在mine()进行埋雷,需要mine数组,行和列,这里只需要在9 X 9 的数组里进行埋雷,在原有的字符‘0’上改成‘1’,这里需要随机值;
用
Setray(mine,BOW,COL);
void Setray(char board[BOWS][COLS], int bow, int col)
{
int count = ray;
printf("--------扫雷游戏--------\n");
while (count)
{
int x = rand() % bow + 1;
int y = rand() % col + 1;
if(x>=1 && x<=bow && y>=1 && y<=col)
{
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
}
在将埋好的雷在把棋盘打印出来:
在这里埋了10个雷,在这里注意,我们这只是打印出来看一下,不是真的打印出来,在游戏测试的时候不需要打印出来,还有在埋雷的时候注意要行在(1-9)之间,列也在(1-9)之间。
在这里需要说明一下为什么需要#define定义出两个不同的行和列,因为是如图下所示:
我们初始化棋盘是这整个棋盘,但是在如图红圈内随机埋雷,因为如果你要计算一个位置的周围的雷的个数,就需要如此。
排雷
埋好了雷,就可以开始排雷了,如果要排雷,就需要输入要排查的坐标,排查的用mine棋盘,在计算出其周围的雷的个数在打印在show棋盘上,如果这个坐标在mine棋盘上是‘1’则挖到雷了,如果不是,则打印出该坐标周围一圈的雷的个数的字符。
想要赢的条件,则是排完所有的雷,则下完 9X9的数组减去地雷。
int Countrow(char mine[BOWS][COLS], int x, int y)
{
return (mine[x - 1][y] + mine[x - 1][y - 1]
+ mine[x - 1][y + 1] + mine[x][y - 1] +
mine[x][y + 1] + mine[x + 1][y] + mine[x + 1][y + 1] +
mine[x + 1][y - 1])-8*'0';
}
void Rowray(char mine[BOWS][COLS], char show[BOWS][COLS], int bow, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win< bow*col - ray)
{
printf("请输入一个要排查的坐标:");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= bow && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("不好意思,你被炸死了;\n");
Display(mine, BOW, COL);
break;
}
else
{
//统计周围的雷的个数
int ret = Countrow(mine, x, y);
show[x][y] = ret + '0';
Display(show, BOW, COL);
win++;
}
}
else
printf("输入坐标错误,请重新输入:");
}
if (win == bow * col - ray)
{
printf("排雷成功\n");
Display(show, BOW, COL);
}
}
优化版排雷
虽然这样可以完成扫雷,但是在排雷的可以优化成和扫雷小游戏一样,该函数实现的功能是:若排查的位置周围没有雷,则向外四周爆炸式展开直至遇到地雷的坐标时停下来。
类似于一个递归函数;
优化版:炸金花式展开。
总有几点注意:
1.在该点的坐标不是雷。
2.在该点的坐标周围没有雷。
3.还有该坐标不是‘ ’。
以上几点都满足则,将该点改成‘ ’;
那如何实现该递归函数?当排查的位置没有雷且该位置周围没有雷,则向外展开一圈的8个坐标,然后看着8个坐标是否可以逐个向其自身再向外展开一圈,这一次就用到递归调用。
具体实现:若每次判断一个坐标上没有雷且周围一圈也没有雷,则向我们展示的show棋盘上未知的字符‘’,改成空格,然后再之后的递归调用前给一个判断,如果这个将要被调用递归的坐标存放的是‘’,才能进行下一步,否则将跳出此次递归。
当在进行操作是有一点注意:非法访问内存空间,出现该问题的原因:数组的越界访问,当向外扩张的递归,向外矿张时,当最外一圈的坐标,无法起到限制作用,无法阻止其向外展开。所以会出现越界访问!!!如何解决:在进行下一次递归调用时加一个限定条件:要再次进行递归调用必须要在原来位置存放的是‘*’的条件下:
下面有具体代码展示:
void explod(char mine[BOWS][COLS], char show[BOWS][COLS], int bow, int col, int x, int y)
{
if (x >= 1 && x <= bow && y >= 1 && y <= col)
{
int count = Countrow(mine, x, y);
if (count == 0)
{
show[x][y] = ' ';
int i = 0;
for (i = x-1; i <= x+1; i++)
{
int j = 0;
for (j = y-1; j <= y+1; j++)
{
if (show[i][j] == '*')
{
explod(mine, show, bow, col, i, j);
}
}
}
}
else
{
show[x][y] = count + '0';
}
}
}
void Rowray(char mine[BOWS][COLS], char show[BOWS][COLS], int bow, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < bow * col - ray)
{
printf("请输入一个要排查的坐标:");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= bow && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("不好意思,你被炸死了;\n");
Display(mine, BOW, COL);
break;
}
else
{
explod(mine, show, bow, col, x, y);//炸金花式展开;
/*system("cls");*/
Display(show, bow, col);
}
}
else
printf("输入坐标错误,请重新输入:");
}
if (win >= bow * col - ray)
{
printf("排雷成功\n");
Display(show, BOW, COL);
}
else
printf("排雷失败\n");
}
最后整体的代码如下:
shaolei.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ray 10
#define BOW 9
#define COL 9
#define BOWS BOW+2
#define COLS COL+2
//初始化棋盘
void Inboard(char board[BOWS][COLS], int bows, int cols,char set);
//打印棋盘
void Display(char board[BOWS][COLS], int bow, int col);
//布置雷、
void Setray(char board[BOWS][COLS], int bow, int col);
//排雷
void Rowray(char mine[BOWS][COLS], char show[BOWS][COLS], int bow, int col);
void Disshow(char show[BOWS][COLS], int bow, int col, int ret);
saolei.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "saolei.h"
void Inboard(char board[BOWS][COLS], int bows, int cols,char set)
{
int i = 0;
int j = 0;
for (i = 0; i < bows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void Display(char board[BOWS][COLS], int bow, int col)
{
int i = 0;
int j = 0;
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= bow; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
void Setray(char board[BOWS][COLS], int bow, int col)
{
int count = ray;
printf("--------扫雷游戏--------\n");
while (count)
{
int x = rand() % bow + 1;
int y = rand() % col + 1;
if(x>=1 && x<=bow && y>=1 && y<=col)
{
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
}
int Countrow(char mine[BOWS][COLS], int x, int y)
{
return (mine[x - 1][y] + mine[x - 1][y - 1]
+ mine[x - 1][y + 1] + mine[x][y - 1] +
mine[x][y + 1] + mine[x + 1][y] + mine[x + 1][y + 1] +
mine[x + 1][y - 1])-8*'0';
}
void explod(char mine[BOWS][COLS], char show[BOWS][COLS], int bow, int col, int x, int y)
{
if (x >= 1 && x <= bow && y >= 1 && y <= col)
{
int count = Countrow(mine, x, y);
if (count == 0)
{
show[x][y] = ' ';
int i = 0;
for (i = x-1; i <= x+1; i++)
{
int j = 0;
for (j = y-1; j <= y+1; j++)
{
if (show[i][j] == '*')
{
explod(mine, show, bow, col, i, j);
}
}
}
}
else
{
show[x][y] = count + '0';
}
}
}
void Rowray(char mine[BOWS][COLS], char show[BOWS][COLS], int bow, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < bow * col - ray)
{
printf("请输入一个要排查的坐标:");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= bow && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("不好意思,你被炸死了;\n");
Display(mine, BOW, COL);
break;
}
else
{
explod(mine, show, bow, col, x, y);//炸金花式展开;
/*system("cls");*/
Display(show, bow, col);
}
}
else
printf("输入坐标错误,请重新输入:");
}
if (win >= bow * col - ray)
{
printf("排雷成功\n");
Display(show, BOW, COL);
}
else
printf("排雷失败\n");
}
game.h
#define _CRT_SECURE_NO_WARNINGS 1
#include "saolei.h"
void menu()
{
printf("***********************\n");
printf("***********************\n");
printf("**** 1.play 0.exit****\n");
printf("***********************\n");
}
void game()
{
char mine[BOWS][COLS] = { 0 };
char show[BOWS][COLS] = { 0 };
Inboard(mine,BOWS,COLS,'0');
Inboard(show,BOWS,COLS,'*');
//Display(show, BOW, COL);
/*Display(mine, BOWS, COLS);*/
//布置雷
Setray(mine,BOW,COL);
Display(show, BOW, COL);
Display(mine, BOW, COL);
//排雷
Rowray(mine,show,BOW,COL);
//将雷打印在show棋盘上
}
int main()
{
srand((unsigned int)time(NULL));
int i = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &i);
switch (i)
{
case 1:
printf("进入游戏\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (i);
return 0;
}
最后收尾 ^ _ ^ ;
写完我知道自己还有很多不足,我会继续努力,请大家小小支持一波。
如果还有哪里不足的地方,还请大家斧正。