数据结构课设B——贪吃蛇大作战

文章目录

B

B(1)

给出现在地图的情况,以及小蛇行动的方向,判断小蛇向这个方向走会不会死掉。
分别讨论小蛇会死掉的情况即可。
会死掉的情况如下:

  1. 目标点是墙壁
  2. 目标点是障碍物
  3. 目标点是对手的蛇的身体
  4. 目标点是自己的蛇的身体(尾巴除外)
  5. 到目标点的路径被阻隔

需要注意的情况:

  • 对于第三点:需要注意两条小蛇是按编号的先后顺序走的,而不是同时走的,所以目标点是对手小蛇身上的任意一点都会死掉。
    在这里插入图片描述
    上面情况的下面的蛇向左走会死。

  • 而对于第四点:我们可以理解为尾巴比头先动,头可以走到原先尾巴的位置。
    在这里插入图片描述
    所以上面的情况向上走小蛇不会死掉。

  • 第五点是针对可以斜着走的蛇来说的,如果方向大于3的话需要判断到达目标点的两侧是否会被堵死。我们斜着走的目标点,即原先的横纵坐标都发生了改变,我们只需判断只改变横坐标和只改变纵坐标的两个点是否是都是阻碍蛇运动的点(障碍物、对手的蛇的身体、自己的蛇的舌体出去尾巴的部分),即可判断设能否到达目标点。特别注意的是,对于蛇的尾巴,正常情况下并不会阻碍蛇的运动,但是当蛇通过斜着走可以吃到东西,就意味着蛇会变长,原先会向前挪动的尾巴就会还留在原先的位置,这就导致了尾巴由原先不会产生阻碍变得会产生阻碍了。
    如下面的情况下,若右上角的食物不存在,则小蛇可以向右上角走。
    在这里插入图片描述

但右上角有了食物,小蛇往右上角走就会死。
在这里插入图片描述

B(2)

通过编写machine_move函数决定小蛇下一步的方向。这一问比较玄学,没有明确的做法,也很难判断方法的优劣,这里只是提供一些我能想到的思路,效果怎样真的很难说。

首先我们可以通过修改主函数使两条小蛇都动起来,主函数里默认t一直等于0,只有白蛇在动。
我们通过在如下位置,添加一条t^=1;的语句,实现小蛇的交替行动。
在这里插入图片描述
但后续我们会发现运行到一半经常会出现只有一个小蛇在动的情况,卡了很久才发现使主函数中还有一处细节没有改过来:
在这里插入图片描述
原先的代码是基于一条蛇运动的情况下的,我们改成上图所示即可。

接下来就是策略的问题了。

这道题的本质其实就是在所有可行的方向找一个最优的方向,但这个最优又没有明确的指标,我们只能根据自己的直觉与经验进行安排。

我认为我们想要保证胜率,首先要保证存活率,尽量避免把自己绕死的和被对方把路封死的情况。

我们在第一问中已经讨论了往哪些方想走会死,我们需要从不会死方向中选择,但选剩下的方向就一定不会死么?只是不会造成直接死亡罢了,难免会出现当前这一步不会死但是会把自己后续的路封死的情况。
如下图,我们若要向下走去吃食物,若在吃完那个食物之后,把中间四个格子填满之前,游戏若没能结束,那我们一定会输。
在这里插入图片描述
那我们应该如何避免上述的情况呢?

我们可以分别遍历小蛇可以走的方向,判断这样走会不会造成把自己堵死,较为简单的判断方法为以新的头部的起点进行bfs,判断与新的头部相连通的点的个数,从而避免进入可活动范围小,会被自己填充满的区域。

在这里插入图片描述
如图向上走的话绿色的点都与新的头部相连通,有较大的活动范围。
在这里插入图片描述

而向下走经过bfs判断活动范围只有3个格子,四步之后会把自己堵死,因此把向下设为较低的优先级。

