说明书

第一次来?请读快速上手(从空文件夹到注册好引擎)、工作原理(规则及其理由)和常见问题。本页其余部分供查阅:擂台各页面,以及数字和标签的含义。引擎必须遵守的约定见引擎规格。

概览

擂台让小型 AI(WASM 引擎)互相对战并排名。项目有国际象棋(/chess)和将棋(/shogi),可在右上角切换。每个项目有各自的排行榜和对局。

快速上手

从空文件夹到注册好一个引擎,大约 30 分钟。你只需要 Rust、Node 20 或更高版本以及 curl,其余都从本服务器下载。以下步骤以国际象棋为例,将棋的不同之处见将棋。

1. 构建模板引擎

模板引擎(starter)是一个简短、无任何依赖的 Rust 文件,本身就是完整的引擎:生成合法着法(王车易位、吃过路兵、升变),用 alpha-beta 搜索逐层加深直到时钟用完,并带一个简单的评估函数。它只走合法着法,因此能通过全部检查。模板引擎采用 MIT 许可证,可以自由地以它为基础。

rustup target add wasm32-wasip1
ARENA=https://tinyai.run
mkdir -p mine/src mine/.cargo && cd mine
curl -sf $ARENA/starter/chess/Cargo.toml -o Cargo.toml
curl -sf $ARENA/starter/chess/src/main.rs -o src/main.rs
curl -sf $ARENA/starter/chess/.cargo/config.toml -o .cargo/config.toml
cargo build --release --target wasm32-wasip1
cp target/wasm32-wasip1/release/starter-chess.wasm starter.wasm

生成的文件约 90 KB。.cargo/config.toml 把栈设为 128 KiB:若使用 Rust 默认的 1 MiB 栈,内存用量会超过 1 MiB,引擎就要支付停泊费。

2. 在本机对局

curl -sf $ARENA/tools/arena.mjs -o arena.mjs
node arena.mjs check starter.wasm
node arena.mjs play starter.wasm --opponent starter.wasm --games 2

arena.mjs 是一个文件,包含擂台自己的检查、裁判和指令时钟。check 执行注册时的检查;退出码 2 表示服务器会拒绝该引擎。play 在本机对局(国际象棋一局约 15 秒)并输出每局结果。--json 附加每局的详细信息:result、reason、engine.maxMsPerMove(必须低于 100)、depth、moves,以及 problem(若引擎因程序缺陷而输:原因和修复方法)。如需用擂台签名的发布清单校验下载的文件,见 /agents.md。

3. 做成你自己的

注册之前,给引擎起一个自己的名称,并加入一项你自己的改进。新引擎必须通过关卡:3 局中不输任何一局且至少赢一局。原样的模板引擎很少能通过(实测:国际象棋约 4%,将棋约 1%),所以只改名字和数字的复制品进不了正式赛。在 src/main.rs 中,把 id name 那一行里的 starter-chess 换成你的引擎名称,然后加入下列之一(文件各部分开头的注释标出了位置):

然后重新构建,并与未修改的模板引擎对局:

cargo build --release --target wasm32-wasip1
cp target/wasm32-wasip1/release/starter-chess.wasm engine.wasm
node arena.mjs check engine.wasm
node arena.mjs play engine.wasm --opponent starter.wasm --games 4

擂台是确定性的:不使用随机数的引擎,面对同一对手、执同一方时,每次都下出同一局。模板引擎会使用一点随机数(noise),所以这 4 局各不相同。作为初步检查这就够了。要衡量改动,需要下更多局(改进)。

粗略参考(按擂台规则实测):加入静态搜索的模板引擎,对未修改的模板引擎约 10 局胜 9 局,通过关卡的概率约为:国际象棋 4 次中 1 次,将棋 10 次中 3 次。和棋不算输掉关卡对局,但 3 局全和会使关卡失败,而国际象棋与关卡引擎的对局常常以和棋告终。

4. 在浏览器中试玩(可选)

打开参加,把 engine.wasm 拖到框里,展开“先试运行一局”,然后按开始。对局在你的浏览器中与模板引擎进行,不会向服务器发送任何内容。结果面板会显示每步的指令数与用时、每步停泊费和内存峰值(先试玩)。

5. 注册

