在本文中,我将分析第七(最终难度)水平,并分享游戏获胜者的决定*。
*为玩家澄清。 QA Game分两个阶段推出:六月和七月。从第一流开始,亚历山大就一直得分最高,因此在本文中我们分析了他的结果。其余的领导人可以在这里看到。
“内部”是什么:Ace.js库用于游戏中的代码编辑器;语法高亮和自动完成功能可在其中使用; webWorker用于在客户端执行代码(受本文启发)。后端是用Python和Flask编写的,并部署在Heroku上。总共花了大约2个月来编写游戏。
当我编写质量检查游戏时,我还没有Ace.js和webWorkers的经验,尝试它们很有趣。如果您想制作类似的游戏,那么我建议您考虑一下:
QA游戏传奇
在游戏中,您需要控制角色ZERO2,该角色能够测试,搜索和修复错误。控制使用JavaScript代码完成,ZERO2拥有自己的SDK,从而大大简化了编程。
例如,要测试该级别上所有可用的功能,您需要运行以下代码:
let result = scan();
for (f of result.features) {
smoke_test(f);
}
然后修复在测试期间发现的所有错误,这是:
result = scan();
for (b of result.bugs) {
fix_bug(b);
}
游戏中的每个新关卡都包含其他功能,并且需要使用更复杂的算法;每种方法的详细分析发布在GitHub上。在本文中,我将详细分析等级7,因为它确定了哪个玩家将获得最大积分。
如何获得最高积分?游戏创作者版本。
在第7级,玩家需要修复并验证120秒内的最大错误数量,同时:
- RUN按钮只能被按下60次;
- 120秒后,算法自动结束,不再授予积分(在前端和后端均进行了验证);
- 对于每个固定的错误,将给予100分,对于经过纠正和验证的错误-150分;
- 每次启动RUN时,所有点都会重置,并且会随机生成新的错误。
要获得最大点数,您需要分析影响结果的因素:
- 简化代码。有必要删除所有不必要的结构并编写清晰的代码,并检查是否存在循环。由于代码错误,许多参与者失去了分数,从而导致无休止的空循环。
- 减少对请求的响应时间。每种SDK方法都会向服务器发出一个请求,平均每个请求需要200-400毫秒。为了减少这个数字,您需要找到合适的服务器并从中执行查询。
- 算法优化。在大多数情况下,都需要找到重现该错误的步骤(函数research_bug)。因此,您需要考虑如何优化算法,以便以最少的尝试次数找到解决方案。
- 该算法的“并行化”。标准启动发生在一个线程(一个webWorker)中,并且所有API方法都是同步的。您可以尝试“并行化”算法。您还可以查看是否有可能使某些方法异步(扰流器警报:可以)。
算法优化
如果指定的播放步骤不正确,那么research_bug(bug_id,步骤)函数将返回0;如果指定的播放步骤是正确的步骤组合的开始,则返回1;如果指定的步骤是再现该错误的完整步骤的组合,则返回100。
选择播放步骤的算法可能如下所示:
function find_steps(bug_id) {
let path = '';
let result = 0;
while (result != 100) {
path += '>';
result = investigate_bug(bug_id, path);
if (result === 0) {
path = path.slice(0, -1);
path += '<';
result = investigate_bug(bug_id, path);
}
}
};
如果对于特定的序列,当接收到“ 0”时,您没有重新检查相同的序列,而是替换了最后一个字符,则可以加速此功能。相反,您需要立即在字符串中添加另一个字符并检查结果是否有换行符。
这是什么意思?使用此算法可以“节省”对research_bug的调用次数(尽管并非在所有情况下都可以更快地工作):
function find_steps2(bug_id) {
let path = "";
result = 0;
prev_result = 0; // ,
// 0,
//
while (result != 100) {
result = investigate_bug(bug_id, path + ">");
if (result === 0) {
if (prev_result === 0) {
result = investigate_bug(bug_id, path + "<");
if (result > 0) {
prev_result = 1;
path += "<";
} else {
// 0,
// path
// 100 1
result = investigate_bug(bug_id, path);
}
} else {
prev_result = 0;
path += "<";
}
} else {
prev_result = 1;
path += ">";
}
}
让我们比较一下结果:
正确的播放步骤 | find_steps函数中对research_bug的调用次数 | find_steps2函数中的research_bug调用数 |
---|---|---|
>> | 2 | 2 |
<< | 4 | 6 |
<<< | 6 | 五 |
>> << >> | 8 | 7 |
<<<<<< | 12 | 12 |
也许您可以找到更好的算法?
“并行化”错误工作的执行
这可以通过两种方式完成:
- 创建新的webWorkers,然后将JavaScript代码传递给他们:
let my_code = "console.log('Any JS code which you want to run');"; let bb = new Blob([hidden_js + my_code], { type: 'text/javascript' }); // convert the blob into a pseudo URL let bbURL = URL.createObjectURL(bb); // Prepare the worker to run the code let worker = new Worker(bbURL);
使用这种方法,剩下的就是解决彼此之间同步流的问题,在这里您可以使用fix_bug(bug_id)函数属性-如果该函数返回“ 0”,则该bug尚未修复。 - 查看JS中SDK方法调用的所有API方法,并使用自己喜欢的编程语言编写自己的脚本。这种方法之所以有用,是因为您具有完全的操作自由度,可以在多个线程中轻松运行解决方案的能力,可以从服务器运行自己的脚本的能力,这对于网络请求具有最小的延迟。
异步功能
在分析完所有SDK函数之后,您可以看到可以通过简单地重写游戏中使用的标准函数来使fix_bug和verify_fix函数异步化:
function verify_fix(bug, path) {
let xhr = new XMLHttpRequest();
// - true - ,
xhr.open('POST', "https://qa.semrush-games.com/api/verify_fix", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send("bug=" + bug + "&path=" + path);
}
function fix_bug(bug, path) {
var xhr = new XMLHttpRequest();
xhr.open('POST', "https://qa.semrush-games.com/api/fix_bug", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function () {
if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
if (this.response.toString().length > 3) {
// , :
verify_fix(bug, path);
}
}
};
xhr.send("bug=" + bug.toString());
}
如何获得最高积分?优胜者版本。
亚历山大以28,050分成为获胜者。他讲述了自己如何做到这一点,然后讲述了第一人称视角。
当我加入游戏时,参与者仍然很少(少于10人)。经过几次尝试,我的程序获得了超过11,000分,并遥遥领先。
但是由于解决方案本身很琐碎,所以我意识到我不会长期待在第一位,所以我开始考虑如何改进程序。
首先,我研究了对工作速度影响最大的因素,结果发现对服务器的请求占用了99%的时间。每个请求大约花费110-120毫秒。因此,有3个主要选项可用于加速程序:
- 改进算法并减少对服务器的请求数量;
- 使用对服务器的异步请求;
- 减少一个请求的时间。
我拒绝第二种选择,因为它会超出问题和原始同步API的范围。
有几种方法可以减少对服务器的请求数量,但是所有这些方法都只增加了很小的一部分(总计百分之几十)。
因此,我开始考虑如何减少一个请求的时间。我查看了游戏服务器的部署位置,结果发现它位于都柏林的AWS中(从我所在的城市> 100ms到都柏林)。首先,我想在该数据中心租用一台服务器,然后直接从下一机架运行该程序。但是由于我在德国有免费的服务器,所以我首先决定从那里运行该程序。
我安装了DE,VNC,Firefox,启动了该程序-降低ping可以立即使获得的积分增加2倍。而且由于与其他地方的差距很大,所以我决定不再进一步改善结果。
这是一个故事。
参加者的常见错误
作为总结,我将分享一些典型的错误,这些错误使参与者无法获得更多的积分:
- 在相同的已修复错误列表上无休止地循环。如果算法不记得已经修复的错误,并对其进行了多次修复,则会浪费时间。
- 循环中的错误,选择错误的播放步骤。结果,循环变得无止境。在寻找重播步骤时,许多贡献者使用100个字符的限制,尽管重播错误的最大行长为10个字符;
- 并非所有参与者都尝试多次运行其算法,并且如果您将同一算法运行2-3次,则由于错误分布不同以及其他重现错误的顺序不同,您可以获得更多的积分。
我很乐意回答有关游戏的问题,并查看解决第七关的选项。