【初级擂台】扫雷机器人

BlueSky2008 2003-10-18 12:42:57

在一个20*20的地图上,随机分布着81颗地雷,
扫雷机器人走到其中一个方格上,如果是地雷,它将被炸死,如果不是,它可以探测到它周围的八个方格中有几个地雷。现在你拥有10个扫雷机器人(10次错误机会),请你将这片雷区中的所有地雷标出来。


提供的函数:int GetMineNum(int x,int y)
功能: 返回地图上map[x][y]处周围的八个方格中有几个地雷.

大家先考虑一下,测试程序明天给。
...全文
75 37 打赏 收藏 转发到动态 举报
写回复
用AI写文章
37 条回复
切换为时间正序
请发表友善的回复…
发表回复
chenfeng584521 2003-12-03
  • 打赏
  • 举报
回复
我是个新手
但是我有个问题,程序中没有出现析构函数,是不需要吗?
请解释一下
iampolaris 2003-11-25
  • 打赏
  • 举报
回复
xuexi
sunjguang 2003-11-25
  • 打赏
  • 举报
回复
学习一下。
twhSDK 2003-11-23
  • 打赏
  • 举报
回复
扫雷以前边过。过了这么久具体程序我是一下写不出来了
不过小弟编扫雷采用结构体二维数组的方法。其中结构体的一个成员用来保存
周围八个雷区的雷数。这样的话在初始化的时候就可以统计周边的雷数并保存起来。
以后每次用的时候直接访问那个结构体成员几可以了。比如map[x][y].lei_Number
cauiot 2003-11-22
  • 打赏
  • 举报
回复
up
zhangxiangm 2003-11-17
  • 打赏
  • 举报
回复
UP&MARK
rebbie 2003-11-13
  • 打赏
  • 举报
回复
赫赫,典型带权的深度遍历算法(有个专门名词称呼的,忘了,可以根据当前状态动态调整,保持当前搜索的为位置的深度搜索算法,加权树),
当前开出点周围的雷区的权的计算根据当前开出点显示雷的个数及未确定个数进行概率平均,然后生成该点概率之和,取出概率最小点继续遍历..
tass 2003-11-12
  • 打赏
  • 举报
回复
up
skywind 2003-11-08
  • 打赏
  • 举报
回复
工作室带我们的老师以前出过一个,等我问问他代码 :-)
xuchunqiang 2003-11-07
  • 打赏
  • 举报
回复
有五子棋的算法吗?
0001cxg 2003-11-04
  • 打赏
  • 举报
回复
先记号。。。改天再看了
mmmcd 2003-11-04
  • 打赏
  • 举报
回复
最近太忙,没时间看

作为【初级擂台】,这难度不小。
BlueSky2008 2003-11-03
  • 打赏
  • 举报
回复
//续,算法部分

#define UNCERTAIN 0x0
#define MINE_NUM 0x00FF
#define CERTAIN_SAFE 0x0100
#define CERTAIN_MINE 0x0200
#define GUESS_MINE 0x0400
#define GUESS_SAFE 0x0800

#define X_RANGE X_RANGE_MAX-2
#define Y_RANGE Y_RANGE_MAX-2

int map[Y_RANGE][X_RANGE];

//8个方向
POINT dir[8] =
{ -1,-1, 0,-1, 1,-1,
-1, 0, 1, 0,
-1, 1, 0, 1, 1, 1
};

//该函数检测是否越界
inline bool valid_position(int x,int y)
{
return x>=0 && x< X_RANGE && y>=0 && y< Y_RANGE;
}