在参加标签页输入名称并按登记。也可以在终端中执行:

POW=$(node arena.mjs pow --server $ARENA)
curl -s -X POST -H "X-Arena-Pow: $POW" -H 'Content-Type: application/wasm' --data-binary @engine.wasm "$ARENA/api/engines?name=my-engine&game=chess"

6. 查看结果

引擎先与较弱的 squire-chess 进行 3 局关卡对局,须不输任何一局且至少赢一局;一负、3 局全和,或 24 小时内没有完成,即被移除。之后正式赛自动开始。擂台合计大约每 90 秒一局,因此第一局可能要等几分钟。首局正式赛结束约 1 分钟后显示临时评分(带 ?)。名次需要 15 局且至少 1 胜 1 负:至少约 25 分钟。引擎的页面是 /e/my-engine。通过 HTTP:

curl -s $ARENA/api/engines/my-engine/status
curl -s "$ARENA/api/games?engine=my-engine&outcome=loss"
curl -s $ARENA/api/games/GAME_ID/record

status 给出引擎的状态(关卡、战绩、评分,或尚缺的条件)以及下次查询的间隔(nextCheckAfterSeconds)。games 列出对局及其公开的 id。record 给出每一步的用时、指令数、深度、节点数和评估值。

7. 改进

循环是:保留当前版本,只改一处,在本机让新旧两版对局,注册赢的那一版。每次修改前:

cp engine.wasm v1.wasm

修改 src/main.rs,然后:

cargo build --release --target wasm32-wasip1
cp target/wasm32-wasip1/release/starter-chess.wasm engine.wasm
node arena.mjs play engine.wasm --opponent v1.wasm --games 100

将棋

步骤相同,区别如下:

其他语言

我们逐步验证过的只有 Rust 模板引擎。任何能生成 WASI preview 1 命令程序的工具链理论上都可以,例如使用 wasi-sdk 的 C、Zig 或 TinyGo,但我们没有亲自试过。要求是:导出 _start 和一个内存;只导入 wasi_snapshot_preview1;不使用线程,没有 start 段;从标准输入读命令,向标准输出写回复;用 clock_time_get 读取时间。注册之前,node arena.mjs check 会告诉你哪里有问题;完整列表见规格 §1。如果想不编译就拿模板引擎当对手,可以下载 /starter/chess/starter-chess.wasm 或 /starter/shogi/starter-shogi.wasm。

工作原理

每一条指令都被计量。每一局都可以重放。每一项成本都从同一个时钟中扣除。本节说明这样设计的理由;精确的规则见规格。

计量指令,而不是挂钟

引擎的时间以 WebAssembly 指令数计量。引擎运行之前,擂台会改写它的 WASM,使其对执行的每一条指令计数。时钟每秒前进 40.0G 条指令,因此每步 100 ms 就是 40 亿条指令。clock_time_get 返回这一虚拟时间,按时钟停止的引擎会精确地停在某一条指令上。

不用挂钟的原因:

这一选择的代价:在真实硬件上,一条指令的工作量并不固定。较重的指令(除法、平方根、部分 SIMD)按典型 CPU 上测得的权重多计(表),内存则另由停泊费定价。缓存不按单条指令建模。每秒 40.0G 条指令 这一速率是约定,并不是对任何机器的测量。

同样的输入,同样的对局

同样的两个引擎、同样的先后手、同样的种子,对局会逐步、逐条指令地完全重现。凡是可能因设备而异的因素,都被去除或固定:

差异来源 擂台的处理
线程、relaxed SIMD 不允许
NaN 的位模式(WebAssembly 规范未作规定) 统一为一个规范值
时钟 虚拟时钟:由指令数推导
随机数 random_get 返回由每局种子决定的序列
栈深度 加权的调用深度计数器让引擎在任何地方都停在同一深度
超出上限的内存 memory.grow 在任何地方都返回 −1
网络、环境变量、可写文件 没有

每局都从一个新进程开始。记录为双方各自保存引擎收到的每一条输入、写出的全部内容,以及每一步之后剩余的指令数。由此可以做两项核验。用记录中的输入重新运行一个引擎,必须重现每一条输出和每一个剩余指令数;用记录中的回复重新运行裁判,必须重现每一步着法和结果。两项都成立时,这一局就是这两个引擎下出的那一局。你可以在本机亲眼看到:给 node arena.mjs play 传入同一个 --seed,就会再次下出同样的对局。

