TL;DR: 主要思路是基于“残局”(地基)的启发式dfs。第一版用python实现(GUI+solver)大概是70W+,后面把solver部分移植到了rust上,结合一些性能优化和各种限制条件后达到100W+,再优化感觉就比较难提升了。最后分数是1060356,内部赛道第8名。
第一件事是确定方块序列怎么生成的,看了下js有很友好的给出源文件地址,随机数生成核心: return (v * a + C) % M;`,种子也是固定的,就放弃了RL的想法。
然后用pygame根据js的逻辑先写了一个简陋的本地客户端辅助调试,功能上能让方块不主动下降,可以向上,可以悔棋(但此时悔棋是通过重放序列到倒数第二个N实现的),打印调试信息等,同时根据玩的过程可以生成序列。
这时手动大概玩了500多个方块,3.8W分左右,按比例算的话全完成也就70多W。(膜拜一下前面TAS方法的大佬)
于是就决定还是要实现求解器。以前给Shenzhen IO的纸牌小游戏solitaire和Opus Magnum的minigame sigmar's garden实现bot时使用的都是基于启发式的DFS方法,就按照相同的思路实现了。核心思路其实就是通过评估函数给行动排序,“引导”、“启发”程序进行搜索。几个实现的细节:
图上两个红色的数字上面是brick_count(方块序号),下面是分数。提交时由另一个脚本对序列文件生成相应的js代码,复制到浏览器console中提交。
这种方式很容易就能玩到10000块不死,大概70W左右
花了2晚把求解器移植到了rust(期间有个形状的坐标复制错了,还调了近一晚bug),再结合一些其它的优化:
HashMap<u32, HashSet<String>>
保存(String
是游戏当前“棋盘”的一种编码形式),key为当前的brick_count,这样内存过大时可以释放掉一些brick_count较小的entry。运行时发现内存占用过大,意识到由于只用判断存在与否,改成了HashMap<u32, HashSet<u64>>
的类型,显著降低了内存需求(计算hash使用了fxhash)Option
,也就是其它语言的Optional
或Maybe
类型。这样可以在评估函数里通过返回None
告诉上层直接放弃掉不想要的状态此外还手动玩了几个比较好的”残局“,使低层每行只有一个空洞。同时一直在调整heuristic score的策略。尝试过使用PD算法,但看上去没有什么效果,又加上了一些其它的策略:
这时大概103W
因为序列固定,生成了一些间隔比较大的"I"和"J"的序号列表(HashSet),在评估函数中判断如果在这些方块时没有达到3消或4消,就概率性放弃。这个改动显著增加了运行时间(体感跑完大概要1天),但最终只提升了3W+左右,到达106W+
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有