/*
该函数查询一个点周围的环境;
uc_queue[]: 不确定的点的队列;
uc_num: 不确定的点数
c_num: 确定为雷的点数
*/
void get_around(int x, int y, POINT uc_queue[], int& uc_num, int& c_num)
{
int tx, ty;

uc_num = 0;
c_num = 0;

for(int i=0; i<8; i++)
{
tx = x + dir[i].x;
ty = y + dir[i].y;

if(valid_position(tx,ty) )
{
if(map[ty][tx] & CERTAIN_MINE | map[ty][tx] & GUESS_MINE )
{
c_num++;
}
else if(map[ty][tx] == UNCERTAIN )
{
uc_queue[uc_num].x = tx;
uc_queue[uc_num].y = ty;
uc_num++;
}
}
}
return;
}

/*
该函数由一个已知点位置进行推导,如果推导出现矛盾,返回0,否则返回1。
*/
bool deduce(int x,int y)
{
POINT uc_queue[8]; //不确定的位置的队列
int uc_num; //不确定的位置数
int mine_num; //周围总的雷数
int c_num; //已确定的雷数

//越界
if(!valid_position(x,y))return 1;

//不是已知的无雷点,由此推导不会得到任何信息 ?
if(!(map[y][x] & CERTAIN_SAFE) )return 1;

get_around(x, y, uc_queue, uc_num, c_num);

mine_num = map[y][x] & MINE_NUM;

// 不确定的点可以全为雷,也可以全为空,
// 实际的情况应该介于这两种情况之间。
// 如果超出这个范围,说明前面的推导有误。
// 如果正好处在边界上,那么可以推导到下一个状态。
if(mine_num < c_num)
return 0;
if(mine_num > c_num + uc_num)
return 0;

int guess = 0;

if(mine_num == c_num)
guess = GUESS_SAFE;
if(mine_num == c_num + uc_num)
guess = GUESS_MINE;

if(guess)
{
for(int i=0; i<uc_num; i++)
{
map[uc_queue[i].y][uc_queue[i].x] |= guess;
}

for( i=0; i<uc_num; i++)
{
for(int j=0;j<8;j++)
{
int tx = uc_queue[i].x + dir[j].x;
int ty = uc_queue[i].y + dir[j].y;

if(deduce(tx,ty) ==0 )
{
//回复设置
for(int k=0; k<uc_num; k++)
{
map[uc_queue[k].y][uc_queue[k].x] &= ~guess;
}
return 0;
}
}
}

for(int k=0; k<uc_num; k++)
{
map[uc_queue[k].y][uc_queue[k].x] &= ~guess;
}
}//if(guess)
return 1;
}


/*
该函数检测某一个点的赋值会不会引起矛盾。

guess 的取值:GUESS_MINE: 此点为雷
GUESS_SAFE: 此点不为雷
*/
bool check(int x,int y, int guess)
{
bool result = 1;

map[y][x] |= guess;

for(int i =0;i<8;i++){
if(deduce(x+dir[i].x, y+dir[i].y)==0)
//如果一个赋值在它周围8个点中的任何一个推导出矛盾,
//这个赋值都是错误的。
{
result = 0;
break;
}
}

map[y][x] &= ~guess;

return result;
}

/*
该函数从一个已经确定的位置(x,y)出发,广度优先搜索它周围的
8个点,看能否确定出新的位置
*/
void BFSearch(int x,int y)
{
int tx,ty;
int queue = 0;//广度优先所需要的队列,用bit位表示
int i;

for(i=0; i<8; i++)
{
tx = x + dir[i].x;
ty = y + dir[i].y;

if(!valid_position(tx,ty))continue;

if(map[ty][tx] == UNCERTAIN)
{
if(check(tx,ty,GUESS_MINE)==0) //如果将map[ty][tx]位置设为雷,推导出矛盾,
{ //那么map[ty][tx]位置一定为安全位置。
map[ty][tx] = MineMap.GetMineNum(tx,ty);
map[ty][tx] |= CERTAIN_SAFE;
queue |= (0x01<<i);
}
else if(check(tx,ty,GUESS_SAFE)==0) //如果将map[ty][tx]位置设为安全位置,
{ //推导出矛盾,那么map[ty][tx]位置一定为雷。
map[ty][tx] = CERTAIN_MINE;
queue |= 0x01<<i;
}
}
}// for 8 direction

for(i=0; i<8; i++)
{
if(queue & (0x01<<i)) //对每一个新确定的位置,广度优先搜索,
{ //看能否通过它再确定出新的位置
BFSearch(x+dir[i].x, y+dir[i].y);
}
}
}