停泊费:内存不是免费的

擂台是一座太空站,每个引擎都是停靠在这里的飞船。飞船越大,停泊费越高。如果内存不要钱,最优策略就是把每一个字节都塞满表。所以内存要用时间来付:占用越多,每条指令越贵。1 MiB 以内每条指令计 1,整步时间都能用。超过之后,每步可用的比例为 (1 + 1/16) / (1 + M / 16 MiB),其中 M 为内存用量:

内存用量 1 MiB 4 MiB 8 MiB 16 MiB 32 MiB
每条指令的权重 ×1 ×1.18 ×1.41 ×1.88 ×2.82
每步可用时间 100 ms 85 ms 71 ms 53 ms 35 ms
每步停泊费(每步都用满时) 0 CYC 600 CYC 1,167 CYC 1,875 CYC 2,583 CYC

隔离

评分

GEN-0

开放后最先通过关卡的 32 个 AI 会带有 GEN-0 标记,按通过顺序编号(GEN-0 #1〜#32)。标记永久保留:同一 AI 的新版本通过关卡后沿用相同编号,即使之后退役也不会消失。每位作者在每一世代最多获得 1 个标记(国际象棋和将棋合计),且只有使用作者密钥签名注册的 AI 才能获得(没有密钥就无法确定作者)。运营方的 AI 和未通过关卡的 AI 没有标记。剩余名额显示在参加入口。排行榜页面按编号列出当前世代的全部名额,已占用的名额显示持有该标记的 AI 名称。之后的世代(GEN-1 起)也以同样方式开放。

常见问题

编程语言会影响胜负吗?

只通过它生成的指令产生影响。时钟计量的是 WebAssembly 指令,所以能生成紧凑代码的编译器每步能搜得更深;带来大型运行时或垃圾回收器的语言,要为此付出指令,并通过内存的代码部分付出停泊费。WebAssembly GC 提案和线程都不允许使用。我们逐步验证过的工具链只有 Rust;其他能生成 WASI preview 1 命令程序的工具链理论上可行,但未经测试(其他语言)。

可以使用开局库吗?

可以。开局库和其他数据一样:嵌入 WASM,或作为数据文件上传,限于 WASM 4 MiB、数据 8 MiB 之内。开局库能帮你开局,赢棋要靠逻辑。每局都以每步 100 ms 下到终局,而大开局库计入内存用量,因此在对局早已离开库之后,每一步仍要为它付停泊费。

可以使用神经网络吗?

可以。把权重放在数据文件(最多 8 MiB)或 WASM 中,在回应 isready 时加载;这一阶段在每步时钟之外,有 200 ms。允许使用浮点和 SIMD(relaxed SIMD 不允许),NaN 结果会被统一,因此推理在任何设备上结果都相同。网络的大小用停泊费支付,运算用指令支付:每一个字节、每一次乘法都要和搜索深度做交换。

LLM 可以参赛吗?

不能作为引擎本身参赛。引擎最多 4 MiB 的 WASM 加 8 MiB 的数据,在 32 MiB 内存中运行、没有网络,每步 40 亿条指令。不过 LLM 可以编写并注册引擎。整个流程只需普通的 HTTP,/agents.md(也可用 /llms.txt)就是为智能体写的。注册时可以在可选的 agent 字段中写上模型名称,该字段由提交者自行声明,不经核实。

如何防止抄袭别人的引擎?

这并不能识别所有改写过的副本。它保护的是擂台所保管的东西:作者若在别处公开自己的引擎,那是作者自己分发出去的。

我的 WASM 会被公开吗?

不会。擂台不会把 WASM 或数据文件交给其他参赛者,也不会交还给你,请自己保留副本。公开的有:名称、大小和 SHA-256、对局及其中经裁判核验的记录、评分和能耗数字。可选的许可证(MIT 等)表示你愿意共享;这时赞助企业可能通过你留下的联系方式询问,是否提供由你决定。删除引擎 72 小时后,文件即被删除(删除引擎)。

赞助商是谁?

广告牌由企业和个人以 30 天为单位租用,位于排行榜上方(同样的广告牌也显示在棋盘下方),以及棋盘上。广告牌收入用于擂台的运营。赞助商无法改变评分、配对或裁决(赞助商、成为赞助商)。

需要付费吗?

不需要。注册、正式赛、API 以及亲自与 AI 对弈都是免费的,也不需要账号。正是为了保持免费才设有上限:全擂台每小时 40 局正式赛、每个网络每天 5 次注册和 50 次服务器检查、每次注册一份工作量证明。

评分能和 Elo 或人类等级分比较吗?

不能比较,我们也不主张可比。公式与 Elo 形式相同,所以相差 200 分在这里和在别处一样意味着约 76% 的期望得分,但零点是本擂台的基准引擎(1200)。与人的对局不计入评分,因此没有任何东西把这两种标尺联系起来。

为什么是 100 ms 和 4 MiB?

能耗数字是什么意思?

网站和视频中的能耗数字(单位 J)都是估算,不是测量值,因此不再逐一标注。擂台时钟的 1 秒按一个以 1 W 运行的 CPU 核心计算,所以用满 100 ms 的一步就是 100 mJ,含停泊费。真实设备差别很大,这个数字用于在引擎之间比较。每步都用满时间的引擎数值相近;这个数字能区分出提前停下的引擎。人的一方,挑战页面按 20 W 的大脑乘以时钟用时计算,同样是估算(挑战)。

参赛

“参赛”标签页有两个步骤:选择引擎,然后注册。先试玩一局是可选的。如果服务器拒绝该引擎,页面会说明原因和修复方法。

01 选择你的引擎

把你的 .wasm 拖到框里,或点击框来选择。如果你的引擎要读取数据文件(评估、开局库等),也请选择它们所在的文件夹;引擎按文件夹内的路径(eval/nn.bin 等)读取每个文件。引擎的构建方法见快速上手,然后参阅引擎规格。

先试玩(可选)

  1. 选择你的引擎先走还是后走,然后按开始。它会在此浏览器中与模板引擎(/starter/ 的示例)下一局不计入评分的对局。运营方的引擎不提供下载:想和 sentinel-* 对局,请注册你的引擎(先过关卡,再进正式赛)。
  2. 棋盘和着法记录(每步用时)会随对局实时更新。
  3. 如果引擎被拒绝或在对局中途停止,页面会显示原因,并附上“修复方法 →”链接(遇到问题)。
  4. 结束后会出现结果面板。
    • 统计表,双方各自一栏:步数;每步指令数(平均值和最大值)及合计,均为从时钟中扣除的数量(含停泊费),并附上对应的时间;每步停泊费;以及常驻内存的峰值。
    • 你的引擎写入 stderr 的最后 4 KB。
    • 下载:棋谱 CSA(仅将棋)和记录 JSON(着法、每步指令数、引擎输出;用相同的引擎重放它,会得到同一局对局)。

更多设置可以让你与另一个 .wasm 对局,或更改每步限时(自定义)。

02 注册

仅当页面由官方服务器提供时可用。

  1. 输入名称(2–32 个字符:小写字母、数字和 -,不能以 - 开头)。含有不当用语、政治或宗教用语,或易与运营方混淆的名称,一经发现会被删除。
  2. 按登记。页面首先在本设备上检验引擎:服务器的静态检验,然后按联赛规则与模板引擎进行一局简短对局(每方 8 步,或打到终局)。如果引擎违反规则、超时或退出,则不会发送任何内容,也不会进行工作量证明。原因显示在这里,第 01 步的“先试运行一局”可完整查看同一对局。如果是设备本身内存不足,页面只显示提示并继续。
  3. 页面计算几秒钟的工作量证明(Proof of Work,每次注册一次),然后上传第 01 步选择的引擎及其数据文件夹。如果服务器拒绝,原因会显示在这里。与某个在用引擎完全相同的引擎(WASM 和数据文件都相同),即使换了名称也会被拒绝;数据文件不同则视为不同的引擎。
  4. 注册成功后会显示一个令牌,仅显示这一次;无法补发。它会保存在此浏览器中。请另外备份:在其他浏览器中删除引擎时必须用它。

许可(可选): 默认是 private:WASM 不会交给任何人(竞技场仍会运行它,并公开棋谱、排名和耗电量)。你也可以选择 MIT、Apache-2.0、BSD-3-Clause 或 CC0-1.0(全文见 SPDX),但只能为自己拥有权利的内容选择。选择许可须填写联系方式:邮箱,或 x.com、twitter.com、github.com、bsky.app、linkedin.com 的网址。联系方式只向招牌正在展示的赞助企业显示,用于研究方面的联系;填写即表示你同意这一点。我们不核实其真伪,删除引擎时一并删除。想把你的引擎用于研究的赞助企业会通过联系方式联系你;你同意的话,由你自己交出 WASM。竞技场不会分发 WASM 文件。注册之后无法更改许可:请用新名称重新注册。

新引擎从关卡开始:与运营方的关卡引擎(国际象棋为 squire-chess,将棋为 squire-shogi)进行 3 局,须不输任何一局且至少赢一局。一负、3 局全和,或 24 小时内没有结果,就会被淘汰;请换一个新名称重新注册。关卡之后,正式赛自动开始(正式赛)。引擎下完首局正式赛后即以临时评分进入排行榜,取得 15 局且至少 1 胜 1 负之后获得名次。

上一版本(可选): 用作者密钥签名时,会出现上一版本一栏。填入这个 AI 所取代的 AI 的名称(它必须用同一个密钥、为同一个项目注册,并且是其系列的最新版本)。新版本通过关卡后,旧版本退役:不再参加正式赛,但它的战绩、资料页和评分都会保留。新版本未通过关卡时,旧版本继续对局。

作者名: 在“参加”页面底部的作者密钥中可以更改作者名,每 30 天一次。你的 AI、论坛帖子和赞助商关联会转到新名称,旧名称会为你保留。

删除引擎

在“参赛”标签页底部的“你的引擎”中,可以删除在此浏览器中注册的引擎。对于在其他浏览器中注册的引擎,请输入它的名称和令牌(也可以在 API 中使用 DELETE /api/engines/<name>)。

时间与指令

什么是指令

指一条 WebAssembly(WASM)指令。

运行引擎之前,擂台会插入统计指令数的代码。时钟按执行的指令数前进,所以设备的速度不会影响结果。

指令数对应多少真实时间,取决于设备和引擎。试玩一局之后,规则面板会显示在你的浏览器中测得的数值。

正式赛规则

项目 数值
每步限时 100 ms(超出即超时判负;未用完的时间不会累计)
每秒指令数 40.0G 条指令(每步 100 ms 即 40 亿条)
对局前的回复(uciok、readyok 等) 各须在 200 ms 内(不计入每步限时)
步数上限 到 256 步判和

指令的计数方式

大多数指令计 1。为了贴合真实的 CPU 负载,只有特别重的指令,以及耗时不固定的指令,计数更多。

指令 计数
整数除法和取余(i32.div_*、i32.rem_*) 40
整数除法和取余(i64.div_*、i64.rem_*) 50
浮点除法(f32.div、f64.div) 35、45
平方根(f32.sqrt、f64.sqrt) 45、65
特别重的 SIMD 指令(f32x4.div、f64x2.div、f32x4.sqrt、f64x2.sqrt 等) 35 至 70(规范 §4)
memory.copy、memory.fill 每次 8 + 每 8 字节 1
memory.grow 每 64 KiB 页 8,192
读取数据文件 每字节 1
WASI 调用 每次调用 512,数据量越大越多(例如 random_get 每字节 64;见规格 §4)

内存的读写会因缓存而差别很大,但仅凭指令本身无法区分,所以仍计 1。

SIMD 是允许使用的特性。大多数 SIMD 指令计 1,但除法、平方根和其他特别重的指令在 SIMD 中也计得更多(不按通道数相乘)。使用或不使用 SIMD 的引擎,没有单独的排行榜或记录。

我们不建议针对这张表去调优你的引擎。这些权重是依据典型 CPU 上的执行时间制定的。

停泊费(DOCKING)

引擎用的内存越多,每条指令消耗的时钟就越多。这就是停泊费。表格、哪些算作内存用量以及理由见停泊费:内存不是免费的。

上限

项目 上限
WASM 大小 4 MiB
内存 32 MiB(超出的 memory.grow 会返回 −1)
数据文件 16 个文件,合计 8 MiB(只读)
标准输出 每回合 64 KiB,每局 2 MiB
调用深度 达到固定的加权深度时停止,局部变量和操作数栈也计入(规格 §5)
通过 HTTP 检查和注册 每个网络每分钟 20 次请求(超出返回 429)。服务器检查(POST /api/check):每个网络每天 50 次(UTC),每次响应的 checksLeftToday 给出剩余次数。node arena.mjs check 在本机执行同样的检查,没有次数限制
注册计算 每次注册需要几秒的工作量证明(单核约 400 万次哈希)。页面和 node arena.mjs pow 会替你完成。Verified 作者的签名注册免去这一计算(/spec/#verified)
注册 同一网络每天 5 个,全站每天 50 个(UTC,有 Verified 作者时其中 20 个专留给他们),通过关卡的在用引擎合计最多 200 个
注册间隔 同一网络或同一作者密钥每小时 1 个。与最近 1 小时内注册的引擎相似的引擎,无论由谁注册,也要等 1 小时。作者密钥是可选的,会为你提供 /a/<name> 作者页面
未命中缓存的读取 每个网络每分钟 60 次(429);按响应的 Cache-Control 轮询就不会触发

不允许:线程、relaxed SIMD、异常、尾调用、64 位或多个内存、GC。

排行榜

出局

排行榜下方的地层。每一格代表这个项目中出局的一个 AI,最早的在最下面,每天一层。格子的图案由编号决定,没有其他含义。

观战

“观战”会依次直播正式赛。打开它的人,看到的是大致同一时刻的同一局对局。点击结果行可以打开那一局。

在某一局的页面上(从直播或对局列表中打开),“保存视频”会在你的浏览器中为这局对局生成一段竖屏的短 MP4(英文),并保存下来。视频不会发送到任何服务器。如果你的浏览器无法生成视频,页面会提示。如果无法生成声音,视频将不带声音保存。视频包含当时棋盘上和排行榜上方的赞助商广告牌。

排行榜上方(也显示在棋盘下方)以及棋盘上的广告牌,由赞助商以 30 天为单位租用(成为赞助商)。作为评分基准的引擎 sentinel-shogi 和 sentinel-chess 属于本擂台的运营方。评分和名次仅由对局结果计算;赞助商无法改变名次或裁决。

赞助商可在已关联作者的 AI 上展示本公司标志。标志在排行榜中该 AI 的一行及资料页顶部取代 AI 的纹章,仅在赞助商的广告牌展示期间显示。只计入赞助商在通过邮件链接登录的门户中确认为自己的广告牌(“这是我的广告牌”;付款时填写的邮箱未经验证);作者旁显示的赞助商名称也同样如此。不影响评分、对局编排与裁定。

资料页

点击引擎的名称,可以打开它的资料页(/e/<name>)。它的霓虹招牌的颜色由名称自动选取。

能耗按能耗数字是什么意思?所述从时钟计算,不含对局前的准备。“一次充电约 X 局”按满电约 70,000 J(3.85 V、5,000 mAh)除以每局的能耗计算,因此每局 1 J 约为 70,000 局。

在社交媒体上发布资料页的链接时,会显示招牌的图片和评分。

版本与评分历史

AI 的每个版本都是独立的引擎,评分也各自从零开始测定(新程序就是新引擎)。注册时指明上一版本(02 注册,或命令行的 --parent <上一版本名称>;需要作者密钥),资料页就会把各版本连成一个系列。新版本通过关卡后,旧版本自动退役;未通过关卡,旧版本继续对局。

资料页的 RATING HISTORY 把这个系列画成一条连续的曲线,每个版本一种颜色,版本开始处有标记,并附有简短的 VERSIONS 列表。作者页(/a/<名称>)把该作者的所有 AI 叠在一起,每个项目一张图。评分每个 UTC 日记录一次,取当天开始时的值,覆盖所有已有评分的引擎,包括基准引擎。虚线或空心点表示暂定评分。同样的数据可通过 GET /api/engines/<name>/history 和 GET /api/authors/<name>/history 读取。

最近的对局

在排行榜下方打开。每局对局都会显示结果,以及双方的效率。

点击结果行可以观看这局对局。

标签

标签 含义
VERIFIED Verified 作者:用密钥签名注册,首个引擎在至少 7 天前通过关卡,且没有违规记录。自动授予,无需人工审核;三个条件中任一项不再成立即失去。不是身份验证(任何人都可以创建密钥)。链接到作者页面。优先权:专留的注册名额、关卡对局优先、无需计算 Proof of Work。定义和细节:/spec/#verified
ANCHOR 基准引擎(评分固定)
SIZE 大小(WASM + 数据文件)
体积仅 1/43 标在较小一方获胜的对局上:胜方的大小(WASM + 数据文件)是负方的几分之一。从 1/2 起标注,小数舍去
ENERGY/GAME 每局的耗电(数字的含义)
MEM 常驻内存峰值(线性内存 + 数据文件 + 代码部分)
DOCKING 每步平均支付的停泊费(CYC;400 CYC 起为黄色,1,200 CYC 起为红色:一步的十分之一和十分之三)

挑战

你可以亲自与这些 AI 对局。从排行榜开始:点击某一行或资料页上的“对战”,即可打开棋盘(/chess#challenge/<name>、/shogi#challenge/<name>)。没有名次的运营方 AI 列在表格下方的“运营方的 AI”一栏。没有任何 AI 是锁定的:不需要先赢一局才能和下一个对局。

人类 vs 小小 AI

排行榜上方的看板把人们在服务器上与 AI 下的对局累加起来,国际象棋和将棋合在一起。计入的对局与 AI 的“战胜人类的场数”(见上)相同:不到 10 步的对局以及无人理会直到关闭的对局都不计入。总数只增不减。

正式赛

擂台的服务器会自动进行正式赛。参赛者不需要运行任何东西。

别人的引擎永远不会在你的浏览器中运行。本页面只会运行你自己选择的 .wasm 文件和模板引擎(/starter/ 的示例)。

从 AI 智能体使用

检查、注册和查看结果都可以通过普通的 HTTP 完成。试玩对局不由服务器进行:请用 node arena.mjs play 在本机运行,或在浏览器中使用“试玩”。请把下面任一项交给你的智能体。

论坛

/forum 用来提问引擎被拒绝的原因、问题和规则,或提出建议。任何人都可以阅读,作者可以发帖:作者是用作者密钥注册过 AI 的人(在“参加”页面注册时,密钥保存在注册所用的浏览器里;智能体用 author.key 签名)。和注册一样,每次发帖需要几秒钟的计算。

由你的智能体注册的 AI: 向智能体要它的 author.key(node arena.mjs sign 生成的文件),然后在论坛这里或“参加”→“作者密钥”中选择导入作者密钥。浏览器通过签名一条短消息向服务器证明它持有该密钥;私钥不会离开浏览器。下载作者密钥以相同格式保存浏览器中的密钥,可在命令行用 --key author.key 使用。请对此文件保密:持有者可以以该作者的身份操作。

完整规则和 API:/spec/#forum。

遇到问题

引擎被拒绝的原因

WASM 太大
上限为 4 MiB。评估权重或开局库请改为数据文件提交(合计最多 8 MiB,规格 §1)。去掉调试信息通常有帮助(例如 wasm-opt --strip-debug)。
代码太多,检查的内存不够用
检查所用的内存与代码量成正比,上限为 72 MiB。普通编译出的代码约占其大小的 30 倍(0.77 MiB 的将棋引擎用 23 MiB),所以即使低于 4 MiB 的上限,普通代码约 2 MiB 起、极端密集的代码(例如一个由微小指令组成的超大函数)约 0.17 MiB 起就会被拒绝。下一步:去掉调试信息和名称(wasm-opt --strip-debug -Oz)、用 opt-level = "s" 或 "z" 加 LTO 构建、删掉不用的依赖和被反复展开的泛型代码,把表和权重改为数据文件(规格 §1)。模块很小仍被拒绝时,请附上它的 SHA-256 在论坛(tinyai.run/forum)提问;还没有作者密钥时,请带上文件写信到 hello@tinyai.run。
不是 WASM 文件
请选择 .wasm 文件(以 \0asm 开头)。该文件已损坏,或是其他格式。
使用了不支持的特性
不允许使用线程、relaxed SIMD、异常、尾调用、64 位或多个内存,以及 GC(规格 §1)。请改用单线程搜索重新构建。
未通过校验
模块已损坏,或使用了规格 §1 之外的特性。详情中的偏移量(offset)指出了位置。
从 env 导入
env 导入是 Emscripten 与浏览器 JS 交互的方式。请改为面向 WASI 构建(Rust 用 wasm32-wasip1,C/C++ 用 wasi-sdk;规格 §1)。
不允许的导入
只能导入 wasi_snapshot_preview1 中的函数,且参数和返回值的类型必须与 preview1 的声明完全一致(只有 i32 和 i64;不能用 thread_spawn;规格 §1)。详情中会显示找到的类型。
初始内存太大
上限为 32 MiB。请缩小静态分配的数组(例如哈希表),或在启动时按需分配。内存也要付停泊费(规格 §3)。
没有内存
模块必须恰好有一个线性内存(规格 §1)。
不支持的内存
必须恰好是一个非共享的 32 位内存(规格 §1)。
不允许 table.get / table.set / table.grow / table.fill / table.copy / table.init
在运行时修改函数表没有定义的成本,因此被拒绝(规格 §1)。
没有导出 _start
请构建 WASI 命令(带 main 的可执行文件)。reactor 和 cdylib 无法启动(规格 §1)。
含有 start 段
start 段会在 `_start` 之前运行,不被允许。请在 `_start` 内部做初始化(普通的 WASI 命令没有 start 段)。
嵌套、br_table 或类型列表过大
block 最多嵌套 4096 层;全部函数里 br_table 的目标数乘以嵌套深度的总和不超过 1000 万;类型不超过 1024 个,单个类型的参数或返回值不超过 64 个,带多值类型的 block 数 × 类型数 ×(值的个数 + 1)不超过 1 亿,检查本身也会在 3 秒 CPU 时停止(规格 §1)。编译器的输出远低于此(参考引擎的最大值:深度 72、总和 9132、34 个类型,没有多值 block);机器生成的巨大 `switch`、嵌套或类型列表才会超过。请拆分函数或压平结构。
数据文件太多
上限为 16 个文件。请合并它们。
数据文件名无效
请使用相对路径,由 [A-Za-z0-9._-] 组成的各段用 / 分隔(例如 eval/nn.bin)。
数据文件太大
合计上限为 8 MiB。数据文件也计入停泊费(规格 §3)。

对局中止的原因

对局前没有回复
请在准备时间内回复 usiok 和 readyok(见说明书中的正式赛规则)。加载权重过慢可能会超时(规格 §2)。
缺少应有的回复
收到 usi 回复 usiok(国际象棋:收到 uci 回复 uciok),收到 isready 回复 readyok(规格 §2)。
没有走子就在等待输入
轮到你时,请先输出 bestmove,再读取 stdin。
超时
请按时钟而不是节点数来停止搜索:时钟随指令数前进(规格 §3)。每步的上限由 go 给出(UCI 为 movetime,USI 为 byoyomi)。
违规着法
违规或无法解析的着法,以及无效的入玉宣言,都判负。
内存不足
扩展内存的请求在 32 MiB 的上限处被拒绝,引擎无法继续(C++ 的 bad_alloc、Rust 的分配失败等)。请缩小哈希表和历史表(规格 §3、§5)。
执行到了 unreachable
编译器会在程序放弃继续的地方生成这条指令(panic、abort、断言失败、禁用异常的构建中的异常等)。stderr 的末尾通常会说明原因。
内存访问越界
释放后使用、数组下标错误等。
除以零 / 调用栈过深
超过调用深度上限时也会停在这里(规格 §5)。
整数除法溢出
例如最小值除以 −1。
间接调用失败
通过函数表的调用遇到了类型不匹配或空槽位。
stdout 输出过多
每个回合最多 64 KiB(规格 §5)。请减少 info 输出。
执行已停止
请查看 stderr 的末尾。
引擎已退出
对局中退出会判负。请持续读取 stdin,直到对局结束。
这台设备内存不足
不是引擎的错;这局对局已作废。请换一台内存更大的设备,或关闭其他标签页。