dsh-eval-guard 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -13
- package/assets/commit0/instance.json +30 -0
- package/assets/commit0/workspace.zip +0 -0
- package/assets/featurebench/hidden/test_regression.py +690 -0
- package/assets/featurebench/instance.json +16 -0
- package/assets/featurebench/workspace.zip +0 -0
- package/assets/swe-bench-pro/hidden/test.patch +36 -0
- package/assets/swe-bench-pro/instance.json +10 -0
- package/assets/swe-bench-pro/workspace.zip +0 -0
- package/eval/README.md +18 -0
- package/eval/arena/arena/README.md +21 -0
- package/eval/arena/arena/bt.py +104 -0
- package/eval/arena/arena/clock.py +15 -0
- package/eval/arena/arena/core.py +265 -0
- package/eval/arena/arena/run.cmd +24 -0
- package/eval/arena/arena/run.sh +11 -0
- package/eval/arena/arena/schedule.py +79 -0
- package/eval/arena/arena/tournament.py +183 -0
- package/eval/arena/bomberman/bots/README.md +16 -0
- package/eval/arena/bomberman/bots/deepseek-v4.1-flash.cpp +447 -0
- package/eval/arena/bomberman/bots/doubao-seed-2.1.cpp +542 -0
- package/eval/arena/bomberman/bots/glm-5.3.cpp +406 -0
- package/eval/arena/bomberman/bots/gpt-6-astra.cpp +768 -0
- package/eval/arena/bomberman/bots/gpt-6-sol.cpp +444 -0
- package/eval/arena/bomberman/bots/kimi-k3.cpp +432 -0
- package/eval/arena/bomberman/bots/mimo-v2.6-pro.cpp +529 -0
- package/eval/arena/bomberman/bots/opus-5.5.cpp +548 -0
- package/eval/arena/bomberman/bots/qwen-3.8-max.cpp +836 -0
- package/eval/arena/bomberman/tournament/README.md +11 -0
- package/eval/arena/bomberman/tournament/bt.py +56 -0
- package/eval/arena/bomberman/tournament/evaluate.py +199 -0
- package/eval/arena/bomberman/tournament/run.py +79 -0
- package/eval/arena/bomberman/tournament/schedule-2to10.json.gz +0 -0
- package/eval/arena/bomberman/workspace/MANIFEST.json +13 -0
- package/eval/arena/bomberman/workspace/README.md +29 -0
- package/eval/arena/bomberman/workspace/RULES.md +100 -0
- package/eval/arena/bomberman/workspace/baseline.cpp +240 -0
- package/eval/arena/bomberman/workspace/clock.py +25 -0
- package/eval/arena/bomberman/workspace/engine/arena.py +198 -0
- package/eval/arena/bomberman/workspace/engine/build.py +35 -0
- package/eval/arena/bomberman/workspace/engine/engine.py +256 -0
- package/eval/arena/bomberman/workspace/engine/verify_replay.py +24 -0
- package/eval/arena/bomberman/workspace/run.cmd +28 -0
- package/eval/arena/bomberman/workspace/run.sh +16 -0
- package/eval/arena/bomberman/workspace/viewer/index.html +42 -0
- package/eval/arena/ctf/bots/README.md +18 -0
- package/eval/arena/ctf/bots/deepseek-v4.1-flash.cpp +601 -0
- package/eval/arena/ctf/bots/doubao-seed-2.1-turbo.cpp +320 -0
- package/eval/arena/ctf/bots/fable-5.1.cpp +634 -0
- package/eval/arena/ctf/bots/glm-5.3.cpp +689 -0
- package/eval/arena/ctf/bots/gpt-6-astra.cpp +657 -0
- package/eval/arena/ctf/bots/gpt-6.1-sol.cpp +725 -0
- package/eval/arena/ctf/bots/kimi-k3.cpp +667 -0
- package/eval/arena/ctf/bots/mimo-v2.6-pro.cpp +533 -0
- package/eval/arena/ctf/bots/opus-5.5.cpp +645 -0
- package/eval/arena/ctf/bots/qwen-3.8-max.cpp +726 -0
- package/eval/arena/ctf/bots/sonnet-5.5.cpp +569 -0
- package/eval/arena/ctf/tournament/README.md +15 -0
- package/eval/arena/ctf/tournament/formal-2to8.json +31 -0
- package/eval/arena/ctf/tournament/formal-9to15.json +31 -0
- package/eval/arena/ctf/workspace/README.md +41 -0
- package/eval/arena/ctf/workspace/RULES.md +119 -0
- package/eval/arena/ctf/workspace/arena.py +186 -0
- package/eval/arena/ctf/workspace/baseline.cpp +257 -0
- package/eval/arena/ctf/workspace/clock.py +15 -0
- package/eval/arena/ctf/workspace/core.py +265 -0
- package/eval/arena/ctf/workspace/engine.py +389 -0
- package/eval/arena/ctf/workspace/run.cmd +24 -0
- package/eval/arena/ctf/workspace/run.sh +11 -0
- package/eval/arena/ctf/workspace/viewer.html +71 -0
- package/eval/arena/land/bots/README.md +17 -0
- package/eval/arena/land/bots/deepseek-4.1f@workbuddy#1/bot.cpp +608 -0
- package/eval/arena/land/bots/glm-5.3@workbuddy#1/bot.cpp +521 -0
- package/eval/arena/land/bots/gpt-6-astra@codex#1/bot.cpp +373 -0
- package/eval/arena/land/bots/gpt-6-sol@codex#1/bot.cpp +341 -0
- package/eval/arena/land/bots/kimi-k3@workbuddy#1/bot.cpp +492 -0
- package/eval/arena/land/bots/mimo-2.6-pro@zcode#1/bot.cpp +652 -0
- package/eval/arena/land/bots/mystery-model@zcode#1/bot.cpp +466 -0
- package/eval/arena/land/bots/opus-5.5@claude-code#1/bot.cpp +630 -0
- package/eval/arena/land/bots/sonnet-5@claude-code#1/bot.cpp +619 -0
- package/eval/arena/land/tournament/make_replay_page.py +17 -0
- package/eval/arena/land/tournament/rate.py +290 -0
- package/eval/arena/land/tournament/round_robin.py +172 -0
- package/eval/arena/land/tournament/sparring.cpp +356 -0
- package/eval/arena/land/workspace/RULES.md +114 -0
- package/eval/arena/land/workspace/TASK.md +46 -0
- package/eval/arena/land/workspace/anchors/.build/greedy_f2e6d2dea5ca +0 -0
- package/eval/arena/land/workspace/anchors/greedy.cpp +194 -0
- package/eval/arena/land/workspace/anchors/random.cpp +80 -0
- package/eval/arena/land/workspace/clock.py +64 -0
- package/eval/arena/land/workspace/engine/build.py +33 -0
- package/eval/arena/land/workspace/engine/game.py +273 -0
- package/eval/arena/land/workspace/engine/referee.py +308 -0
- package/eval/arena/land/workspace/starter/.build/bot_be32b45b4699 +0 -0
- package/eval/arena/land/workspace/starter/bot.cpp +82 -0
- package/eval/arena/land/workspace/viewer/viewer.html +210 -0
- package/eval/arena/tank/bots/README.md +24 -0
- package/eval/arena/tank/bots/half1/deepseek-v4.1-flash.cpp +367 -0
- package/eval/arena/tank/bots/half1/doubao-seed-evolving.cpp +402 -0
- package/eval/arena/tank/bots/half1/gemini-4-argon.cpp +1132 -0
- package/eval/arena/tank/bots/half1/glm-5.3.cpp +551 -0
- package/eval/arena/tank/bots/half1/gpt-6-astra.cpp +233 -0
- package/eval/arena/tank/bots/half1/gpt-6.1-sol-ultrafast.cpp +350 -0
- package/eval/arena/tank/bots/half1/gpt-6.1-sol.cpp +78 -0
- package/eval/arena/tank/bots/half1/grok-4.7.cpp +1099 -0
- package/eval/arena/tank/bots/half1/haiku-5.5.cpp +210 -0
- package/eval/arena/tank/bots/half1/kimi-k3.cpp +353 -0
- package/eval/arena/tank/bots/half1/mimo-v2.6-pro.cpp +336 -0
- package/eval/arena/tank/bots/half1/muse-wonder-full.cpp +560 -0
- package/eval/arena/tank/bots/half1/opus-5.5.cpp +435 -0
- package/eval/arena/tank/bots/half1/qwen-3.8-max.cpp +537 -0
- package/eval/arena/tank/bots/half2/deepseek-v4.1-flash.cpp +425 -0
- package/eval/arena/tank/bots/half2/doubao-seed-evolving.cpp +308 -0
- package/eval/arena/tank/bots/half2/gemini-4-argon.cpp +1390 -0
- package/eval/arena/tank/bots/half2/glm-5.3.cpp +651 -0
- package/eval/arena/tank/bots/half2/gpt-6-astra.cpp +241 -0
- package/eval/arena/tank/bots/half2/gpt-6.1-sol-ultrafast.cpp +356 -0
- package/eval/arena/tank/bots/half2/gpt-6.1-sol.cpp +87 -0
- package/eval/arena/tank/bots/half2/grok-4.7.cpp +1205 -0
- package/eval/arena/tank/bots/half2/haiku-5.5.cpp +210 -0
- package/eval/arena/tank/bots/half2/kimi-k3.cpp +487 -0
- package/eval/arena/tank/bots/half2/mimo-v2.6-pro.cpp +336 -0
- package/eval/arena/tank/bots/half2/muse-wonder-full.cpp +560 -0
- package/eval/arena/tank/bots/half2/opus-5.5.cpp +476 -0
- package/eval/arena/tank/bots/half2/qwen-3.8-max.cpp +392 -0
- package/eval/arena/tank/tournament/README.md +24 -0
- package/eval/arena/tank/tournament/bt.py +218 -0
- package/eval/arena/tank/tournament/final.json +202 -0
- package/eval/arena/tank/tournament/halftime.json +146 -0
- package/eval/arena/tank/tournament/progress.json +911 -0
- package/eval/arena/tank/tournament/schedule.py +100 -0
- package/eval/arena/tank/tournament/sparring.cpp +928 -0
- package/eval/arena/tank/tournament/tournament.py +1426 -0
- package/eval/arena/tank/workspace/README.md +169 -0
- package/eval/arena/tank/workspace/RULES.md +443 -0
- package/eval/arena/tank/workspace/arena.py +862 -0
- package/eval/arena/tank/workspace/baseline.cpp +259 -0
- package/eval/arena/tank/workspace/clock.py +103 -0
- package/eval/arena/tank/workspace/core.py +712 -0
- package/eval/arena/tank/workspace/engine.py +573 -0
- package/eval/arena/tank/workspace/history/.lock +0 -0
- package/eval/arena/tank/workspace/history/20261017-193610-08715287f91f-bot.cpp +109 -0
- package/eval/arena/tank/workspace/history/20261017-193619-f00b2d5635fa-gpt-6-astra.cpp +241 -0
- package/eval/arena/tank/workspace/history/20261017-193643-63319e0a2b1a-gpt-6_1-sol-ultrafast.cpp +356 -0
- package/eval/arena/tank/workspace/history/20261017-193647-41c1341d0a8a-gemini-4-argon.cpp +1390 -0
- package/eval/arena/tank/workspace/history/20261017-193652-e34c0f45539a-gpt-6_1-sol.cpp +87 -0
- package/eval/arena/tank/workspace/history/20261017-193654-c026348ce96e-opus-5_5.cpp +476 -0
- package/eval/arena/tank/workspace/history/20261017-193655-4a21366e94d1-muse-wonder-full.cpp +560 -0
- package/eval/arena/tank/workspace/history/20261017-193657-dbc4e9969c52-mimo-v2_6-pro.cpp +336 -0
- package/eval/arena/tank/workspace/history/20261017-193703-723cd036eb31-grok-4_7.cpp +1205 -0
- package/eval/arena/tank/workspace/history/20261017-193707-1ebbb227be68-deepseek-v4_1-flash.cpp +425 -0
- package/eval/arena/tank/workspace/history/20261017-193709-aeb0e7202ad5-doubao-seed-evolving.cpp +308 -0
- package/eval/arena/tank/workspace/history/20261017-193710-b21ebeb9bc76-kimi-k3.cpp +487 -0
- package/eval/arena/tank/workspace/history/20261017-193712-54cd86531905-haiku-5_5.cpp +210 -0
- package/eval/arena/tank/workspace/history/20261017-193714-efdb8e35c0bf-qwen-3_8-max.cpp +392 -0
- package/eval/arena/tank/workspace/history/20261017-193715-681423c23fdb-glm-5_3.cpp +651 -0
- package/eval/arena/tank/workspace/history/20261017-193717-1a8b3c75181d-sparring.cpp +928 -0
- package/eval/arena/tank/workspace/history/20261017-193720-1e0803a541e8-baseline.cpp +259 -0
- package/eval/arena/tank/workspace/history/log.jsonl +103 -0
- package/eval/arena/tank/workspace/judge/cycles.hpp +53 -0
- package/eval/arena/tank/workspace/judge/fast.cpp +337 -0
- package/eval/arena/tank/workspace/judge/referee.cpp +125 -0
- package/eval/arena/tank/workspace/judge/speed.cpp +99 -0
- package/eval/arena/tank/workspace/run.cmd +31 -0
- package/eval/arena/tank/workspace/run.sh +23 -0
- package/eval/arena/tank/workspace/tank.hpp +1762 -0
- package/eval/arena/tank/workspace/tank_main.cpp +215 -0
- package/eval/arena/tank/workspace/viewer.html +1150 -0
- package/eval/arena/warehouse/workspace/SPEC.md +130 -0
- package/eval/arena/warehouse/workspace/TASK.md +71 -0
- package/eval/arena/warehouse/workspace/baseline/common.h +88 -0
- package/eval/arena/warehouse/workspace/baseline/main.cpp +27 -0
- package/eval/arena/warehouse/workspace/baseline/old_stuff.cpp +87 -0
- package/eval/arena/warehouse/workspace/baseline/sim.cpp +427 -0
- package/eval/arena/warehouse/workspace/baseline/util.cpp +220 -0
- package/eval/arena/warehouse/workspace/clock.py +42 -0
- package/eval/arena/warehouse/workspace/src/common.h +88 -0
- package/eval/arena/warehouse/workspace/src/main.cpp +27 -0
- package/eval/arena/warehouse/workspace/src/old_stuff.cpp +87 -0
- package/eval/arena/warehouse/workspace/src/sim.cpp +427 -0
- package/eval/arena/warehouse/workspace/src/util.cpp +220 -0
- package/eval/arena/warehouse/workspace/tools/bench.py +119 -0
- package/eval/arena/warehouse/workspace/tools/check_input.py +161 -0
- package/eval/arena/warehouse/workspace/tools/gen.py +132 -0
- package/eval/arena/warehouse/workspace/tools/measure.py +107 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_crowded.in +1420 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_crowded.out +9162 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_dynamic.in +1465 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_dynamic.out +3439 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_flood.in +2757 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_flood.out +3126 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_small.in +731 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_small.out +2578 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_wide.in +528 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_wide.out +2026 -0
- package/eval/bomberman.py +20 -0
- package/eval/commit0.py +21 -0
- package/eval/common.py +61 -0
- package/eval/ctf.py +20 -0
- package/eval/featurebench.py +25 -0
- package/eval/land.py +20 -0
- package/eval/legacy/bomberman-prices.json +4 -0
- package/eval/legacy/bomberman.py +161 -0
- package/eval/legacy/commit0.py +91 -0
- package/eval/legacy/ctf-prices.json +4 -0
- package/eval/legacy/ctf.py +178 -0
- package/eval/legacy/featurebench.py +103 -0
- package/eval/legacy/land-prices.json +4 -0
- package/eval/legacy/land.py +177 -0
- package/eval/legacy/swe_bench_pro.py +116 -0
- package/eval/legacy/tank-prices.json +3 -0
- package/eval/legacy/tank.py +408 -0
- package/eval/legacy/warehouse-prices.json +4 -0
- package/eval/legacy/warehouse.py +365 -0
- package/eval/swe-bench-pro.py +25 -0
- package/eval/tank.py +20 -0
- package/eval/warehouse.py +20 -0
- package/lib/client/index.js +376 -63
- package/lib/client/index.js.map +1 -1
- package/lib/index.js +684 -27
- package/package.json +2 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""赛程生成:任意参赛人数、任意单局人数。
|
|
2
|
+
|
|
3
|
+
每张地图上,把参赛程序分成若干组,每组在同一张地图上把座位完整轮换一圈
|
|
4
|
+
(k 人组打 k 局),所以组内的座位优势完全抵消。分组时优先选出场少、
|
|
5
|
+
彼此交手少的程序,使出场次数最多相差 1、两两交手次数尽量接近。
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import itertools
|
|
10
|
+
import random
|
|
11
|
+
from collections import Counter
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def groups_for_map(players, k, appear, meet, rng):
|
|
15
|
+
"""为一张地图选出若干 k 人组,覆盖所有程序至少一次。"""
|
|
16
|
+
if k == len(players):
|
|
17
|
+
order = players[:]
|
|
18
|
+
rng.shuffle(order)
|
|
19
|
+
return [order]
|
|
20
|
+
remaining = set(players)
|
|
21
|
+
groups = []
|
|
22
|
+
while remaining:
|
|
23
|
+
group = []
|
|
24
|
+
pool = sorted(remaining, key=lambda p: (appear[p], rng.random()))
|
|
25
|
+
while len(group) < k:
|
|
26
|
+
if not pool: # 最后一组不满时,从已分组的程序里补
|
|
27
|
+
pool = sorted((p for p in players if p not in group),
|
|
28
|
+
key=lambda p: (appear[p], rng.random()))
|
|
29
|
+
best = min(pool, key=lambda p: (appear[p], sum(meet[frozenset((p, q))] for q in group), rng.random()))
|
|
30
|
+
group.append(best)
|
|
31
|
+
pool.remove(best)
|
|
32
|
+
remaining.discard(best)
|
|
33
|
+
for a, b in itertools.combinations(group, 2):
|
|
34
|
+
meet[frozenset((a, b))] += k
|
|
35
|
+
for p in group:
|
|
36
|
+
appear[p] += k
|
|
37
|
+
rng.shuffle(group)
|
|
38
|
+
groups.append(group)
|
|
39
|
+
return groups
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def make_schedule(players, sizes, maps_per_size, seed):
|
|
43
|
+
"""返回局列表:{id, size, map_seed, map_index, group, seats}。seats[i] 是第 i 号位的程序名。"""
|
|
44
|
+
rng = random.Random(seed)
|
|
45
|
+
used = set()
|
|
46
|
+
games = []
|
|
47
|
+
for k in sizes:
|
|
48
|
+
if not 2 <= k <= len(players):
|
|
49
|
+
raise ValueError(f"单局人数 {k} 超出范围")
|
|
50
|
+
appear, meet = Counter(), Counter()
|
|
51
|
+
for m in range(maps_per_size):
|
|
52
|
+
map_seed = rng.randrange(1, 2**31)
|
|
53
|
+
while map_seed in used:
|
|
54
|
+
map_seed = rng.randrange(1, 2**31)
|
|
55
|
+
used.add(map_seed)
|
|
56
|
+
for g, group in enumerate(groups_for_map(players, k, appear, meet, rng)):
|
|
57
|
+
for shift in range(k):
|
|
58
|
+
games.append({"size": k, "map_seed": map_seed, "map_index": m, "group": g,
|
|
59
|
+
"seats": group[shift:] + group[:shift]})
|
|
60
|
+
for i, game in enumerate(games, 1):
|
|
61
|
+
game["id"] = f"{game['size']:02d}-{game['map_index']:03d}-{game['group']:02d}-{i:05d}"
|
|
62
|
+
return games
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def audit(games, players):
|
|
66
|
+
"""按人数统计出场、座位、交手是否均衡。"""
|
|
67
|
+
report = {}
|
|
68
|
+
for k in sorted({g["size"] for g in games}):
|
|
69
|
+
rows = [g for g in games if g["size"] == k]
|
|
70
|
+
appear = Counter(p for g in rows for p in g["seats"])
|
|
71
|
+
seat = Counter((p, i) for g in rows for i, p in enumerate(g["seats"]))
|
|
72
|
+
meet = Counter(frozenset(pair) for g in rows for pair in itertools.combinations(g["seats"], 2))
|
|
73
|
+
pairs = [meet[frozenset(pair)] for pair in itertools.combinations(players, 2)]
|
|
74
|
+
report[k] = {"games": len(rows), "maps": len({g["map_seed"] for g in rows}),
|
|
75
|
+
"appear_min": min(appear[p] for p in players), "appear_max": max(appear[p] for p in players),
|
|
76
|
+
"seat_min": min(seat[p, i] for p in players for i in range(k) if appear[p]),
|
|
77
|
+
"seat_max": max(seat.values()),
|
|
78
|
+
"meet_min": min(pairs), "meet_max": max(pairs)}
|
|
79
|
+
return report
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""正式赛:定赛程 → 跑比赛(可中断续跑)→ 出成绩。
|
|
2
|
+
|
|
3
|
+
python tournament.py plan 赛事配置.json 赛事目录
|
|
4
|
+
python tournament.py run 赛事目录 --workers 4
|
|
5
|
+
python tournament.py rate 赛事目录
|
|
6
|
+
|
|
7
|
+
赛事配置(JSON):
|
|
8
|
+
game 游戏引擎文件(提供 Game 类)
|
|
9
|
+
anchor 基准的名字,实力值以它为 0
|
|
10
|
+
players {名字: 程序路径}
|
|
11
|
+
sizes 要打的单局人数列表
|
|
12
|
+
maps_per_size 每种人数用几张地图
|
|
13
|
+
seed 赛程随机种子(可选;不填则随机,写入赛程后冻结)
|
|
14
|
+
replays 是否保存全部回放(默认 true,gzip 压缩)
|
|
15
|
+
build_dir 编译结果放在哪里(可选;默认是赛事目录下的 build/)
|
|
16
|
+
copies 每位选手(含基准)复制几份上场(可选,默认 1)。单局人数超过选手数时用,
|
|
17
|
+
复制品名为 名字#2、名字#3……,计分时合并,复制品之间不比较
|
|
18
|
+
|
|
19
|
+
rate 可以一次读多个赛事目录合并计分,比如 2–6 方和补跑的 7–15 方:
|
|
20
|
+
python tournament.py rate 赛事目录A 赛事目录B
|
|
21
|
+
成绩写在第一个目录里。
|
|
22
|
+
"""
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import argparse
|
|
26
|
+
import gzip
|
|
27
|
+
import importlib.util
|
|
28
|
+
import json
|
|
29
|
+
import os
|
|
30
|
+
import secrets
|
|
31
|
+
import sys
|
|
32
|
+
import time
|
|
33
|
+
from concurrent.futures import ProcessPoolExecutor, as_completed
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
|
|
36
|
+
HERE = Path(__file__).resolve().parent
|
|
37
|
+
sys.path.insert(0, str(HERE))
|
|
38
|
+
import bt # noqa: E402
|
|
39
|
+
import core # noqa: E402
|
|
40
|
+
import schedule # noqa: E402
|
|
41
|
+
|
|
42
|
+
_GAMES = {}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def load_game(path):
|
|
46
|
+
if path not in _GAMES:
|
|
47
|
+
spec = importlib.util.spec_from_file_location(f"game_{len(_GAMES)}", path)
|
|
48
|
+
module = importlib.util.module_from_spec(spec)
|
|
49
|
+
sys.path.insert(0, str(Path(path).parent))
|
|
50
|
+
spec.loader.exec_module(module)
|
|
51
|
+
_GAMES[path] = module.Game
|
|
52
|
+
return _GAMES[path]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def plan(config_path, out):
|
|
56
|
+
out = Path(out)
|
|
57
|
+
if (out / "赛程.json").exists():
|
|
58
|
+
raise SystemExit("赛程已存在,不覆盖。要重排请换一个赛事目录。")
|
|
59
|
+
cfg = json.loads(Path(config_path).read_text(encoding="utf-8-sig")) # 兼容记事本存的带 BOM 文件
|
|
60
|
+
base = Path(config_path).resolve().parent
|
|
61
|
+
players = {n: str((base / p).resolve()) for n, p in cfg["players"].items()}
|
|
62
|
+
copies = cfg.get("copies", 1) # 人数超过选手数时,每位选手复制几份上场
|
|
63
|
+
players.update({f"{n}#{k}": p for n, p in list(players.items()) for k in range(2, copies + 1)})
|
|
64
|
+
for n, p in players.items():
|
|
65
|
+
if not Path(p).is_file():
|
|
66
|
+
raise SystemExit(f"{n} 的程序不存在:{p}")
|
|
67
|
+
seed = cfg.get("seed", secrets.randbits(48))
|
|
68
|
+
games = schedule.make_schedule(sorted(players), cfg["sizes"], cfg["maps_per_size"], seed)
|
|
69
|
+
doc = {"game": str((base / cfg["game"]).resolve()), "anchor": cfg["anchor"], "players": players,
|
|
70
|
+
"sizes": cfg["sizes"], "maps_per_size": cfg["maps_per_size"], "seed": seed,
|
|
71
|
+
"replays": cfg.get("replays", True), "build_dir": cfg.get("build_dir"), "audit": schedule.audit(games, sorted(players)), "games": games}
|
|
72
|
+
out.mkdir(parents=True, exist_ok=True)
|
|
73
|
+
(out / "赛程.json").write_text(json.dumps(doc, ensure_ascii=False, indent=1), encoding="utf-8")
|
|
74
|
+
total = len(games)
|
|
75
|
+
print(f"已冻结赛程:{total} 局")
|
|
76
|
+
for k, a in doc["audit"].items():
|
|
77
|
+
print(f" {k} 人局 {a['games']} 局,每人出场 {a['appear_min']}–{a['appear_max']},两两交手 {a['meet_min']}–{a['meet_max']}")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def play(game_path, commands, g, replay_dir):
|
|
81
|
+
Game = load_game(game_path)
|
|
82
|
+
t = time.time()
|
|
83
|
+
result, replay = core.run_match(Game, [commands[n] for n in g["seats"]], g["seats"], g["map_seed"],
|
|
84
|
+
record=replay_dir is not None)
|
|
85
|
+
if replay is not None:
|
|
86
|
+
path = Path(replay_dir) / f"{g['id']}.json.gz"
|
|
87
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
88
|
+
with gzip.open(path, "wt", encoding="utf-8") as f:
|
|
89
|
+
json.dump(replay, f, ensure_ascii=False, separators=(",", ":"))
|
|
90
|
+
return {"id": g["id"], "size": g["size"], "map_seed": g["map_seed"], "seats": g["seats"],
|
|
91
|
+
"score": result["score"], "turns": result.get("turns"), "seconds": round(time.time() - t, 2),
|
|
92
|
+
"faults": {b["name"]: b["dead"] or len(b["faults"]) for b in result["bots"] if b["faults"] or b["dead"]},
|
|
93
|
+
"max_ms": {b["name"]: b["max_ms"] for b in result["bots"]},
|
|
94
|
+
"stats": result.get("stats")}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def run(out, workers):
|
|
98
|
+
out = Path(out)
|
|
99
|
+
doc = json.loads((out / "赛程.json").read_text(encoding="utf-8"))
|
|
100
|
+
done_file = out / "结果.jsonl"
|
|
101
|
+
done = set()
|
|
102
|
+
if done_file.exists():
|
|
103
|
+
for line in done_file.read_text(encoding="utf-8").splitlines():
|
|
104
|
+
if line.strip():
|
|
105
|
+
done.add(json.loads(line)["id"])
|
|
106
|
+
todo = [g for g in doc["games"] if g["id"] not in done]
|
|
107
|
+
print(f"共 {len(doc['games'])} 局,已完成 {len(done)},本次运行 {len(todo)},并发 {workers}", flush=True)
|
|
108
|
+
replay_dir = str(out / "回放") if doc["replays"] else None
|
|
109
|
+
# 开赛前在主进程里统一编译一次
|
|
110
|
+
build = Path(doc.get("build_dir") or out / "build")
|
|
111
|
+
commands = {}
|
|
112
|
+
for name, path in doc["players"].items():
|
|
113
|
+
try:
|
|
114
|
+
commands[name] = core.command_for(path, build)
|
|
115
|
+
except RuntimeError as exc:
|
|
116
|
+
raise SystemExit(f"{name}:{exc}")
|
|
117
|
+
print(f"已编译 {len(commands)} 个程序,放在 {build}", flush=True)
|
|
118
|
+
start = time.time()
|
|
119
|
+
with ProcessPoolExecutor(workers) as pool, done_file.open("a", encoding="utf-8") as f:
|
|
120
|
+
futures = [pool.submit(play, doc["game"], commands, g, replay_dir) for g in todo]
|
|
121
|
+
for i, fut in enumerate(as_completed(futures), 1):
|
|
122
|
+
row = fut.result()
|
|
123
|
+
f.write(json.dumps(row, ensure_ascii=False) + "\n")
|
|
124
|
+
f.flush()
|
|
125
|
+
if i % 25 == 0 or i == len(todo):
|
|
126
|
+
rate_ = i / (time.time() - start)
|
|
127
|
+
print(f" {len(done) + i}/{len(doc['games'])} 约剩 {(len(todo) - i) / rate_ / 60:.1f} 分钟", flush=True)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def rate(outs):
|
|
131
|
+
outs = [Path(o) for o in outs]
|
|
132
|
+
out = outs[0]
|
|
133
|
+
rows, names, anchor = [], set(), None
|
|
134
|
+
for o in outs:
|
|
135
|
+
doc = json.loads((o / "赛程.json").read_text(encoding="utf-8"))
|
|
136
|
+
part = [json.loads(x) for x in (o / "结果.jsonl").read_text(encoding="utf-8").splitlines() if x.strip()]
|
|
137
|
+
if len(part) < len(doc["games"]):
|
|
138
|
+
print(f"注意:{o.name} 只完成了 {len(part)}/{len(doc['games'])} 局,以下为中途成绩。")
|
|
139
|
+
if anchor not in (None, doc["anchor"]):
|
|
140
|
+
raise SystemExit("合并计分的几个赛事目录,基准必须相同")
|
|
141
|
+
anchor = doc["anchor"]
|
|
142
|
+
rows += part
|
|
143
|
+
names |= {bt.base(n) for n in doc["players"]}
|
|
144
|
+
names = sorted(names)
|
|
145
|
+
doc = {"anchor": anchor}
|
|
146
|
+
ci, per_size = bt.rate(rows, names, doc["anchor"])
|
|
147
|
+
order = sorted(names, key=lambda n: -ci[n][0])
|
|
148
|
+
sizes = sorted(per_size)
|
|
149
|
+
lines = ["| 名次 | 选手 | 实力值 | 95% 区间 | " + " | ".join(f"{k}人" for k in sizes) + " | 故障局 |",
|
|
150
|
+
"|---:|---|---:|---|" + "---:|" * len(sizes) + "---:|"]
|
|
151
|
+
faults = {n: sum(1 for r in rows if any(bt.base(f) == n for f in r["faults"])) for n in names}
|
|
152
|
+
rank = 0
|
|
153
|
+
for n in order:
|
|
154
|
+
if n != doc["anchor"]:
|
|
155
|
+
rank += 1
|
|
156
|
+
v, lo, hi = ci[n]
|
|
157
|
+
lines.append(f"| {'—' if n == doc['anchor'] else rank} | {n} | {v:.0f} | {lo:.0f} ~ {hi:.0f} | " +
|
|
158
|
+
" | ".join(f"{per_size[k][n]:.0f}" for k in sizes) + f" | {faults[n]} |")
|
|
159
|
+
text = (f"# 成绩\n\n{len(rows)} 局。实力值以{doc['anchor']}为 0,强 10 倍记 +400;各人数等权;"
|
|
160
|
+
f"区间按地图重抽样 200 次。\n\n" + "\n".join(lines) + "\n")
|
|
161
|
+
(out / "成绩.md").write_text(text, encoding="utf-8")
|
|
162
|
+
(out / "成绩.json").write_text(json.dumps({"ratings": ci, "per_size": per_size}, ensure_ascii=False, indent=1),
|
|
163
|
+
encoding="utf-8")
|
|
164
|
+
print(text)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def main():
|
|
168
|
+
ap = argparse.ArgumentParser()
|
|
169
|
+
sub = ap.add_subparsers(dest="cmd", required=True)
|
|
170
|
+
p = sub.add_parser("plan"); p.add_argument("config"); p.add_argument("out")
|
|
171
|
+
p = sub.add_parser("run"); p.add_argument("out"); p.add_argument("--workers", type=int, default=4)
|
|
172
|
+
p = sub.add_parser("rate"); p.add_argument("out", nargs="+")
|
|
173
|
+
a = ap.parse_args()
|
|
174
|
+
if a.cmd == "plan":
|
|
175
|
+
plan(a.config, a.out)
|
|
176
|
+
elif a.cmd == "run":
|
|
177
|
+
run(a.out, a.workers)
|
|
178
|
+
else:
|
|
179
|
+
rate(a.out)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
if __name__ == "__main__":
|
|
183
|
+
main()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# 各模型的交卷代码
|
|
2
|
+
|
|
3
|
+
都是模型在比赛中独立写出、交卷后封存的原样代码,主办方没有改动。基准是 `../workspace/baseline.cpp`(选手包里公开的,可修改的练习对手)。
|
|
4
|
+
|
|
5
|
+
| 文件 | 模型 | 工具 | 正式赛名次 | BT 实力值 | SHA-256(前 16 位) |
|
|
6
|
+
|---|---|---|---:|---:|---|
|
|
7
|
+
| `gpt-6-astra.cpp` | GPT-6 Astra | Codex | 1 | 488.6 | `ffa1355ef2a13874` |
|
|
8
|
+
| `opus-5.5.cpp` | Opus 5.5 | Claude Code | 2 | 321.8 | `5bb8cbb8c99f8d56` |
|
|
9
|
+
| `gpt-6-sol.cpp` | GPT-6 Sol | Codex | 3 | 301.9 | `6cf1e62beeb04a78` |
|
|
10
|
+
| `deepseek-v4.1-flash.cpp` | DeepSeek V4.1 Flash | ZCode | 4 | 270.7 | `893ca913a06e3a36` |
|
|
11
|
+
| `kimi-k3.cpp` | Kimi K3 | ZCode | 5 | 230.4 | `204e89bcc828cd0a` |
|
|
12
|
+
| `qwen-3.8-max.cpp` | Qwen 3.8 Max | ZCode | 6 | 226.5 | `37fa03b4f491362d` |
|
|
13
|
+
| `glm-5.3.cpp` | GLM 5.3 | ZCode | 7 | 175.0 | `62cfd8c25179af31` |
|
|
14
|
+
| `doubao-seed-2.1.cpp` | 豆包 Seed 2.1 | ZCode | 8 | 95.4 | `230cea2edfdaaf24` |
|
|
15
|
+
| `mimo-v2.6-pro.cpp` | MiMo V2.6 Pro | ZCode | 9 | 39.4 | `00cc917e49b93b59` |
|
|
16
|
+
| `../workspace/baseline.cpp` | 基准 | — | — | 0 | `a4ca3e21d2f8a75c` |
|