算法实现自动扫雷游戏
1.游戏的构思
2.算法伪代码的实现
3.算法的实现
1.首先需要建立起游戏的整个框架(棋盘的绘制,地雷的生成,基本函数的实现等)
2.构思AI算法的大概样貌(先尝试写伪码)
void AI() //AI算法
if(First)
SelectRandomPos()//游戏开始随机选择一处位置翻开
SelectPos()//根据已有条件选择最佳翻开位置
if(!GameOver)
AI()//递归继续进行选择,直到游戏结束
3.简单的伪码写好后,接下来应该来思考如何具体实现这一算法
3.1对于SelectRandomPos()实现随机位置,我们可以使用
Col = (rand())%ColNum;
Row = (rand())%RowNum;//Col,Row为随机翻开的行列坐标
//ColNum和RowNum为总的行列数
//注意这里的随机为伪随机,要想真正接近随机可以使用下面这段代码
srand((unsigned int)(time(NULL)));
int k = (rand())%25;
int l = (rand())%14;
参考:用时间做种子生成随机数
实现这一函数功能
3.2对于函数SelectPos(),这是我们的重头戏,这个函数我们的作用是实现选择最佳翻开位置,在我们不知道当前位置是否有雷的情况下,那么我们去选择呢?我们需要将问题数字化,定义一个指数来判断,是否应该翻开当前位置,而概率则可充当这一任务。
我们知道当第一次翻开时,我们是随机的,那么第二次我们就可以根据我们已知的条件来判断了,例如当前如图(1)
对于当前情况我们需要分析当前点周围出现地雷的概率如图(2)
周围有Count个点未被翻开,而周围显示有N个地雷,所以我们可以知道
P=N%Count,得到概率为0.25,注意此处N,P,Count应该定义为浮点型。
现在我们应该翻开它周围点吗?我们还需要思考能不能翻开它周围以外的点,我称它为冒险博弈GetRisk();
由于我们已经知道,被掀开点周围有2个地雷Mine,(如果)地雷(MineNum)和行(Col)列(Row)数目是已知的,那我们就可以得到除了它周围以外的点是地雷Mine的风险risk,P=(MineNum-已知地雷数目)%(Col*Row-9)
将得到的概率相互一比较,得到最优选择。
当区域内多点被掀开,我们应该如何去考虑它的概率?如图(3)
但我们掀开周围点后,发现另一个点周围有三个地雷,那么如何确定这两个点周围点是地雷的概率呢?
首先我们需要确定新掀开的点周围有雷的概率,然后再更新前一个点的概率,注意概率的更新只影响到被掀开点周围的点。
然后将两掀开点周围的点纳入一个数组MineAround[]中,再计算被掀开点周围有雷的概率,注意,这一步的计算和前面不同,我们先将区域划分如图(4)
计算区域1的概率时,我们暂时不考虑区域2,区域1中右上角的点已经被掀开,那么说明确定那一点没有雷,那么剩下的点是雷的概率是P1=1%7;
同样对于区域二来说它中心点周围点为雷的概率是:P2=3%7;
然后我们来考虑两者结合的情况,对于两区域为被掀开的点来说它们是雷的概率由它周围点来确定,例如我们简单的举出两个点,如图(5)
对于点b的区域来说,它自身是没有被掀开的,我们计算点b是雷的概率就需要通过它周围的被掀开的点来确定,对于b来说,在它周围只有左下角点被掀开,所以我们认为点b是雷的概率为Pb=P2=3%7,也就是显示3的点的周围点概率。
那么如何确定点a是雷的概率呢?
对于点a,它周围有两个点被掀开,我们就基于这两个点来确定它是雷的概率Pa:
Pa1一定是雷的概率 = (1%7)(3%7)
Pa2一定不是雷的概率 = (6%7)(4%7)
对于这里我们的目的是知道a不是雷的概率,所以我们取Pa = 1-Pa1;
(虽然对于该点来说只有是雷和不是雷的情况,但是这里只讨论可能性)
这样,当我们计算得到所有周围点的概率后,比较出一个最好的概率情况(概率相同时任意取),然后再与Risk比较,得出最后最优选择点位。
--------------------------------2020/12/17-机房-----------------------------------------------
显然在实际情况中,我们遇到的情况将不会只有这些,如果单纯应用上面的算法,那么你扫雷的效率(正确率)是不高的,这是为什么呢?我们需要结合实际情况来分析:
如图(6):
当程序运行到这一步的时候,如图所示我标出的红色线框
,按照我们的逻辑,对于红色线框中的未被掀开点,我们可以确定它不是雷的(因为它周围的唯一一颗雷已经被发现-标上了旗帜),但是当程序实际运行的时候程序会怎么做呢?如图(7)
可以看到程序并没有选择我们认为的最优点,这是为什么呢?我们来尝试通过我们的算法计算一下最优点不是雷的概率:P 1= (3%4)1(1%3)=1%12
对于走错的那个点(踩到雷的点),我们计算一下它不是雷的概率:P2 = (1-2%7)=5%7>P1
程序走得没错,完全按照了我们的算法来执行,但是出现了错误,说明我们算法还有改进的空间,我们忽略了这一种特殊情况,所以我们将它添加上:
for i in MineAround[](已经被掀开的点组成的数组,不包括旗帜,空点和地雷)
if i.Prob==0(i周围为雷的概率为0)
then open i.around
#在这里,我们加入了一种特殊情况,当已经被掀开的点中,存在周围点是雷概率为0的特殊情况是,我们不再比较最优选择(概率),直接打开它的周围点。
添加代码后,实现效果如图(7)
笔者最近有点忙,更新可能会很慢。
未完待续。。。。。。。。
当然在实际调试情况中,我们不仅仅将只是遇到这些情况,例如:
1.当我们遇到确认是地雷的点该怎么办?
2.当我们遇到的所有点周围是雷的概率都很高(但不确定是雷或者不是雷)怎么办?
3.等等等等。。。还需要我们慢慢去探讨。
可视化程序设计,课程教师:ZZL
2020/12/15