/*
该函数逐行逐列的扫描地图,每遇到一个不确定的点,就将其打开,
然后调用BFSearch()函数,看看能否通过已知的点,确定出新的点。
当BFSearch()最终返回时,所有可以确定的点都以搜索过。
*/
void MineCracker(void)
{
int x,y,n;

for(y=0; y<Y_RANGE; y++)
{
for(x=0; x<X_RANGE; x++)
{
if(map[y][x]==UNCERTAIN)
{
n = MineMap.GetMineNum(x,y);

if(n>=0 && n<=8)
map[y][x] = n | CERTAIN_SAFE;
else
map[y][x] = CERTAIN_MINE;

BFSearch(x,y); //对每一个新确定的位置,广度优先搜索,
//看能否通过它再确定出新的位置
}
}
}
}
BlueSky2008 2003-11-03
  • 打赏
  • 举报
回复
公布答案了。
程序中我都加了详细的注释,如有不理解的地方,可以再来问我。
并且可以演示,需要windows.h 头文件。VC++6.0下调试通过。
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <iostream.h>
#include <fstream.h>
#include <windows.h>

#define X_RANGE_MAX 22
#define Y_RANGE_MAX 22

class MINE
{
public:
int GetMineNum(int x,int y);
int Reset(void);
void GetSummary(int* ErrNum, int* CallTime, int* Revealed){
*ErrNum = err_num;
*CallTime = call_time;
*Revealed = revealed;
};
protected:
char map[Y_RANGE_MAX][X_RANGE_MAX];
int err_num;
int call_time;
int revealed;
int judge(int x,int y);
};

MINE::GetMineNum(int x,int y)
{
int sum = -1;

COORD pos;

pos.X = x;
pos.Y = y;
for(int i=0; i<3;i++)
{
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
printf(" ");
Sleep(100);
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
printf("?");
Sleep(100);
}

x=x+1;
y=y+1;

if(judge(x,y)==0)
{
sum = map[y-1][x-1] + map[y-1][x] + map[y-1][x+1]
+ map[y][x-1] + map[y][x+1]
+ map[y+1][x-1] + map[y+1][x] + map[y+1][x+1];
}

if(sum==-1)
{
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
printf("#");
}
else
{
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
printf("%d",sum);
}
return sum;
}


MINE::Reset(void)
{
int mine_num = 80;
int x,y;

err_num = 0;
call_time = 0;
revealed =0;

for(y=0; y<Y_RANGE_MAX; y++)
for(x=0; x<X_RANGE_MAX; x++)
map[y][x] = 0;

srand(time(0));
while(mine_num)
{
x = rand()%20+1;
y = rand()%20+1;
if(!map[y][x])
{
map[y][x]=1;
mine_num--;
}
}

for(y=1;y<=20;y++)
{
for(x=1;x<=20;x++)
{
cout<<(map[y][x]? "*" : "o");
}
cout<<endl;
}

return 0;
}

MINE::judge(int x,int y)
{
int err;


call_time++;

if(x<1 || x>X_RANGE_MAX-2 || y<1 || y>Y_RANGE_MAX-2 )//越界
{
err = 2;
}
else if(map[y][x]==1)//地雷
{
err_num++;
err = 1;
}
else if(map[y][x]==0)//新位置,无雷
{
err = 0;
}

return err;
}

MINE MineMap;

void MineCracker(void);

