dsh-eval-guard 0.2.3 → 0.3.1

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.
Files changed (221) hide show
  1. package/README.md +91 -13
  2. package/assets/commit0/instance.json +30 -0
  3. package/assets/commit0/workspace.zip +0 -0
  4. package/assets/featurebench/hidden/test_regression.py +690 -0
  5. package/assets/featurebench/instance.json +16 -0
  6. package/assets/featurebench/workspace.zip +0 -0
  7. package/assets/swe-bench-pro/hidden/test.patch +36 -0
  8. package/assets/swe-bench-pro/instance.json +10 -0
  9. package/assets/swe-bench-pro/workspace.zip +0 -0
  10. package/eval/README.md +18 -0
  11. package/eval/arena/arena/README.md +21 -0
  12. package/eval/arena/arena/bt.py +104 -0
  13. package/eval/arena/arena/clock.py +15 -0
  14. package/eval/arena/arena/core.py +265 -0
  15. package/eval/arena/arena/run.cmd +24 -0
  16. package/eval/arena/arena/run.sh +11 -0
  17. package/eval/arena/arena/schedule.py +79 -0
  18. package/eval/arena/arena/tournament.py +183 -0
  19. package/eval/arena/bomberman/bots/README.md +16 -0
  20. package/eval/arena/bomberman/bots/deepseek-v4.1-flash.cpp +447 -0
  21. package/eval/arena/bomberman/bots/doubao-seed-2.1.cpp +542 -0
  22. package/eval/arena/bomberman/bots/glm-5.3.cpp +406 -0
  23. package/eval/arena/bomberman/bots/gpt-6-astra.cpp +768 -0
  24. package/eval/arena/bomberman/bots/gpt-6-sol.cpp +444 -0
  25. package/eval/arena/bomberman/bots/kimi-k3.cpp +432 -0
  26. package/eval/arena/bomberman/bots/mimo-v2.6-pro.cpp +529 -0
  27. package/eval/arena/bomberman/bots/opus-5.5.cpp +548 -0
  28. package/eval/arena/bomberman/bots/qwen-3.8-max.cpp +836 -0
  29. package/eval/arena/bomberman/tournament/README.md +11 -0
  30. package/eval/arena/bomberman/tournament/bt.py +56 -0
  31. package/eval/arena/bomberman/tournament/evaluate.py +199 -0
  32. package/eval/arena/bomberman/tournament/run.py +79 -0
  33. package/eval/arena/bomberman/tournament/schedule-2to10.json.gz +0 -0
  34. package/eval/arena/bomberman/workspace/MANIFEST.json +13 -0
  35. package/eval/arena/bomberman/workspace/README.md +29 -0
  36. package/eval/arena/bomberman/workspace/RULES.md +100 -0
  37. package/eval/arena/bomberman/workspace/baseline.cpp +240 -0
  38. package/eval/arena/bomberman/workspace/clock.py +25 -0
  39. package/eval/arena/bomberman/workspace/engine/arena.py +198 -0
  40. package/eval/arena/bomberman/workspace/engine/build.py +35 -0
  41. package/eval/arena/bomberman/workspace/engine/engine.py +256 -0
  42. package/eval/arena/bomberman/workspace/engine/verify_replay.py +24 -0
  43. package/eval/arena/bomberman/workspace/run.cmd +28 -0
  44. package/eval/arena/bomberman/workspace/run.sh +16 -0
  45. package/eval/arena/bomberman/workspace/viewer/index.html +42 -0
  46. package/eval/arena/ctf/bots/README.md +18 -0
  47. package/eval/arena/ctf/bots/deepseek-v4.1-flash.cpp +601 -0
  48. package/eval/arena/ctf/bots/doubao-seed-2.1-turbo.cpp +320 -0
  49. package/eval/arena/ctf/bots/fable-5.1.cpp +634 -0
  50. package/eval/arena/ctf/bots/glm-5.3.cpp +689 -0
  51. package/eval/arena/ctf/bots/gpt-6-astra.cpp +657 -0
  52. package/eval/arena/ctf/bots/gpt-6.1-sol.cpp +725 -0
  53. package/eval/arena/ctf/bots/kimi-k3.cpp +667 -0
  54. package/eval/arena/ctf/bots/mimo-v2.6-pro.cpp +533 -0
  55. package/eval/arena/ctf/bots/opus-5.5.cpp +645 -0
  56. package/eval/arena/ctf/bots/qwen-3.8-max.cpp +726 -0
  57. package/eval/arena/ctf/bots/sonnet-5.5.cpp +569 -0
  58. package/eval/arena/ctf/tournament/README.md +15 -0
  59. package/eval/arena/ctf/tournament/formal-2to8.json +31 -0
  60. package/eval/arena/ctf/tournament/formal-9to15.json +31 -0
  61. package/eval/arena/ctf/workspace/README.md +41 -0
  62. package/eval/arena/ctf/workspace/RULES.md +119 -0
  63. package/eval/arena/ctf/workspace/arena.py +186 -0
  64. package/eval/arena/ctf/workspace/baseline.cpp +257 -0
  65. package/eval/arena/ctf/workspace/clock.py +15 -0
  66. package/eval/arena/ctf/workspace/core.py +265 -0
  67. package/eval/arena/ctf/workspace/engine.py +389 -0
  68. package/eval/arena/ctf/workspace/run.cmd +24 -0
  69. package/eval/arena/ctf/workspace/run.sh +11 -0
  70. package/eval/arena/ctf/workspace/viewer.html +71 -0
  71. package/eval/arena/land/bots/README.md +17 -0
  72. package/eval/arena/land/bots/deepseek-4.1f@workbuddy#1/bot.cpp +608 -0
  73. package/eval/arena/land/bots/glm-5.3@workbuddy#1/bot.cpp +521 -0
  74. package/eval/arena/land/bots/gpt-6-astra@codex#1/bot.cpp +373 -0
  75. package/eval/arena/land/bots/gpt-6-sol@codex#1/bot.cpp +341 -0
  76. package/eval/arena/land/bots/kimi-k3@workbuddy#1/bot.cpp +492 -0
  77. package/eval/arena/land/bots/mimo-2.6-pro@zcode#1/bot.cpp +652 -0
  78. package/eval/arena/land/bots/mystery-model@zcode#1/bot.cpp +466 -0
  79. package/eval/arena/land/bots/opus-5.5@claude-code#1/bot.cpp +630 -0
  80. package/eval/arena/land/bots/sonnet-5@claude-code#1/bot.cpp +619 -0
  81. package/eval/arena/land/tournament/make_replay_page.py +17 -0
  82. package/eval/arena/land/tournament/rate.py +290 -0
  83. package/eval/arena/land/tournament/round_robin.py +172 -0
  84. package/eval/arena/land/tournament/sparring.cpp +356 -0
  85. package/eval/arena/land/workspace/RULES.md +114 -0
  86. package/eval/arena/land/workspace/TASK.md +46 -0
  87. package/eval/arena/land/workspace/anchors/.build/greedy_f2e6d2dea5ca +0 -0
  88. package/eval/arena/land/workspace/anchors/greedy.cpp +194 -0
  89. package/eval/arena/land/workspace/anchors/random.cpp +80 -0
  90. package/eval/arena/land/workspace/clock.py +64 -0
  91. package/eval/arena/land/workspace/engine/build.py +33 -0
  92. package/eval/arena/land/workspace/engine/game.py +273 -0
  93. package/eval/arena/land/workspace/engine/referee.py +308 -0
  94. package/eval/arena/land/workspace/starter/.build/bot_be32b45b4699 +0 -0
  95. package/eval/arena/land/workspace/starter/bot.cpp +82 -0
  96. package/eval/arena/land/workspace/viewer/viewer.html +210 -0
  97. package/eval/arena/tank/bots/README.md +24 -0
  98. package/eval/arena/tank/bots/half1/deepseek-v4.1-flash.cpp +367 -0
  99. package/eval/arena/tank/bots/half1/doubao-seed-evolving.cpp +402 -0
  100. package/eval/arena/tank/bots/half1/gemini-4-argon.cpp +1132 -0
  101. package/eval/arena/tank/bots/half1/glm-5.3.cpp +551 -0
  102. package/eval/arena/tank/bots/half1/gpt-6-astra.cpp +233 -0
  103. package/eval/arena/tank/bots/half1/gpt-6.1-sol-ultrafast.cpp +350 -0
  104. package/eval/arena/tank/bots/half1/gpt-6.1-sol.cpp +78 -0
  105. package/eval/arena/tank/bots/half1/grok-4.7.cpp +1099 -0
  106. package/eval/arena/tank/bots/half1/haiku-5.5.cpp +210 -0
  107. package/eval/arena/tank/bots/half1/kimi-k3.cpp +353 -0
  108. package/eval/arena/tank/bots/half1/mimo-v2.6-pro.cpp +336 -0
  109. package/eval/arena/tank/bots/half1/muse-wonder-full.cpp +560 -0
  110. package/eval/arena/tank/bots/half1/opus-5.5.cpp +435 -0
  111. package/eval/arena/tank/bots/half1/qwen-3.8-max.cpp +537 -0
  112. package/eval/arena/tank/bots/half2/deepseek-v4.1-flash.cpp +425 -0
  113. package/eval/arena/tank/bots/half2/doubao-seed-evolving.cpp +308 -0
  114. package/eval/arena/tank/bots/half2/gemini-4-argon.cpp +1390 -0
  115. package/eval/arena/tank/bots/half2/glm-5.3.cpp +651 -0
  116. package/eval/arena/tank/bots/half2/gpt-6-astra.cpp +241 -0
  117. package/eval/arena/tank/bots/half2/gpt-6.1-sol-ultrafast.cpp +356 -0
  118. package/eval/arena/tank/bots/half2/gpt-6.1-sol.cpp +87 -0
  119. package/eval/arena/tank/bots/half2/grok-4.7.cpp +1205 -0
  120. package/eval/arena/tank/bots/half2/haiku-5.5.cpp +210 -0
  121. package/eval/arena/tank/bots/half2/kimi-k3.cpp +487 -0
  122. package/eval/arena/tank/bots/half2/mimo-v2.6-pro.cpp +336 -0
  123. package/eval/arena/tank/bots/half2/muse-wonder-full.cpp +560 -0
  124. package/eval/arena/tank/bots/half2/opus-5.5.cpp +476 -0
  125. package/eval/arena/tank/bots/half2/qwen-3.8-max.cpp +392 -0
  126. package/eval/arena/tank/tournament/README.md +24 -0
  127. package/eval/arena/tank/tournament/bt.py +218 -0
  128. package/eval/arena/tank/tournament/final.json +202 -0
  129. package/eval/arena/tank/tournament/halftime.json +146 -0
  130. package/eval/arena/tank/tournament/progress.json +911 -0
  131. package/eval/arena/tank/tournament/schedule.py +100 -0
  132. package/eval/arena/tank/tournament/sparring.cpp +928 -0
  133. package/eval/arena/tank/tournament/tournament.py +1426 -0
  134. package/eval/arena/tank/workspace/README.md +169 -0
  135. package/eval/arena/tank/workspace/RULES.md +443 -0
  136. package/eval/arena/tank/workspace/arena.py +862 -0
  137. package/eval/arena/tank/workspace/baseline.cpp +259 -0
  138. package/eval/arena/tank/workspace/clock.py +103 -0
  139. package/eval/arena/tank/workspace/core.py +712 -0
  140. package/eval/arena/tank/workspace/engine.py +573 -0
  141. package/eval/arena/tank/workspace/history/.lock +0 -0
  142. package/eval/arena/tank/workspace/history/20261017-193610-08715287f91f-bot.cpp +109 -0
  143. package/eval/arena/tank/workspace/history/20261017-193619-f00b2d5635fa-gpt-6-astra.cpp +241 -0
  144. package/eval/arena/tank/workspace/history/20261017-193643-63319e0a2b1a-gpt-6_1-sol-ultrafast.cpp +356 -0
  145. package/eval/arena/tank/workspace/history/20261017-193647-41c1341d0a8a-gemini-4-argon.cpp +1390 -0
  146. package/eval/arena/tank/workspace/history/20261017-193652-e34c0f45539a-gpt-6_1-sol.cpp +87 -0
  147. package/eval/arena/tank/workspace/history/20261017-193654-c026348ce96e-opus-5_5.cpp +476 -0
  148. package/eval/arena/tank/workspace/history/20261017-193655-4a21366e94d1-muse-wonder-full.cpp +560 -0
  149. package/eval/arena/tank/workspace/history/20261017-193657-dbc4e9969c52-mimo-v2_6-pro.cpp +336 -0
  150. package/eval/arena/tank/workspace/history/20261017-193703-723cd036eb31-grok-4_7.cpp +1205 -0
  151. package/eval/arena/tank/workspace/history/20261017-193707-1ebbb227be68-deepseek-v4_1-flash.cpp +425 -0
  152. package/eval/arena/tank/workspace/history/20261017-193709-aeb0e7202ad5-doubao-seed-evolving.cpp +308 -0
  153. package/eval/arena/tank/workspace/history/20261017-193710-b21ebeb9bc76-kimi-k3.cpp +487 -0
  154. package/eval/arena/tank/workspace/history/20261017-193712-54cd86531905-haiku-5_5.cpp +210 -0
  155. package/eval/arena/tank/workspace/history/20261017-193714-efdb8e35c0bf-qwen-3_8-max.cpp +392 -0
  156. package/eval/arena/tank/workspace/history/20261017-193715-681423c23fdb-glm-5_3.cpp +651 -0
  157. package/eval/arena/tank/workspace/history/20261017-193717-1a8b3c75181d-sparring.cpp +928 -0
  158. package/eval/arena/tank/workspace/history/20261017-193720-1e0803a541e8-baseline.cpp +259 -0
  159. package/eval/arena/tank/workspace/history/log.jsonl +103 -0
  160. package/eval/arena/tank/workspace/judge/cycles.hpp +53 -0
  161. package/eval/arena/tank/workspace/judge/fast.cpp +337 -0
  162. package/eval/arena/tank/workspace/judge/referee.cpp +125 -0
  163. package/eval/arena/tank/workspace/judge/speed.cpp +99 -0
  164. package/eval/arena/tank/workspace/run.cmd +31 -0
  165. package/eval/arena/tank/workspace/run.sh +23 -0
  166. package/eval/arena/tank/workspace/tank.hpp +1762 -0
  167. package/eval/arena/tank/workspace/tank_main.cpp +215 -0
  168. package/eval/arena/tank/workspace/viewer.html +1150 -0
  169. package/eval/arena/warehouse/workspace/SPEC.md +130 -0
  170. package/eval/arena/warehouse/workspace/TASK.md +71 -0
  171. package/eval/arena/warehouse/workspace/baseline/common.h +88 -0
  172. package/eval/arena/warehouse/workspace/baseline/main.cpp +27 -0
  173. package/eval/arena/warehouse/workspace/baseline/old_stuff.cpp +87 -0
  174. package/eval/arena/warehouse/workspace/baseline/sim.cpp +427 -0
  175. package/eval/arena/warehouse/workspace/baseline/util.cpp +220 -0
  176. package/eval/arena/warehouse/workspace/clock.py +42 -0
  177. package/eval/arena/warehouse/workspace/src/common.h +88 -0
  178. package/eval/arena/warehouse/workspace/src/main.cpp +27 -0
  179. package/eval/arena/warehouse/workspace/src/old_stuff.cpp +87 -0
  180. package/eval/arena/warehouse/workspace/src/sim.cpp +427 -0
  181. package/eval/arena/warehouse/workspace/src/util.cpp +220 -0
  182. package/eval/arena/warehouse/workspace/tools/bench.py +119 -0
  183. package/eval/arena/warehouse/workspace/tools/check_input.py +161 -0
  184. package/eval/arena/warehouse/workspace/tools/gen.py +132 -0
  185. package/eval/arena/warehouse/workspace/tools/measure.py +107 -0
  186. package/eval/arena/warehouse/workspace/workloads/public/pub_crowded.in +1420 -0
  187. package/eval/arena/warehouse/workspace/workloads/public/pub_crowded.out +9162 -0
  188. package/eval/arena/warehouse/workspace/workloads/public/pub_dynamic.in +1465 -0
  189. package/eval/arena/warehouse/workspace/workloads/public/pub_dynamic.out +3439 -0
  190. package/eval/arena/warehouse/workspace/workloads/public/pub_flood.in +2757 -0
  191. package/eval/arena/warehouse/workspace/workloads/public/pub_flood.out +3126 -0
  192. package/eval/arena/warehouse/workspace/workloads/public/pub_small.in +731 -0
  193. package/eval/arena/warehouse/workspace/workloads/public/pub_small.out +2578 -0
  194. package/eval/arena/warehouse/workspace/workloads/public/pub_wide.in +528 -0
  195. package/eval/arena/warehouse/workspace/workloads/public/pub_wide.out +2026 -0
  196. package/eval/bomberman.py +20 -0
  197. package/eval/commit0.py +21 -0
  198. package/eval/common.py +61 -0
  199. package/eval/ctf.py +20 -0
  200. package/eval/featurebench.py +25 -0
  201. package/eval/land.py +20 -0
  202. package/eval/legacy/bomberman-prices.json +4 -0
  203. package/eval/legacy/bomberman.py +161 -0
  204. package/eval/legacy/commit0.py +91 -0
  205. package/eval/legacy/ctf-prices.json +4 -0
  206. package/eval/legacy/ctf.py +178 -0
  207. package/eval/legacy/featurebench.py +103 -0
  208. package/eval/legacy/land-prices.json +4 -0
  209. package/eval/legacy/land.py +177 -0
  210. package/eval/legacy/swe_bench_pro.py +116 -0
  211. package/eval/legacy/tank-prices.json +3 -0
  212. package/eval/legacy/tank.py +408 -0
  213. package/eval/legacy/warehouse-prices.json +4 -0
  214. package/eval/legacy/warehouse.py +365 -0
  215. package/eval/swe-bench-pro.py +25 -0
  216. package/eval/tank.py +20 -0
  217. package/eval/warehouse.py +20 -0
  218. package/lib/client/index.js +376 -63
  219. package/lib/client/index.js.map +1 -1
  220. package/lib/index.js +684 -27
  221. package/package.json +2 -1
