缘由
今天的新书:编程之美 到手了,随便读了翻了一下。就翻到一个有趣的问题,计算扫雷游戏中未知方块是雷的概率。
看看图,就是那些还可以点的方块是雷的概率是多少。相信大家都玩过,我就不介绍游戏规则了。
思路
简单的思考之后,我有了一个思路:
- 对每一个出现的数字作如下计算
- 针对一个数字,统计数字周边可点击的方块的个数
- 数字的数值除以方块的个数,就是周边方块是雷的概率
- 如果多个数字计算了同一个方块的是雷的概率,则记录概率的高的那一个。
本来想看看参考答案的,翻过书的一页过去居然是空白,然后就是下一页,书上对这道题没有解答。
命令行的扫雷游戏
界面
为了验证我的想法是正确的,我决定自己先写一个命令行版扫雷游戏。
我打算用一个二维数组来来表示扫雷的界面:就16×16(书上的图的大小,但是为了和储存雷的数组统一,还是使用17×17)吧,再用一个17×17的数组存储雷的位置和各个方块的周边的雷的数量,第一列和第一行用于标识行列号。
用一些特别的符号来表示,
- 数字1-8已经被占了,用于表示周边方块的雷数
- +来表示可以点击的地方,空白的地方
- 空白 用来表示上图中空白的部分
流程
先对存储雷的位置和各个方块的周边的雷的数量的17×17(除去行列号应该是16×16)的数组赋值,如下
- 先随机生成雷的位置,雷就用99来代替吧,选用数字是为了打印方便
- 然后在雷的周边方块生成数字,就一个雷一个雷的计算吧,计算一个雷周边方便未变1,第二个雷计算的周边方块在原有的基础上增加1
- 没有雷也没有数字的地方就以0来表示。这是因为方便在第二步的时候直接加起来
- 界面就按上面所说的字符代表,通过输入行和列来表示点击了哪个雷
- 每输入一对数相当于点击了一下方块,只有+号才可以被点击
- 直到界面中只剩下雷没有被点的时候,游戏结束。
代码
#include <stdlib.h>
#include <stdio.h>
#include "time.h"
#define HEXASCII(val) ((val)>0 && (val)<10)?'0'+(val):\
((val)>9 && (val)<16)?'A'+(val)-10:'0' //将十进制转化为ASCII,在printf打印的时候方便处理
#define MAXTHUNDERCOUNT 10 //产生最大的地雷数
static char thunder[17][17];
static char view[17][17];
void initThunder(char thunder[17][17]) {
int i, j;
//循环变量
int x, y;
//x,y的坐标
for (i = 0; i < 17; i++) {
thunder[0][i] = i;
//设置行号
thunder[i][0] = i;
//设置列号
}
for (i = 1; i < 17; i++) {
for (j = 1; j < 17; j++) {
thunder[i][j] = 0; //全部设置为0
}
}
srand (time(NULL));
//产生随机种子,每次产生的随机数都不一样
for(i=0;i<MAXTHUNDERCOUNT;i++) {
x=rand()%16+1;
y=rand()%16+1;
thunder[x][y]=99; //用99代表雷
//将周围雷周围的数字加1
if(thunder[x-1][y]>=0&&thunder[x-1][y]<=8) {
thunder[x-1][y]++;
}
if(thunder[x+1][y]>=0&&thunder[x+1][y]<=8) {
thunder[x+1][y]++;
}
if(thunder[x][y-1]>=0&&thunder[x][y-1]<=8) {
thunder[x][y-1]++;
}
if(thunder[x][y+1]>=0&&thunder[x][y+1]<=8) {
thunder[x][y+1]++;
}
if(thunder[x-1][y-1]>=0&&thunder[x-1][y-1]<=8) {
thunder[x-1][y-1]++;
}
if(thunder[x-1][y+1]>=0&&thunder[x-1][y+1]<=8) {
thunder[x-1][y+1]++;
}
if(thunder[x+1][y-1]>=0&&thunder[x+1][y-1]<=8) {
thunder[x+1][y-1]++;
}
if(thunder[x+1][y+1]>=0&&thunder[x+1][y+1]<=8) {
thunder[x+1][y+1]++;
}
}
}
void printView(char view[17][17]) {
int i;
int j;
for (i = 0; i < 17; i++) {
for (j = 0; j < 17; j++) {
if (i == 0 || j == 0) {
printf(" %2d", view[i][j]); //打印行列号,必须用%d
} else {
printf(" %2c", view[i][j]);
}
}
printf("\n");
}
}
void initView( char view[17][17]) {
//输出thunder
/*
for(i=0;i<17;i++){
for(j=0;j<17;j++){
printf(" %2d",thunder[i][j]);//打印行列号,必须用%d
}
printf("\n");
}*/
int i, j;
//循环变量
int x, y;
//x,y的坐标
for (i = 0; i < 17; i++) {
view[0][i] = i;
//设置行号
view[i][0] = i;
//设置列号
}
for (i = 1; i < 17; i++) {
for (j = 1; j < 17; j++) {
view[i][j] = '+';
}
}
printView(view);
}
void recursionToSpace( int x, int y) {
view[x][y] = ' ';
if(x - 1 > 0){ //第一个是为了防止出界
if ( thunder[x - 1][y] == 0 && view[x - 1][y] == '+') { //最后一个表示这个已经变过了,已经是空格的话,就不用再变了
recursionToSpace(x - 1,y);
}else if(thunder[x - 1][y] > 0) {
view[x - 1][y]=HEXASCII(thunder[x - 1][y]);
}
}
if(x + 1 < 17 ){
if (thunder[x + 1][y] == 0 && view[x + 1][y] == '+') {
recursionToSpace(x + 1,y);
}else if(thunder[x + 1][y] > 0 ){
view[x + 1][y]=HEXASCII(thunder[x + 1][y]);
}
}
if(y - 1 > 0){
if ( thunder[x][y - 1] == 0 && view[x][y - 1] == '+') {
recursionToSpace(x,y - 1);
}else if(thunder[x][y - 1] > 0){
view[x][y - 1]=HEXASCII(thunder[x][y - 1]);
}
}
if(y + 1 < 17){
if (thunder[x][y + 1] == 0 && view[x][y + 1] == '+') {
recursionToSpace(x,y + 1);
}else if( thunder[x][y + 1] >0){
view[x][y + 1]=HEXASCII(thunder[x][y + 1]);
}
}
if(x-1 > 0 && y-1 > 0){
if (thunder[x - 1][y - 1] == 0 && view[x - 1][y - 1] == '+') {
recursionToSpace(x - 1,y - 1);
}else if( thunder[x - 1][y - 1] > 0){
view[x - 1][y - 1] =HEXASCII(thunder[x - 1][y - 1] );
}
}
if(x - 1> 0 && y + 1 < 17){
if (thunder[x - 1][y + 1] == 0 && view[x - 1][y + 1] == '+') {
recursionToSpace(x - 1,y + 1);
}else if(thunder[x - 1][y + 1] > 0){
view[x - 1][y + 1]=HEXASCII(thunder[x - 1][y + 1]);
}
}
if(x + 1<17 && y-1 > 0){
if (thunder[x + 1][y - 1] == 0 && view[x + 1][y - 1] == '+') {
recursionToSpace(x + 1,y - 1);
}else if(thunder[x + 1][y - 1] > 0){
view[x + 1][y - 1]=HEXASCII(thunder[x + 1][y - 1]);
}
}
if(x+1<17 && y+1 <17){
if (thunder[x + 1][y + 1] == 0 && view[x + 1][y + 1] == '+') {
recursionToSpace(x + 1,y + 1);
}else if(thunder[x + 1][y + 1] > 0){
view[x + 1][y + 1]=HEXASCII(thunder[x + 1][y + 1]);
}
}
}
int main(){
initThunder(thunder);
initView(view);
int x,y,i,j;
while(1){
printf("想点击的坐标:\n");
scanf("%d %d",&x,&y);
printf("你选择的是结果是:%d\n",thunder[x][y]);
if(thunder[x][y]==99){ //如果点到了雷
printf("踩到雷了,输了\n");
for(i=0;i<17;i++){
for(j=0;j<17;j++){
printf(" %2d",thunder[i][j]);//打印行列号,必须用%d
}
printf("\n");
}
}else if(thunder[x][y]==0){ //如果点到的是空白
//就要这个空白周边的8个不是雷的方块也显示出来
//并且如果周边是空白,也会类似方式传染开,相信玩过扫雷的都明白
recursionToSpace(x, y);
printView(view);
}else{ //如果点到是数字
view[x][y]=HEXASCII(thunder[x][y]);
printView(view);
}
int VictoryFlag=0;
//那么只有遍历才知道有多少加号
for (i = 1; i < 17; i++) {
for (j = 1; j < 17; j++) {
if(view[i][j] == '+')
VictoryFlag++;
}
}
if(VictoryFlag==MAXTHUNDERCOUNT){//胜利条件,view中的+号等于MAXTHUNDERCOUNT即胜利了
printf("胜利了,游戏结束\n");
break;
}
}
}