【发布时间】:2016-12-20 23:23:22
【问题描述】:
在 Screeps 中,每个玩家对 CPU 的使用都是有限的,但 documentation for this feature 并没有使这种强制执行的方式足够清晰,以便编写 CPU 限制健壮的代码。我考虑了以下四种可能性:
1。玩家的循环永远不会被打断。
在一种极端情况下,播放器的内存反序列化、主脚本执行和内存重新序列化永远不会中断,超过 CPU 限制仅仅意味着玩家的循环将在后续滴答中被跳过,直到 CPU 债务被偿还。在这种情况下,CPU 限制健壮代码并不是绝对必要的,但它仍然有助于检测玩家的循环何时被跳过并可能开始更有效地做事。这可以使用如下代码轻松实现:
module.exports.loop = function()
{
var skippedTicks = 0;
if ( 'time' in Memory )
{
skippedTicks = Game.time - Memory.time - 1;
}
// Main body of loop goes here, and possibly uses skippedTicks to try to do
// things more efficiently.
Memory.time = Game.time;
};
这种管理玩家 CPU 使用率的方式很容易被无限循环滥用,我几乎可以肯定这不是 Screeps 的行为。
2。玩家的循环是原子的。
下一个可能性是玩家的循环是原子的。如果超出 CPU 限制,则玩家的循环被中断,但既不提交预定的游戏状态更改,也不提交对内存的更改。当检测到中断循环时提高效率变得更加重要,因为忽略它意味着玩家的脚本将无法更改游戏状态或内存。但是,检测中断的循环仍然很简单:
module.exports.loop = function()
{
var failedTicks = 0;
if ( 'time' in Memory )
{
failedTicks = Game.time - Memory.time - 1;
// N failed ticks means the result of this calculation failed to commit N times.
Memory.workQuota /= Math.pow( 2, failedTicks );
}
// Main body of loop goes here, and uses Memory.workQuota to limit the number
// of active game objects to process.
Memory.time = Game.time;
}
2.5。对 Memory 的更改是原子的,但对 Game 对象的更改不是。
编辑:在阅读RawMemory 对象的文档后,我想到了这种可能性。如果脚本被中断,任何已经安排好的游戏状态更改都会提交,但不会提交对内存的更改。考虑到 RawMemory 提供的功能,这是有道理的,因为如果脚本在运行自定义内存序列化之前被中断,则会运行默认的 JSON 序列化,这会使自定义内存序列化更加复杂:自定义反序列化需要能够处理除了自定义序列化编写的任何格式之外的默认 JSON。
3。 JavaScript 语句是原子的。
另一种可能性是播放器的循环不是原子的,但 JavaScript 语句是原子的。当玩家的循环因超过 CPU 限制而中断时,会提交不完整的游戏状态更改和内存更改,但需要仔细编码 - 进行 Screeps API 调用的语句必须将调用结果分配给内存键 - 游戏状态更改和内存更改不会相互不一致。为这种情况编写完全受 CPU 限制的健壮代码似乎很复杂 - 这不是我已经解决的问题,我想在尝试之前确定这是 Screeps 的真实行为。
4。没有什么是原子的。
在另一个极端,甚至单个语句都不是原子的:将 Screeps API 调用的结果分配给内存中的键的语句可能会在调用完成和分配结果之间被中断,并且不完整的提交游戏状态更改和不完整的内存更改(现在彼此不一致)。在这种情况下,编写 CPU 限制健壮代码的可能性非常有限。例如,虽然以下语句写入 Memory 的值的存在无疑表明 Screeps API 调用已完成,但它的缺失并不表明调用未完成无疑:
Memory.callResults[ Game.time ][ creep.name ] = creep.move( TOP );
有谁知道其中哪些是 Screeps 的行为?还是我没有考虑过的其他事情?文档中的以下引用:
CPU 限制 100 意味着在 100 毫秒后执行脚本将被终止,即使它尚未完成某些工作。
暗示可能是案例 3 或案例 4,但不是很令人信服。
另一方面,在模拟模式下的实验结果,其中包含单个蠕变、以下主循环,并在对话框中为无响应脚本选择“终止”:
module.exports.loop = function()
{
var failedTicks = 0;
if ( 'time' in Memory )
{
var failedTicks = Game.time - Memory.time - 1;
console.log( '' + failedTicks + ' failed ticks.' );
}
for ( var creepName in Game.creeps )
{
var creep = Game.creeps[ creepName ];
creep.move( TOP );
}
if ( failedTicks < 3 )
{
// This intentional infinite loop was initially commented out, and
// uncommented after Memory.time had been successfully initialized.
while ( true )
{
}
}
Memory.time = Game.time;
};
是因为 failedTicks 达到了它的阈值,creep 只在无限循环被跳过的滴答声上移动。这指向案例 2,但不是结论性的,因为模拟模式下的 CPU 限制与在线不同 - 它似乎是无限的,除非使用对话框的“终止”按钮终止。
【问题讨论】:
-
我尝试生成 Creep,然后通过无限循环强制超出我的 CPU 限制。我的循环确实终止了,我的小兵最终产卵了。这似乎排除了#1 和#2,但没有区分#3 和#4。
标签: javascript screeps