总结一下就是以目标点为头部(即头的新位置)为起点进行bfs计算可达点的个数。

防止被敌方和墙以及障碍物堵死也是同样的道理,唯一不同的是我们要尽量考虑到对手的能动性:我们既要判断敌方在当前状态下自己会不会被堵死,也要尽量考虑在自己做出选择后对手的下一个决策会不会把自己的路封死,且拥有倍速和斜走道具的对手具有更大的威胁性,可能会出其不意致使己方面临不得不自杀的情况。由于情况太多,我们能考虑到的情况有限,但还是尽量避免这种不得不自杀的情况比较好。

在这里插入图片描述
如图,若我们用之前的方法进行bfs得到的结果是当粉蛇向右走时,绿色的格子都是它的活动范围,而实际上当粉蛇向右走,而白蛇选择向上走,粉蛇下一步的活动空间为0,被迫自杀,如图,实际上向右走没有可达点。
在这里插入图片描述

因此我们可以在之前bfs的基础上加上对于对手的蛇可以到达的点的特殊判断,即不把必须通过可以被对手下一步封死的格子才能联通的格子作为活动范围,以确保不会走进陷阱,提高存活率。

通过上述的方法我们即可以尽量避免中途的死亡,提高存活率,接下来就是怎样取得胜利了。

我们可以避免被对方堵死,当然也可以用同样的方法去堵对方,即判断自己达到目标点后,以对方的头部为起点bfs,判断对方的活动范围,若可以有效限制对方的活动范围,则提高该方向的优先级,从而迫使对方自杀。

好了,数据结构课程设计的部分结束了,接下来就是玄学设计的部分了,由于无法确定对手的策略,也就无法找到准确的最优解,只能较为模糊的判断大致应该怎么走,我们究竟应该优先选择什么道具,吃食物时应该考虑着重考虑所到之处食物的密度还是着重考虑到最近的食物的最短路,应该用什么算法来进行最短路判断。。。这些就是仁者见仁智者见智的问题了,效果很难评估(有足够的时间和精力的话倒是可以自己写评估函数测试一下不同方法之间博弈的胜率)。

我们可以对每个方向设置一个优先级,地图中的每个道具和食物都对优先级产生影响,道具的影响程度大于食物,影响程度随距离递减(这里的距离可以采用最简单的曼哈顿距离,考虑障碍物的话可以通过bfs搜索,但效果很难说,递减可以是线性的,也可以是指数级别的,不确定那种更优),若判断对手可以比自己先到达食物或道具所在地,可以适当减少该道具或对优先级的影响。另外优先级也受活动范围之类的其他因素影响,影响因素和程度要靠直觉判断。

对于其他的思路,比如封堵,可以在拥有一定的优势的情况下,依靠拖时间来取得胜利,但这些方法难免需要一定的自身长度作为资本,前期需要积累足够大的优势,且编程复杂度高。因此存活和长度是最主要的,这些方法可以起到锦上添花的效果,但很难雪中送炭,有时间和精力的话可以实现以下作为优化策略。

封堵可以是对对手的封堵,即在自身包围圈内,当自身长度>对手长度+圈内食物数量时,可以使自己的头部一直追着尾部转圈。
也可以是对食物的封堵,即围住图中剩下的食物阻止地图刷新,这种方法感觉比前一种靠谱一点,因为不需要太大的前期优势即可采用,触发门槛较低,条件是自身长度>对手长度+包围圈外的食物。

经验教训:最好是在明确了思路后一气呵成,不要一边想一边写,不然会写的很乱,后续失去改的欲望。

由于时间有限,上述很多操作我都没有亲自实现,也不清楚实际效果,只负责口胡,只做了最简单的实现,即可成功运行且避开大部分的坑。
在这里插入图片描述

发布了45 篇原创文章 · 获赞 8 · 访问量 4007

猜你喜欢

转载自blog.csdn.net/qq_44290978/article/details/104953910