@@ -0,0 +1,308 @@
1
+ """圈地对战本地裁判 / Local referee for the territory game.
2
+
3
+ 用法 Usage:
4
+ python engine/referee.py BOT_A BOT_B [--games N] [--seed S] [--jobs J] [--replay-dir DIR] [--stderr-dir DIR]
5
+ python engine/referee.py BOT_A BOT_B BOT_C BOT_D [...] (4 个 bot = 4 人混战 / melee)
6
+
7
+ bot 可以是 .cpp(按正式参数自动编译)或可执行文件,同一个文件可以重复出现。
8
+ 1 对 1:同一地图交换出生位各打一局。混战:同一地图轮换座位打 4 局,每个 bot 每个座位各坐一次。
9
+ Bots are .cpp files (compiled with the official flags) or executables; a file may repeat.
10
+ 1v1 games come in pairs on the same map with sides swapped; melee games come in groups of 4
11
+ on the same map with seats rotated.
12
+ """
13
+ import argparse
14
+ import json
15
+ import os
16
+ import queue
17
+ import subprocess
18
+ import sys
19
+ import threading
20
+ import time
21
+
22
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
23
+ from game import Game, config_for, make_start # noqa: E402
24
+ import build # noqa: E402
25
+
26
+ MOVE_MS = 50 # per-turn time limit announced to bots
27
+ INIT_MS = 2000 # time limit for turn 0 (includes process start-up)
28
+ GRACE_MS = 15 # tolerance added by the referee, do not rely on it
29
+ MAX_ERRORS = 10 # timeouts + malformed replies allowed per game before forfeiting
30
+ MEM_MB = 512
31
+
32
+ if sys.platform == 'win32':
33
+ # Windows 默认计时精度约 15.6 ms,会让响应时间测量失真;调到 1 ms
34
+ try:
35
+ import ctypes
36
+ ctypes.WinDLL('winmm').timeBeginPeriod(1)
37
+ except Exception:
38
+ pass
39
+
40
+
41
+ def _limit_memory():
42
+ try:
43
+ import resource
44
+ b = MEM_MB * 1024 * 1024
45
+ resource.setrlimit(resource.RLIMIT_AS, (b, b))
46
+ except Exception:
47
+ pass
48
+
49
+
50
+ class Bot:
51
+ def __init__(self, exe, stderr_path=None):
52
+ self.stderr_file = open(stderr_path, 'wb') if stderr_path else subprocess.DEVNULL
53
+ kw = {}
54
+ if sys.platform != 'win32':
55
+ kw['preexec_fn'] = _limit_memory
56
+ else:
57
+ kw['creationflags'] = getattr(subprocess, 'CREATE_NO_WINDOW', 0)
58
+ self.p = subprocess.Popen([exe], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
59
+ stderr=self.stderr_file, bufsize=0, **kw)
60
+ self.q = queue.Queue()
61
+ self.out = queue.Queue()
62
+ self.crashed = False
63
+ self.errors = 0
64
+ self.timeouts = 0
65
+ self.max_ms = 0.0
66
+ threading.Thread(target=self._reader, daemon=True).start()
67
+ threading.Thread(target=self._writer, daemon=True).start()
68
+
69
+ def _reader(self):
70
+ try:
71
+ for line in self.p.stdout:
72
+ self.q.put((time.perf_counter(), line)) # 记录到达时刻,超时按到达时刻判定
73
+ except Exception:
74
+ pass
75
+ self.q.put((time.perf_counter(), None))
76
+
77
+ def _writer(self):
78
+ # a separate thread, so a bot that stops reading stdin cannot block the referee
79
+ while True:
80
+ data = self.out.get()
81
+ if data is None:
82
+ return
83
+ try:
84
+ self.p.stdin.write(data)
85
+ self.p.stdin.flush()
86
+ except (BrokenPipeError, OSError, ValueError):
87
+ self.crashed = True
88
+ return
89
+
90
+ def send(self, text):
91
+ if not self.crashed:
92
+ self.out.put(text.encode())
93
+
94
+ def receive(self, turn, sent_at, limit_ms):
95
+ """Return a direction, or None on timeout / malformed reply (counted as errors)."""
96
+ # 两个 bot 按顺序检查,但是否超时只看回复的到达时刻,所以检查顺序不影响结果
97
+ deadline = sent_at + (limit_ms + GRACE_MS) / 1000.0
98
+ while not self.crashed:
99
+ remaining = deadline - time.perf_counter()
100
+ try:
101
+ if remaining > 0:
102
+ arrived, line = self.q.get(timeout=remaining)
103
+ else:
104
+ arrived, line = self.q.get_nowait()
105
+ except queue.Empty:
106
+ if remaining > 0:
107
+ continue
108
+ self.timeouts += 1
109
+ self.errors += 1
110
+ return None
111
+ if line is None:
112
+ if arrived <= deadline:
113
+ self.crashed = True
114
+ return None
115
+ self.q.put((arrived, None)) # 进程在时限后才退出:本回合先按超时处理
116
+ self.timeouts += 1
117
+ self.errors += 1
118
+ return None
119
+ parts = line.decode(errors='replace').split()
120
+ if len(parts) == 2 and parts[0].lstrip('-').isdigit() and parts[1] in ('U', 'D', 'L', 'R'):
121
+ t = int(parts[0])
122
+ if t < turn:
123
+ continue # 对之前回合的迟到回复,丢弃
124
+ if t == turn:
125
+ if arrived > deadline:
126
+ self.timeouts += 1
127
+ self.errors += 1
128
+ return None
129
+ if turn > 0: # 第 0 回合含进程启动时间,不计入
130
+ self.max_ms = max(self.max_ms, (arrived - sent_at) * 1000)
131
+ return parts[1]
132
+ if arrived > deadline:
133
+ self.timeouts += 1
134
+ self.errors += 1
135
+ return None
136
+ return None
137
+
138
+ def close(self):
139
+ self.out.put(None)
140
+ try:
141
+ self.p.kill()
142
+ except Exception:
143
+ pass
144
+ try:
145
+ self.p.wait(timeout=2)
146
+ except Exception:
147
+ pass
148
+ if self.stderr_file is not subprocess.DEVNULL:
149
+ self.stderr_file.close()
150
+
151
+
152
+ def ranks_of(areas, forfeit):
153
+ """名次(1 = 最好,并列同名次)。判负者的面积按 -1 计,排在所有人之后。"""
154
+ eff = [-1 if f else a for a, f in zip(areas, forfeit)]
155
+ return [1 + sum(e > x for e in eff) for x in eff]
156
+
157
+
158
+ def run_match(exes, names, seed, cfg=None, replay_path=None, stderr_paths=None):
159
+ """Play one game. exes[i] plays as player i (2 bots = 1v1, 4 bots = melee). Returns a result dict."""
160
+ n = len(exes)
161
+ cfg = cfg or config_for(n)
162
+ spawns, dirs = make_start(seed, cfg)
163
+ game = Game(cfg, spawns, dirs)
164
+ initial_owner = ''.join('.0123456789'[o + 1] for o in game.owner)
165
+ bots = [Bot(e, (stderr_paths or [None] * n)[i]) for i, e in enumerate(exes)]
166
+ frames = []
167
+ forfeit = [None] * n
168
+ try:
169
+ for i, b in enumerate(bots):
170
+ head = 'INIT\n%d %d %d %d %d %d %d\n' % (
171
+ cfg['width'], cfg['height'], cfg['max_turns'], game.N, i, MOVE_MS, INIT_MS)
172
+ head += ''.join('SPAWN %d %d %d\n' % (j, x, y) for j, (x, y) in enumerate(spawns))
173
+ b.send(head)
174
+ while not game.over():
175
+ t = game.turn
176
+ text = game.state_text(t)
177
+ limit = INIT_MS if t == 0 else MOVE_MS
178
+ live = [i for i in range(n) if not forfeit[i]]
179
+ sent = {}
180
+ for i in live:
181
+ bots[i].send(text)
182
+ sent[i] = time.perf_counter()
183
+ moves = [None] * n
184
+ for i in live:
185
+ moves[i] = bots[i].receive(t, sent[i], limit)
186
+ newly = []
187
+ for i in live:
188
+ if bots[i].crashed:
189
+ forfeit[i] = 'crash'
190
+ elif bots[i].errors >= MAX_ERRORS:
191
+ forfeit[i] = 'errors'
192
+ if forfeit[i]:
193
+ newly.append(i)
194
+ if newly and n == 2:
195
+ break # 1v1:有人判负,本局立即结束
196
+ for i in newly:
197
+ game.eliminate(i)
198
+ bots[i].close()
199
+ if all(forfeit):
200
+ break
201
+ frame = game.step(moves)
202
+ frame['moves'] = [m or '-' for m in moves]
203
+ for i in newly:
204
+ frame['events'].insert(0, {'type': 'out', 'player': i, 'reason': forfeit[i]})
205
+ frame['trail_clear'].append(i)
206
+ frames.append({k: v for k, v in frame.items() if v})
207
+ finally:
208
+ for b in bots:
209
+ b.close()
210
+
211
+ areas = game.areas()
212
+ ranks = ranks_of(areas, forfeit)
213
+ best = [i for i in range(n) if ranks[i] == 1]
214
+ winner = best[0] if len(best) == 1 else -1
215
+ result = {
216
+ 'mode': 'duel' if n == 2 else 'melee',
217
+ 'seed': seed, 'names': names, 'winner': winner, 'ranks': ranks, 'areas': areas, 'turns': game.turn,
218
+ 'forfeit': forfeit, 'deaths': game.deaths, 'kills': game.kills,
219
+ 'timeouts': [b.timeouts for b in bots], 'errors': [b.errors for b in bots],
220
+ 'max_ms': [round(b.max_ms, 1) for b in bots],
221
+ }
222
+ if replay_path:
223
+ replay = {'version': 2, 'config': cfg, 'names': names, 'seed': seed,
224
+ 'spawns': spawns, 'init_dirs': dirs, 'initial_owner': initial_owner,
225
+ 'frames': frames, 'result': result}
226
+ os.makedirs(os.path.dirname(os.path.abspath(replay_path)), exist_ok=True)
227
+ with open(replay_path, 'w') as f:
228
+ json.dump(replay, f, separators=(',', ':'))
229
+ return result
230
+
231
+
232
+ def _play(job):
233
+ return job[0], run_match(*job[1:])
234
+
235
+
236
+ def main():
237
+ ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
238
+ ap.add_argument('bots', nargs='+', metavar='BOT', help='2 bots = 1v1; 4 bots = melee (the same file may repeat)')
239
+ ap.add_argument('--games', type=int, default=0,
240
+ help='number of games; rounded up to a multiple of 2 (1v1) or 4 (melee). '
241
+ 'Default: 2 (1v1) or 4 (melee)')
242
+ ap.add_argument('--seed', type=int, default=1)
243
+ ap.add_argument('--jobs', type=int, default=1,
244
+ help='games played in parallel (each game runs 2 or 4 bots at once; '
245
+ 'too many makes bots slow and causes timeouts)')
246
+ ap.add_argument('--replay-dir', help='save a replay JSON per game (open with viewer/viewer.html)')
247
+ ap.add_argument('--stderr-dir', help='save bot stderr per game')
248
+ args = ap.parse_args()
249
+
250
+ n = len(args.bots)
251
+ if n not in (2, 4):
252
+ ap.error('give 2 bots (1v1) or 4 bots (melee)')
253
+ exes = [build.resolve(b) for b in args.bots]
254
+ labels = ['%s:%s' % ('ABCD'[i], os.path.basename(b)) for i, b in enumerate(args.bots)]
255
+ per_map = n # 1v1: swap sides; melee: rotate seats so every bot plays every seat
256
+ games = max(args.games or per_map, 1)
257
+ games = (games + per_map - 1) // per_map * per_map
258
+ jobs = []
259
+ for g in range(games):
260
+ seed = args.seed + g // per_map
261
+ rot = g % per_map
262
+ order = [(i + rot) % n for i in range(n)] # order[seat] = bot index
263
+ replay = os.path.join(args.replay_dir, 'game_%d_seed%d_rot%d.json' % (g, seed, rot)) \
264
+ if args.replay_dir else None
265
+ errs = None
266
+ if args.stderr_dir:
267
+ os.makedirs(args.stderr_dir, exist_ok=True)
268
+ errs = [os.path.join(args.stderr_dir, 'game_%d_p%d.log' % (g, i)) for i in range(n)]
269
+ jobs.append((g, order, seed, [exes[o] for o in order], [labels[o] for o in order], seed,
270
+ None, replay, errs))
271
+
272
+ points = [0.0] * n # 1v1: win 1 / draw 0.5; melee: sum of ranks
273
+ area_sum = [0] * n
274
+
275
+ def report(g, order, seed, r):
276
+ for seat, b in enumerate(order):
277
+ area_sum[b] += r['areas'][seat]
278
+ if n == 2:
279
+ points[b] += 1.0 if r['winner'] == seat else 0.5 if r['winner'] == -1 else 0.0
280
+ else:
281
+ points[b] += r['ranks'][seat]
282
+ if n == 2:
283
+ outcome = 'draw' if r['winner'] == -1 else labels[order[r['winner']]] + ' wins'
284
+ else:
285
+ outcome = 'ranks ' + ' '.join('%s=%d' % (labels[o][0], r['ranks'][s]) for s, o in enumerate(order))
286
+ print('game %d seed %d %s areas %s deaths %s timeouts %s max_ms %s forfeit %s -> %s' % (
287
+ g, seed, ' vs '.join(r['names']), r['areas'], r['deaths'], r['timeouts'],
288
+ r['max_ms'], r['forfeit'], outcome), flush=True)
289
+
290
+ if args.jobs <= 1:
291
+ for j in jobs:
292
+ report(j[0], j[1], j[2], run_match(*j[3:]))
293
+ else:
294
+ from concurrent.futures import ProcessPoolExecutor
295
+ meta = {j[0]: j[1:3] for j in jobs}
296
+ with ProcessPoolExecutor(args.jobs) as pool:
297
+ for g, r in pool.map(_play, [(j[0],) + j[3:] for j in jobs]):
298
+ report(g, meta[g][0], meta[g][1], r)
299
+ if n == 2:
300
+ print('TOTAL ' + ' : '.join('%s %.1f' % (labels[i], points[i]) for i in range(n)))
301
+ else:
302
+ print('TOTAL average rank (1 = best) ' + ' '.join(
303
+ '%s %.2f' % (labels[i], points[i] / games) for i in range(n)))
304
+ print('average area ' + ' '.join('%s %.0f' % (labels[i], area_sum[i] / games) for i in range(n)))
305
+
306
+
307
+ if __name__ == '__main__':
308
+ main()
@@ -0,0 +1,82 @@
1
+ // Starter bot: reads the full protocol (any number of players: N = 2 for 1v1, N = 4 for melee)
2
+ // and plays a trivial but legal strategy
3
+ // (keep going straight, turn when the next cell is a wall or its own trail).
4
+ // Copy it to submission/bot.cpp and replace decide() with your own strategy.
5
+ #include <bits/stdc++.h>
6
+ using namespace std;
7
+
8
+ const int DX[4] = {0, 0, -1, 1};
9
+ const int DY[4] = {-1, 1, 0, 0};
10
+ const char DC[4] = {'U', 'D', 'L', 'R'};
11
+
12
+ int dirIndex(char c) { return string("UDLR").find(c); }
13
+
14
+ struct Player {
15
+ int x, y, dir, trailLen, area, deaths;
16
+ };
17
+
18
+ struct State {
19
+ int turn;
20
+ vector<Player> p;
21
+ vector<string> owner; // owner[y][x]: '.' neutral, '0'..'3' territory of that player
22
+ vector<string> trail; // trail[y][x]: '.' none, '0'..'3' trail of that player
23
+ };
24
+
25
+ int W, H, MAX_TURNS, N, ME, MOVE_MS, INIT_MS;
26
+ vector<pair<int, int>> spawn;
27
+
28
+ bool readState(State& s) {
29
+ string tok;
30
+ if (!(cin >> tok)) return false; // "TURN"
31
+ cin >> s.turn;
32
+ s.p.assign(N, {});
33
+ for (int i = 0; i < N; i++) {
34
+ int id;
35
+ char d;
36
+ Player q;
37
+ cin >> tok >> id >> q.x >> q.y >> d >> q.trailLen >> q.area >> q.deaths;
38
+ q.dir = dirIndex(d);
39
+ s.p[id] = q;
40
+ }
41
+ cin >> tok; // OWNER
42
+ s.owner.assign(H, "");
43
+ for (auto& row : s.owner) cin >> row;
44
+ cin >> tok; // TRAIL
45
+ s.trail.assign(H, "");
46
+ for (auto& row : s.trail) cin >> row;
47
+ cin >> tok; // END
48
+ return true;
49
+ }
50
+
51
+ int decide(const State& s) {
52
+ const Player& me = s.p[ME];
53
+ char myTrail = '0' + ME;
54
+ auto ok = [&](int d) {
55
+ if (d == (me.dir ^ 1)) return false; // reversing is not allowed
56
+ int nx = me.x + DX[d], ny = me.y + DY[d];
57
+ return nx >= 0 && nx < W && ny >= 0 && ny < H && s.trail[ny][nx] != myTrail;
58
+ };
59
+ if (ok(me.dir)) return me.dir;
60
+ for (int d = 0; d < 4; d++)
61
+ if (ok(d)) return d;
62
+ return me.dir;
63
+ }
64
+
65
+ int main() {
66
+ ios::sync_with_stdio(false);
67
+ cin.tie(nullptr);
68
+ string tok;
69
+ cin >> tok; // INIT
70
+ cin >> W >> H >> MAX_TURNS >> N >> ME >> MOVE_MS >> INIT_MS;
71
+ spawn.resize(N);
72
+ for (int i = 0; i < N; i++) {
73
+ int id, x, y;
74
+ cin >> tok >> id >> x >> y;
75
+ spawn[id] = {x, y};
76
+ }
77
+ State s;
78
+ while (readState(s)) {
79
+ int d = decide(s);
80
+ cout << s.turn << ' ' << DC[d] << endl; // endl flushes: required every turn
81
+ }
82
+ }
@@ -0,0 +1,210 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>圈地回放</title>
7
+ <style>
8
+ :root {
9
+ --bg: #f6f5f2; --panel: #ffffff; --ink: #1d1d1f; --muted: #6b6b70; --line: #e3e1dc;
10
+ --cell: #ecebe7; --p0: #2f6fdf; --p0-land: #bcd0f5; --p0-trail: #6f9ae8;
11
+ --p1: #d9621b; --p1-land: #f5cfb4; --p1-trail: #e8955f; --accent: #1d1d1f;
12
+ --p2: #2e9a4f; --p2-land: #bfe3c8; --p2-trail: #6cc184;
13
+ --p3: #8a4fd1; --p3-land: #dccaf3; --p3-trail: #ab84e2;
14
+ }
15
+ @media (prefers-color-scheme: dark) {
16
+ :root:not([data-theme="light"]) {
17
+ --bg: #151517; --panel: #1f1f22; --ink: #ececec; --muted: #9a9aa0; --line: #313136;
18
+ --cell: #26262a; --p0-land: #26406f; --p0-trail: #4b78c9; --p1-land: #6a3a1c; --p1-trail: #c07542;
19
+ --p2-land: #1f5a33; --p2-trail: #3f9a5c; --p3-land: #4a2f73; --p3-trail: #8663c2;
20
+ --accent: #ececec;
21
+ }
22
+ }
23
+ * { box-sizing: border-box; }
24
+ body { margin: 0; background: var(--bg); color: var(--ink);
25
+ font: 14px/1.5 system-ui, -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; }
26
+ main { max-width: 1100px; margin: 0 auto; padding: 20px 16px 40px; }
27
+ h1 { font-size: 18px; margin: 0 0 4px; }
28
+ .sub { color: var(--muted); margin: 0 0 16px; }
29
+ .layout { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 16px; align-items: start; }
30
+ @media (max-width: 820px) { .layout { grid-template-columns: 1fr; } }
31
+ .card { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 14px; }
32
+ canvas { width: 100%; height: auto; display: block; border-radius: 6px; image-rendering: pixelated; }
33
+ #drop { border: 1.5px dashed var(--line); border-radius: 10px; padding: 36px 16px; text-align: center; color: var(--muted); }
34
+ #drop.hot { border-color: var(--accent); color: var(--ink); }
35
+ .controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 12px; }
36
+ button, select { font: inherit; color: var(--ink); background: var(--bg); border: 1px solid var(--line);
37
+ border-radius: 6px; padding: 5px 10px; cursor: pointer; }
38
+ button:hover { border-color: var(--muted); }
39
+ input[type=range] { flex: 1; min-width: 160px; accent-color: var(--accent); }
40
+ .turn { font-variant-numeric: tabular-nums; min-width: 88px; }
41
+ .player { display: grid; grid-template-columns: 14px 1fr auto; gap: 4px 8px; align-items: center; margin-bottom: 12px; }
42
+ .sw { width: 14px; height: 14px; border-radius: 3px; }
43
+ .name { font-weight: 600; overflow-wrap: anywhere; }
44
+ .big { font-size: 20px; font-weight: 650; font-variant-numeric: tabular-nums; }
45
+ .kv { grid-column: 2 / 4; color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
46
+ .bar { height: 8px; border-radius: 4px; overflow: hidden; display: flex; background: var(--cell); margin: 4px 0 14px; }
47
+ #log { max-height: 260px; overflow: auto; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
48
+ #log div { padding: 2px 0; border-bottom: 1px solid var(--line); }
49
+ #result { font-weight: 600; margin-bottom: 10px; }
50
+ label.inline { color: var(--muted); display: flex; gap: 6px; align-items: center; }
51
+ </style>
52
+ </head>
53
+ <body>
54
+ <main>
55
+ <h1>圈地回放</h1>
56
+ <p class="sub">把 referee.py 或 tournament.py 生成的回放 JSON 拖到下方,或点击选择(可多选)。空格键播放/暂停,← → 逐回合。</p>
57
+ <div id="drop">拖入回放文件,或 <button id="pick">选择文件</button><input id="file" type="file" accept=".json" multiple hidden></div>
58
+ <div class="layout" id="ui" hidden>
59
+ <div class="card">
60
+ <canvas id="cv"></canvas>
61
+ <div class="controls">
62
+ <button id="play">播放</button>
63
+ <button id="prev">◀</button><button id="next">▶</button>
64
+ <span class="turn" id="turn"></span>
65
+ <input type="range" id="scrub" min="0" value="0">
66
+ </div>
67
+ <div class="controls">
68
+ <label class="inline">速度 <select id="speed">
69
+ <option value="5">5 回合/秒</option><option value="15">15 回合/秒</option>
70
+ <option value="30" selected>30 回合/秒</option><option value="60">60 回合/秒</option><option value="200">200 回合/秒</option>
71
+ </select></label>
72
+ <label class="inline">回放 <select id="which"></select></label>
73
+ </div>
74
+ </div>
75
+ <div class="card">
76
+ <div id="result"></div>
77
+ <div id="players"></div>
78
+ <div class="bar" id="bar"></div>
79
+ <div style="font-weight:600;margin-bottom:4px">事件</div>
80
+ <div id="log"></div>
81
+ </div>
82
+ </div>
83
+ </main>
84
+ <script>
85
+ const $ = id => document.getElementById(id);
86
+ const css = n => getComputedStyle(document.documentElement).getPropertyValue(n).trim();
87
+ const DEATH = { wall: '撞墙', head: '撞头', self: '撞到自己的轨迹', cut: '轨迹被切断' };
88
+ let replays = [], R = null, states = [], cur = 0, timer = null;
89
+
90
+ function load(files) {
91
+ const list = [...files].filter(f => f.name.endsWith('.json'));
92
+ Promise.all(list.map(f => f.text().then(t => { const j = JSON.parse(t); j._file = f.name; return j; })))
93
+ .then(rs => {
94
+ replays = rs.filter(r => r.frames);
95
+ if (!replays.length) return alert('没有找到回放数据');
96
+ $('which').innerHTML = replays.map((r, i) => `<option value="${i}">${r._file}</option>`).join('');
97
+ $('drop').hidden = true; $('ui').hidden = false;
98
+ open(0);
99
+ }).catch(e => alert('读取失败:' + e));
100
+ }
101
+
102
+ // Rebuild every turn's state once; 600 turns x 1600 cells is small.
103
+ function open(i) {
104
+ stop();
105
+ R = replays[i];
106
+ const W = R.config.width, H = R.config.height, n = R.names.length;
107
+ let owner = Int8Array.from(R.initial_owner, c => c === '.' ? -1 : +c);
108
+ let trails = Array.from({ length: n }, () => []);
109
+ let pos = R.spawns.map((s, j) => [s[0], s[1], R.init_dirs[j]]);
110
+ const snap = (events, t) => ({ owner: owner.slice(), trails: trails.map(t => t.slice()), pos, events, t });
111
+ states = [snap([], 0)];
112
+ const deaths = new Array(n).fill(0);
113
+ R.frames.forEach((f, t) => {
114
+ for (const j of f.trail_clear || []) trails[j] = [];
115
+ for (const [j, k] of f.trail_add || []) trails[j].push(k);
116
+ for (const [k, j] of f.owner || []) owner[k] = j;
117
+ pos = f.pos;
118
+ for (const e of f.events || []) if (e.type === 'death') deaths[e.player]++;
119
+ const s = snap(f.events || [], t + 1); s.deaths = deaths.slice(); states.push(s);
120
+ });
121
+ states[0].deaths = new Array(n).fill(0);
122
+ const cv = $('cv'), cs = Math.max(8, Math.floor(720 / Math.max(W, H)));
123
+ cv.width = W * cs; cv.height = H * cs; cv.dataset.cs = cs;
124
+ $('scrub').max = states.length - 1;
125
+ const res = R.result, names = R.names;
126
+ if (n > 2) {
127
+ const order = names.map((nm, j) => j).sort((a, b) => res.ranks[a] - res.ranks[b]);
128
+ $('result').textContent = '混战名次:' + order.map(j => `${res.ranks[j]}. ${names[j]}${res.forfeit[j] ? '(判负)' : ''}`).join(' ') + ` · 种子 ${R.seed}`;
129
+ } else {
130
+ $('result').textContent = res.winner === -1 ? `平局 · 种子 ${R.seed}` :
131
+ `胜者:${names[res.winner]}${res.forfeit.some(Boolean) ? '(对手判负:' + res.forfeit.filter(Boolean).join('/') + ')' : ''} · 种子 ${R.seed}`;
132
+ }
133
+ const log = [];
134
+ states.forEach(s => s.events.forEach(e => {
135
+ const who = names[e.player];
136
+ log.push(e.type === 'death'
137
+ ? `<div>T${s.t - 1} ${who} 阵亡(${DEATH[e.reason]}${e.reason === 'cut' || e.reason === 'head' ? ',' + names[e.by] : ''})</div>`
138
+ : e.type === 'out' ? `<div>T${s.t - 1} ${who} 判负出局(${e.reason === 'crash' ? '程序崩溃' : '错误过多'})</div>`
139
+ : `<div>T${s.t - 1} ${who} 圈地 +${e.cells}</div>`);
140
+ }));
141
+ $('log').innerHTML = log.join('') || '<div>无事件</div>';
142
+ show(0);
143
+ }
144
+
145
+ function show(t) {
146
+ cur = Math.max(0, Math.min(states.length - 1, t));
147
+ const s = states[cur], W = R.config.width, H = R.config.height;
148
+ const cv = $('cv'), ctx = cv.getContext('2d'), cs = +cv.dataset.cs;
149
+ const P = [0, 1, 2, 3], land = P.map(j => css(`--p${j}-land`)), trail = P.map(j => css(`--p${j}-trail`)),
150
+ head = P.map(j => css(`--p${j}`));
151
+ ctx.fillStyle = css('--bg'); ctx.fillRect(0, 0, cv.width, cv.height);
152
+ for (let k = 0; k < W * H; k++) {
153
+ const o = s.owner[k];
154
+ ctx.fillStyle = o < 0 ? css('--cell') : land[o];
155
+ ctx.fillRect((k % W) * cs, Math.floor(k / W) * cs, cs - 1, cs - 1);
156
+ }
157
+ const pr = R.config.protect_radius;
158
+ R.spawns.forEach(([sx, sy], j) => {
159
+ ctx.strokeStyle = head[j]; ctx.lineWidth = 1.5;
160
+ ctx.strokeRect((sx - pr) * cs + 1, (sy - pr) * cs + 1, (2 * pr + 1) * cs - 3, (2 * pr + 1) * cs - 3);
161
+ });
162
+ s.trails.forEach((tr, j) => {
163
+ ctx.fillStyle = trail[j];
164
+ for (const k of tr) ctx.fillRect((k % W) * cs + 1, Math.floor(k / W) * cs + 1, cs - 3, cs - 3);
165
+ });
166
+ s.pos.forEach(([x, y], j) => {
167
+ ctx.fillStyle = head[j];
168
+ ctx.fillRect(x * cs - 1, y * cs - 1, cs + 1, cs + 1);
169
+ ctx.strokeStyle = css('--panel'); ctx.lineWidth = 2;
170
+ ctx.strokeRect(x * cs, y * cs, cs - 1, cs - 1);
171
+ });
172
+ const areas = R.names.map((_, j) => s.owner.reduce((a, o) => a + (o === j), 0));
173
+ $('players').innerHTML = R.names.map((nm, j) => `<div class="player">
174
+ <span class="sw" style="background:${head[j]}"></span><span class="name">${nm}</span><span class="big">${areas[j]}</span>
175
+ <span class="kv">轨迹 ${s.trails[j].length} · 阵亡 ${s.deaths[j]} · 占比 ${(100 * areas[j] / (W * H)).toFixed(1)}%</span></div>`).join('');
176
+ $('bar').innerHTML = areas.map((a, j) => `<span style="width:${100 * a / (W * H)}%;background:${head[j]}"></span>`).join('');
177
+ $('turn').textContent = `回合 ${cur} / ${states.length - 1}`;
178
+ $('scrub').value = cur;
179
+ }
180
+
181
+ function stop() { clearInterval(timer); timer = null; $('play').textContent = '播放'; }
182
+ function toggle() {
183
+ if (timer) return stop();
184
+ if (cur >= states.length - 1) show(0);
185
+ $('play').textContent = '暂停';
186
+ timer = setInterval(() => { if (cur >= states.length - 1) stop(); else show(cur + 1); }, 1000 / +$('speed').value);
187
+ }
188
+
189
+ $('pick').onclick = () => $('file').click();
190
+ $('file').onchange = e => load(e.target.files);
191
+ const drop = $('drop');
192
+ ['dragenter', 'dragover'].forEach(ev => document.addEventListener(ev, e => { e.preventDefault(); drop.classList.add('hot'); }));
193
+ document.addEventListener('dragleave', () => drop.classList.remove('hot'));
194
+ document.addEventListener('drop', e => { e.preventDefault(); drop.classList.remove('hot'); load(e.dataTransfer.files); });
195
+ $('play').onclick = toggle;
196
+ $('prev').onclick = () => { stop(); show(cur - 1); };
197
+ $('next').onclick = () => { stop(); show(cur + 1); };
198
+ $('scrub').oninput = e => { stop(); show(+e.target.value); };
199
+ $('speed').onchange = () => { if (timer) { stop(); toggle(); } };
200
+ $('which').onchange = e => open(+e.target.value);
201
+ document.addEventListener('keydown', e => {
202
+ if (!R) return;
203
+ if (e.key === ' ') { e.preventDefault(); toggle(); }
204
+ if (e.key === 'ArrowLeft') { stop(); show(cur - 1); }
205
+ if (e.key === 'ArrowRight') { stop(); show(cur + 1); }
206
+ });
207
+ matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => R && show(cur));
208
+ </script>
209
+ </body>
210
+ </html>
@@ -0,0 +1,24 @@
1
+ # 各模型的封存代码
2
+
3
+ 都是模型在比赛中写出的原样代码,主办方没有改动。`half1/` 是上半场版本(打中场赛、进步赛里的固定对手池),`half2/` 是下半场版本(打终场赛,决定最终名次)。基准在 `../workspace/baseline.cpp`,出题方参照在 `../tournament/sparring.cpp`。
4
+
5
+ 文件里的 sha256 和 `../results/*/清单.json` 记录的一致。名次和实力值是终场赛的。
6
+
7
+ | 文件名 | 模型 | 短名 | 终场名次 | 实力值 | 上半场行数 | 下半场行数 | 下半场改了没有 |
8
+ |---|---|---|---:|---:|---:|---:|---|
9
+ | `gpt-6-astra.cpp` | GPT-6 Astra | Astra | 1 | 507 | 233 | 241 | 改了 |
10
+ | `gpt-6.1-sol-ultrafast.cpp` | GPT-6.1 Sol(Ultrafast) | SolUF | 2 | 467 | 350 | 356 | 改了 |
11
+ | `gemini-4-argon.cpp` | Gemini 4 Argon Contributor | Gemini | 3 | 355 | 1132 | 1390 | 改了 |
12
+ | `gpt-6.1-sol.cpp` | GPT-6.1 Sol | Sol | 4 | 319 | 78 | 87 | 改了 |
13
+ | `opus-5.5.cpp` | Claude Opus 5.5 | Opus | 5 | 259 | 435 | 476 | 改了 |
14
+ | `muse-wonder-full.cpp` | Muse Wonder Full | Muse | 6 | 55 | 560 | 560 | 没改(和上半场逐字节相同) |
15
+ | `mimo-v2.6-pro.cpp` | 小米 MiMo V2.6 Pro | MiMo | 7 | 29 | 336 | 336 | 没改(和上半场逐字节相同) |
16
+ | `grok-4.7.cpp` | Grok 4.7 | Grok | 8 | 26 | 1099 | 1205 | 改了 |
17
+ | `deepseek-v4.1-flash.cpp` | DeepSeek V4.1 Flash | DeepSeek | 9 | −6 | 367 | 425 | 改了 |
18
+ | `doubao-seed-evolving.cpp` | Doubao-Seed-Evolving | Doubao | 10 | −31 | 402 | 308 | 改了 |
19
+ | `kimi-k3.cpp` | Kimi K3 | Kimi | 11 | −129 | 353 | 487 | 改了 |
20
+ | `haiku-5.5.cpp` | Claude Haiku 5.5 | Haiku | 12 | −149 | 210 | 210 | 没改(和上半场逐字节相同) |
21
+ | `qwen-3.8-max.cpp` | 通义千问 Qwen 3.8 Max | Qwen | 13 | −205 | 537 | 392 | 改了 |
22
+ | `glm-5.3.cpp` | 智谱 GLM 5.3 | GLM | 14 | −315 | 551 | 651 | 改了 |
23
+
24
+ 短名是成绩表、回放和配置里用的名字。用 `../workspace/arena.py` 可以直接拿它们当对手,例如在 `workspace/` 里运行 `python arena.py batch submission/bot.cpp ../bots/half2/gpt-6-astra.cpp --games 20`。