main()
{
int ErrNum;
int CallTime;
int UseTime;
int Revealed;

MineMap.Reset();

UseTime = clock();

//******************
// YourFunction();
//******************

MineCracker();

UseTime = clock() - UseTime;

MineMap.GetSummary(&ErrNum,&CallTime,&Revealed);

COORD pos;
pos.X = 0;
pos.Y = 20;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
cout<< "Error Num: " << ErrNum << endl
<< "Call Times: " << CallTime << endl
// << "Revealed: " << Revealed << endl
<< "Use Time: " << UseTime << endl;

return 0;
}
flyingforce 2003-10-31
  • 打赏
  • 举报
回复
没注意看楼主后面的测试程序,呵呵,回头再说了
flyingforce 2003-10-31
  • 打赏
  • 举报
回复
很有意思的题目

大概想了一下,没有出程序:

定义几个Hashtable:
Safety, 储存所有已经被探查过的安全位置点
Mine, 储存所有已知的雷
Searchable, 储存所有安全的没有被探查过的位置点
DangerSerachable,储存周围已经有位置被搜索过,但不能判断是否安全的位置点
Refresh,储存所有被搜索但没有得到结论的位置点

定义一个值:
priortity,这个值作为一个点的权,用来度量一个点作为雷的可能性大小,如果为1,则认为该点为雷,为0,则认为该点无雷


定义几个function:
GetLeftMines(int x,int y)
返回值:返回x,y点反映周围雷数以及已知周围雷数的差值

GetLeft(int x,int y)
返回值:返回x,y点周围未知情况点的数量

Refresh(int x,int y)
方法:
1.如果GetLeftMines(x,y)=GetLeft(x,y), 将所有周围未知状态点放入Mines,并刷新DangerSearchable,函数结束
2.如果GetLeftMines(x,y)=0,则将周围所有未知状态点放入Searchable
3.如果GetLeftMines(x,y)<GetLeft(x,y),则将所有周围未知状态点放入DangerSerachable ,并重新刷新DangerSearchable 中这些位置点的权,方法为所有未知点的权值添加GetLeftMines(x,y)/GetLeft(x,y)
4.如果DangerSeachable中有点的权值为1,则将该点放入Mines
5.从Refresh中取出所有该Mines的周围点,递归的调用Refresh函数,这儿的问题是权值的计算上可能需要调整
6.将x,y点放入Safety
7.函数结束

函数主流程:
如果所有Hashtable全部为空,则将(1,1)点放入DangerSearchable中,权值为0.5
1.从Serachable中取出第一个点,对他使用refresh函数,如果Searchable为空,则从DangerSearchable中取出权值最小的点使用refresh函数

问题在于如果扫雷出错,因为搂主没给出出错的返回情况,没有考虑
还有一点,如果Mines中雷的数量为81了,则程序结束

随手写写,没有能形成代码,等有时间再说吧。楼主指教。
BlueSky2008 2003-10-30
  • 打赏
  • 举报
回复
to csdnxw(大家帮忙):
可以利用的应该是[0,19].
如果要做的话可要快点了,应为我最迟这周末要公布答案了,不能再等太长时间了。
csdnxw 2003-10-30
  • 打赏
  • 举报
回复
To BlueSky2008:
22*22的数组,周围一圈作屏障
我们可以利用的应该是[1,20]吧?

BlueSky2008 2003-10-29
  • 打赏
  • 举报
回复
to smalldusk:
机器人的放置没有什么限制,可以任意移动到任何位置。但你要事先算准了那个地方不是雷。
其实和我们平时玩的扫雷游戏是一样的.调用函数GetMineNum(x,y),就代表你要用鼠标去点(x,y)处的方格。唯一不同的是现在允许你错多次(因为本来就不能保证一个不错),但点错的次数要尽量少。






smalldust 2003-10-29
  • 打赏
  • 举报
回复
今天才看到题目。有个问题:
机器人放置的规则是什么?任意(x,y)吗?还是一步一步行动那种。
加载更多回复(17)

33,008

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