dsh-eval-guard 0.2.2 → 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.
Files changed (224) hide show
  1. package/README.md +91 -13
  2. package/assets/bomberman/workspace.zip +0 -0
  3. package/assets/commit0/instance.json +30 -0
  4. package/assets/commit0/workspace.zip +0 -0
  5. package/assets/ctf/workspace.zip +0 -0
  6. package/assets/featurebench/hidden/test_regression.py +690 -0
  7. package/assets/featurebench/instance.json +16 -0
  8. package/assets/featurebench/workspace.zip +0 -0
  9. package/assets/swe-bench-pro/hidden/test.patch +36 -0
  10. package/assets/swe-bench-pro/instance.json +10 -0
  11. package/assets/swe-bench-pro/workspace.zip +0 -0
  12. package/assets/tank/workspace.zip +0 -0
  13. package/eval/README.md +18 -0
  14. package/eval/arena/arena/README.md +21 -0
  15. package/eval/arena/arena/bt.py +104 -0
  16. package/eval/arena/arena/clock.py +15 -0
  17. package/eval/arena/arena/core.py +265 -0
  18. package/eval/arena/arena/run.cmd +24 -0
  19. package/eval/arena/arena/run.sh +11 -0
  20. package/eval/arena/arena/schedule.py +79 -0
  21. package/eval/arena/arena/tournament.py +183 -0
  22. package/eval/arena/bomberman/bots/README.md +16 -0
  23. package/eval/arena/bomberman/bots/deepseek-v4.1-flash.cpp +447 -0
  24. package/eval/arena/bomberman/bots/doubao-seed-2.1.cpp +542 -0
  25. package/eval/arena/bomberman/bots/glm-5.3.cpp +406 -0
  26. package/eval/arena/bomberman/bots/gpt-6-astra.cpp +768 -0
  27. package/eval/arena/bomberman/bots/gpt-6-sol.cpp +444 -0
  28. package/eval/arena/bomberman/bots/kimi-k3.cpp +432 -0
  29. package/eval/arena/bomberman/bots/mimo-v2.6-pro.cpp +529 -0
  30. package/eval/arena/bomberman/bots/opus-5.5.cpp +548 -0
  31. package/eval/arena/bomberman/bots/qwen-3.8-max.cpp +836 -0
  32. package/eval/arena/bomberman/tournament/README.md +11 -0
  33. package/eval/arena/bomberman/tournament/bt.py +56 -0
  34. package/eval/arena/bomberman/tournament/evaluate.py +199 -0
  35. package/eval/arena/bomberman/tournament/run.py +79 -0
  36. package/eval/arena/bomberman/tournament/schedule-2to10.json.gz +0 -0
  37. package/eval/arena/bomberman/workspace/MANIFEST.json +13 -0
  38. package/eval/arena/bomberman/workspace/README.md +29 -0
  39. package/eval/arena/bomberman/workspace/RULES.md +100 -0
  40. package/eval/arena/bomberman/workspace/baseline.cpp +240 -0
  41. package/eval/arena/bomberman/workspace/clock.py +25 -0
  42. package/eval/arena/bomberman/workspace/engine/arena.py +198 -0
  43. package/eval/arena/bomberman/workspace/engine/build.py +35 -0
  44. package/eval/arena/bomberman/workspace/engine/engine.py +256 -0
  45. package/eval/arena/bomberman/workspace/engine/verify_replay.py +24 -0
  46. package/eval/arena/bomberman/workspace/run.cmd +28 -0
  47. package/eval/arena/bomberman/workspace/run.sh +16 -0
  48. package/eval/arena/bomberman/workspace/viewer/index.html +42 -0
  49. package/eval/arena/ctf/bots/README.md +18 -0
  50. package/eval/arena/ctf/bots/deepseek-v4.1-flash.cpp +601 -0
  51. package/eval/arena/ctf/bots/doubao-seed-2.1-turbo.cpp +320 -0
  52. package/eval/arena/ctf/bots/fable-5.1.cpp +634 -0
  53. package/eval/arena/ctf/bots/glm-5.3.cpp +689 -0
  54. package/eval/arena/ctf/bots/gpt-6-astra.cpp +657 -0
  55. package/eval/arena/ctf/bots/gpt-6.1-sol.cpp +725 -0
  56. package/eval/arena/ctf/bots/kimi-k3.cpp +667 -0
  57. package/eval/arena/ctf/bots/mimo-v2.6-pro.cpp +533 -0
  58. package/eval/arena/ctf/bots/opus-5.5.cpp +645 -0
  59. package/eval/arena/ctf/bots/qwen-3.8-max.cpp +726 -0
  60. package/eval/arena/ctf/bots/sonnet-5.5.cpp +569 -0
  61. package/eval/arena/ctf/tournament/README.md +15 -0
  62. package/eval/arena/ctf/tournament/formal-2to8.json +31 -0
  63. package/eval/arena/ctf/tournament/formal-9to15.json +31 -0
  64. package/eval/arena/ctf/workspace/README.md +41 -0
  65. package/eval/arena/ctf/workspace/RULES.md +119 -0
  66. package/eval/arena/ctf/workspace/arena.py +186 -0
  67. package/eval/arena/ctf/workspace/baseline.cpp +257 -0
  68. package/eval/arena/ctf/workspace/clock.py +15 -0
  69. package/eval/arena/ctf/workspace/core.py +265 -0
  70. package/eval/arena/ctf/workspace/engine.py +389 -0
  71. package/eval/arena/ctf/workspace/run.cmd +24 -0
  72. package/eval/arena/ctf/workspace/run.sh +11 -0
  73. package/eval/arena/ctf/workspace/viewer.html +71 -0
  74. package/eval/arena/land/bots/README.md +17 -0
  75. package/eval/arena/land/bots/deepseek-4.1f@workbuddy#1/bot.cpp +608 -0
  76. package/eval/arena/land/bots/glm-5.3@workbuddy#1/bot.cpp +521 -0
  77. package/eval/arena/land/bots/gpt-6-astra@codex#1/bot.cpp +373 -0
  78. package/eval/arena/land/bots/gpt-6-sol@codex#1/bot.cpp +341 -0
  79. package/eval/arena/land/bots/kimi-k3@workbuddy#1/bot.cpp +492 -0
  80. package/eval/arena/land/bots/mimo-2.6-pro@zcode#1/bot.cpp +652 -0
  81. package/eval/arena/land/bots/mystery-model@zcode#1/bot.cpp +466 -0
  82. package/eval/arena/land/bots/opus-5.5@claude-code#1/bot.cpp +630 -0
  83. package/eval/arena/land/bots/sonnet-5@claude-code#1/bot.cpp +619 -0
  84. package/eval/arena/land/tournament/make_replay_page.py +17 -0
  85. package/eval/arena/land/tournament/rate.py +290 -0
  86. package/eval/arena/land/tournament/round_robin.py +172 -0
  87. package/eval/arena/land/tournament/sparring.cpp +356 -0
  88. package/eval/arena/land/workspace/RULES.md +114 -0
  89. package/eval/arena/land/workspace/TASK.md +46 -0
  90. package/eval/arena/land/workspace/anchors/.build/greedy_f2e6d2dea5ca +0 -0
  91. package/eval/arena/land/workspace/anchors/greedy.cpp +194 -0
  92. package/eval/arena/land/workspace/anchors/random.cpp +80 -0
  93. package/eval/arena/land/workspace/clock.py +64 -0
  94. package/eval/arena/land/workspace/engine/build.py +33 -0
  95. package/eval/arena/land/workspace/engine/game.py +273 -0
  96. package/eval/arena/land/workspace/engine/referee.py +308 -0
  97. package/eval/arena/land/workspace/starter/.build/bot_be32b45b4699 +0 -0
  98. package/eval/arena/land/workspace/starter/bot.cpp +82 -0
  99. package/eval/arena/land/workspace/viewer/viewer.html +210 -0
  100. package/eval/arena/tank/bots/README.md +24 -0
  101. package/eval/arena/tank/bots/half1/deepseek-v4.1-flash.cpp +367 -0
  102. package/eval/arena/tank/bots/half1/doubao-seed-evolving.cpp +402 -0
  103. package/eval/arena/tank/bots/half1/gemini-4-argon.cpp +1132 -0
  104. package/eval/arena/tank/bots/half1/glm-5.3.cpp +551 -0
  105. package/eval/arena/tank/bots/half1/gpt-6-astra.cpp +233 -0
  106. package/eval/arena/tank/bots/half1/gpt-6.1-sol-ultrafast.cpp +350 -0
  107. package/eval/arena/tank/bots/half1/gpt-6.1-sol.cpp +78 -0
  108. package/eval/arena/tank/bots/half1/grok-4.7.cpp +1099 -0
  109. package/eval/arena/tank/bots/half1/haiku-5.5.cpp +210 -0
  110. package/eval/arena/tank/bots/half1/kimi-k3.cpp +353 -0
  111. package/eval/arena/tank/bots/half1/mimo-v2.6-pro.cpp +336 -0
  112. package/eval/arena/tank/bots/half1/muse-wonder-full.cpp +560 -0
  113. package/eval/arena/tank/bots/half1/opus-5.5.cpp +435 -0
  114. package/eval/arena/tank/bots/half1/qwen-3.8-max.cpp +537 -0
  115. package/eval/arena/tank/bots/half2/deepseek-v4.1-flash.cpp +425 -0
  116. package/eval/arena/tank/bots/half2/doubao-seed-evolving.cpp +308 -0
  117. package/eval/arena/tank/bots/half2/gemini-4-argon.cpp +1390 -0
  118. package/eval/arena/tank/bots/half2/glm-5.3.cpp +651 -0
  119. package/eval/arena/tank/bots/half2/gpt-6-astra.cpp +241 -0
  120. package/eval/arena/tank/bots/half2/gpt-6.1-sol-ultrafast.cpp +356 -0
  121. package/eval/arena/tank/bots/half2/gpt-6.1-sol.cpp +87 -0
  122. package/eval/arena/tank/bots/half2/grok-4.7.cpp +1205 -0
  123. package/eval/arena/tank/bots/half2/haiku-5.5.cpp +210 -0
  124. package/eval/arena/tank/bots/half2/kimi-k3.cpp +487 -0
  125. package/eval/arena/tank/bots/half2/mimo-v2.6-pro.cpp +336 -0
  126. package/eval/arena/tank/bots/half2/muse-wonder-full.cpp +560 -0
  127. package/eval/arena/tank/bots/half2/opus-5.5.cpp +476 -0
  128. package/eval/arena/tank/bots/half2/qwen-3.8-max.cpp +392 -0
  129. package/eval/arena/tank/tournament/README.md +24 -0
  130. package/eval/arena/tank/tournament/bt.py +218 -0
  131. package/eval/arena/tank/tournament/final.json +202 -0
  132. package/eval/arena/tank/tournament/halftime.json +146 -0
  133. package/eval/arena/tank/tournament/progress.json +911 -0
  134. package/eval/arena/tank/tournament/schedule.py +100 -0
  135. package/eval/arena/tank/tournament/sparring.cpp +928 -0
  136. package/eval/arena/tank/tournament/tournament.py +1426 -0
  137. package/eval/arena/tank/workspace/README.md +169 -0
  138. package/eval/arena/tank/workspace/RULES.md +443 -0
  139. package/eval/arena/tank/workspace/arena.py +862 -0
  140. package/eval/arena/tank/workspace/baseline.cpp +259 -0
  141. package/eval/arena/tank/workspace/clock.py +103 -0
  142. package/eval/arena/tank/workspace/core.py +712 -0
  143. package/eval/arena/tank/workspace/engine.py +573 -0
  144. package/eval/arena/tank/workspace/history/.lock +0 -0
  145. package/eval/arena/tank/workspace/history/20261017-193610-08715287f91f-bot.cpp +109 -0
  146. package/eval/arena/tank/workspace/history/20261017-193619-f00b2d5635fa-gpt-6-astra.cpp +241 -0
  147. package/eval/arena/tank/workspace/history/20261017-193643-63319e0a2b1a-gpt-6_1-sol-ultrafast.cpp +356 -0
  148. package/eval/arena/tank/workspace/history/20261017-193647-41c1341d0a8a-gemini-4-argon.cpp +1390 -0
  149. package/eval/arena/tank/workspace/history/20261017-193652-e34c0f45539a-gpt-6_1-sol.cpp +87 -0
  150. package/eval/arena/tank/workspace/history/20261017-193654-c026348ce96e-opus-5_5.cpp +476 -0
  151. package/eval/arena/tank/workspace/history/20261017-193655-4a21366e94d1-muse-wonder-full.cpp +560 -0
  152. package/eval/arena/tank/workspace/history/20261017-193657-dbc4e9969c52-mimo-v2_6-pro.cpp +336 -0
  153. package/eval/arena/tank/workspace/history/20261017-193703-723cd036eb31-grok-4_7.cpp +1205 -0
  154. package/eval/arena/tank/workspace/history/20261017-193707-1ebbb227be68-deepseek-v4_1-flash.cpp +425 -0
  155. package/eval/arena/tank/workspace/history/20261017-193709-aeb0e7202ad5-doubao-seed-evolving.cpp +308 -0
  156. package/eval/arena/tank/workspace/history/20261017-193710-b21ebeb9bc76-kimi-k3.cpp +487 -0
  157. package/eval/arena/tank/workspace/history/20261017-193712-54cd86531905-haiku-5_5.cpp +210 -0
  158. package/eval/arena/tank/workspace/history/20261017-193714-efdb8e35c0bf-qwen-3_8-max.cpp +392 -0
  159. package/eval/arena/tank/workspace/history/20261017-193715-681423c23fdb-glm-5_3.cpp +651 -0
  160. package/eval/arena/tank/workspace/history/20261017-193717-1a8b3c75181d-sparring.cpp +928 -0
  161. package/eval/arena/tank/workspace/history/20261017-193720-1e0803a541e8-baseline.cpp +259 -0
  162. package/eval/arena/tank/workspace/history/log.jsonl +103 -0
  163. package/eval/arena/tank/workspace/judge/cycles.hpp +53 -0
  164. package/eval/arena/tank/workspace/judge/fast.cpp +337 -0
  165. package/eval/arena/tank/workspace/judge/referee.cpp +125 -0
  166. package/eval/arena/tank/workspace/judge/speed.cpp +99 -0
  167. package/eval/arena/tank/workspace/run.cmd +31 -0
  168. package/eval/arena/tank/workspace/run.sh +23 -0
  169. package/eval/arena/tank/workspace/tank.hpp +1762 -0
  170. package/eval/arena/tank/workspace/tank_main.cpp +215 -0
  171. package/eval/arena/tank/workspace/viewer.html +1150 -0
  172. package/eval/arena/warehouse/workspace/SPEC.md +130 -0
  173. package/eval/arena/warehouse/workspace/TASK.md +71 -0
  174. package/eval/arena/warehouse/workspace/baseline/common.h +88 -0
  175. package/eval/arena/warehouse/workspace/baseline/main.cpp +27 -0
  176. package/eval/arena/warehouse/workspace/baseline/old_stuff.cpp +87 -0
  177. package/eval/arena/warehouse/workspace/baseline/sim.cpp +427 -0
  178. package/eval/arena/warehouse/workspace/baseline/util.cpp +220 -0
  179. package/eval/arena/warehouse/workspace/clock.py +42 -0
  180. package/eval/arena/warehouse/workspace/src/common.h +88 -0
  181. package/eval/arena/warehouse/workspace/src/main.cpp +27 -0
  182. package/eval/arena/warehouse/workspace/src/old_stuff.cpp +87 -0
  183. package/eval/arena/warehouse/workspace/src/sim.cpp +427 -0
  184. package/eval/arena/warehouse/workspace/src/util.cpp +220 -0
  185. package/eval/arena/warehouse/workspace/tools/bench.py +119 -0
  186. package/eval/arena/warehouse/workspace/tools/check_input.py +161 -0
  187. package/eval/arena/warehouse/workspace/tools/gen.py +132 -0
  188. package/eval/arena/warehouse/workspace/tools/measure.py +107 -0
  189. package/eval/arena/warehouse/workspace/workloads/public/pub_crowded.in +1420 -0
  190. package/eval/arena/warehouse/workspace/workloads/public/pub_crowded.out +9162 -0
  191. package/eval/arena/warehouse/workspace/workloads/public/pub_dynamic.in +1465 -0
  192. package/eval/arena/warehouse/workspace/workloads/public/pub_dynamic.out +3439 -0
  193. package/eval/arena/warehouse/workspace/workloads/public/pub_flood.in +2757 -0
  194. package/eval/arena/warehouse/workspace/workloads/public/pub_flood.out +3126 -0
  195. package/eval/arena/warehouse/workspace/workloads/public/pub_small.in +731 -0
  196. package/eval/arena/warehouse/workspace/workloads/public/pub_small.out +2578 -0
  197. package/eval/arena/warehouse/workspace/workloads/public/pub_wide.in +528 -0
  198. package/eval/arena/warehouse/workspace/workloads/public/pub_wide.out +2026 -0
  199. package/eval/bomberman.py +20 -0
  200. package/eval/commit0.py +21 -0
  201. package/eval/common.py +61 -0
  202. package/eval/ctf.py +20 -0
  203. package/eval/featurebench.py +25 -0
  204. package/eval/land.py +20 -0
  205. package/eval/legacy/bomberman-prices.json +4 -0
  206. package/eval/legacy/bomberman.py +161 -0
  207. package/eval/legacy/commit0.py +91 -0
  208. package/eval/legacy/ctf-prices.json +4 -0
  209. package/eval/legacy/ctf.py +178 -0
  210. package/eval/legacy/featurebench.py +103 -0
  211. package/eval/legacy/land-prices.json +4 -0
  212. package/eval/legacy/land.py +177 -0
  213. package/eval/legacy/swe_bench_pro.py +116 -0
  214. package/eval/legacy/tank-prices.json +3 -0
  215. package/eval/legacy/tank.py +408 -0
  216. package/eval/legacy/warehouse-prices.json +4 -0
  217. package/eval/legacy/warehouse.py +365 -0
  218. package/eval/swe-bench-pro.py +25 -0
  219. package/eval/tank.py +20 -0
  220. package/eval/warehouse.py +20 -0
  221. package/lib/client/index.js +376 -63
  222. package/lib/client/index.js.map +1 -1
  223. package/lib/index.js +684 -27
  224. package/package.json +2 -1
@@ -0,0 +1,220 @@
1
+ // util.cpp 工具函数
2
+ #include "common.h"
3
+
4
+ int DX4[4] = {0, 1, 0, -1};
5
+ int DY4[4] = {-1, 0, 1, 0}; // 上 右 下 左 !!!顺序不能改!!!
6
+
7
+ vector<string> SPLIT(const string& s) {
8
+ vector<string> res;
9
+ string cur = "";
10
+ for (int i = 0; i < (int)s.size(); i++) {
11
+ char c = s[i];
12
+ if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f') {
13
+ if (cur != "") {
14
+ res.push_back(cur);
15
+ cur = "";
16
+ }
17
+ } else {
18
+ cur = cur + c;
19
+ }
20
+ }
21
+ if (cur != "") res.push_back(cur);
22
+ return res;
23
+ }
24
+
25
+ int S2I(const string& s) {
26
+ return atoi(s.c_str());
27
+ }
28
+
29
+ string I2S(long long v) {
30
+ stringstream ss;
31
+ ss << v;
32
+ return ss.str();
33
+ }
34
+
35
+ string RNAME(int i) {
36
+ return "R" + I2S(i);
37
+ }
38
+
39
+ bool INMAP(int x, int y) {
40
+ if (x < 0) return false;
41
+ if (y < 0) return false;
42
+ if (x >= W) return false;
43
+ if (y >= H) return false;
44
+ return true;
45
+ }
46
+
47
+ bool ISWALL(int x, int y) {
48
+ return MAP[y][x] == '#';
49
+ }
50
+
51
+ // 能不能走(在图内 不是墙 没封)
52
+ bool OK(int x, int y) {
53
+ if (!INMAP(x, y)) return false;
54
+ if (ISWALL(x, y)) return false;
55
+ if (BLK.find(make_pair(x, y)) != BLK.end()) return false;
56
+ return true;
57
+ }
58
+
59
+ // 格子上有没有别的车(死了的不算)
60
+ bool OCC(int x, int y, int except) {
61
+ for (int i = 0; i < NR; i++) {
62
+ if (i == except) continue;
63
+ if (RST[i] == "DEAD") continue;
64
+ if (RX[i] == x && RY[i] == y) return true;
65
+ }
66
+ return false;
67
+ }
68
+
69
+ // BFS 距离表 d[y][x] 起点不管能不能走都是 0
70
+ vector<vector<int> > BFS(int sx, int sy) {
71
+ #if FLAG_FAST_BFS
72
+ return bfs_fast_cached(sx, sy);
73
+ #endif
74
+ vector<vector<int> > d;
75
+ for (int y = 0; y < H; y++) {
76
+ vector<int> row;
77
+ for (int x = 0; x < W; x++) row.push_back(BIGNUM);
78
+ d.push_back(row);
79
+ }
80
+ queue<pair<int, int> > q;
81
+ d[sy][sx] = 0;
82
+ q.push(make_pair(sx, sy));
83
+ while (q.size() > 0) {
84
+ pair<int, int> p = q.front();
85
+ q.pop();
86
+ for (int k = 0; k < 4; k++) {
87
+ int nx = p.first + DX4[k];
88
+ int ny = p.second + DY4[k];
89
+ if (OK(nx, ny) == false) continue;
90
+ if (d[ny][nx] != BIGNUM) continue;
91
+ d[ny][nx] = d[p.second][p.first] + 1;
92
+ q.push(make_pair(nx, ny));
93
+ }
94
+ }
95
+ return d;
96
+ }
97
+
98
+ // BFS2: 给移动用的(从目标往回搜)。张: 和 BFS 一样,但是别合并,上次合并出过事
99
+ vector<vector<int> > BFS2(int sx, int sy) {
100
+ vector<vector<int> > d(H, vector<int>(W, BIGNUM));
101
+ list<pair<int, int> > q;
102
+ d[sy][sx] = 0;
103
+ q.push_back(make_pair(sx, sy));
104
+ while (!q.empty()) {
105
+ int cx = q.front().first, cy = q.front().second;
106
+ q.pop_front();
107
+ for (int k = 0; k < 4; k++) {
108
+ int nx = cx + DX4[k], ny = cy + DY4[k];
109
+ if (!OK(nx, ny)) continue;
110
+ if (d[ny][nx] < BIGNUM) continue;
111
+ d[ny][nx] = d[cy][cx] + 1;
112
+ q.push_back(make_pair(nx, ny));
113
+ }
114
+ }
115
+ return d;
116
+ }
117
+
118
+ // a 到 b 的距离
119
+ int DIST(int ax, int ay, int bx, int by) {
120
+ if (ax == bx && ay == by) return 0;
121
+ if (!OK(bx, by)) return BIGNUM;
122
+ vector<vector<int> > d = BFS(ax, ay);
123
+ return d[by][bx];
124
+ }
125
+
126
+ vector<pair<int, int> > ALL_CHG() {
127
+ vector<pair<int, int> > v;
128
+ for (int y = 0; y < H; y++) {
129
+ for (int x = 0; x < W; x++) {
130
+ if (MAP[y][x] == 'C') v.push_back(make_pair(x, y));
131
+ }
132
+ }
133
+ return v;
134
+ }
135
+
136
+ // 到最近充电桩的距离(2023-03 张: 从 sim.cpp 挪过来的,BFS 先内联着,回头再改)
137
+ int CHG_DIST(int x, int y) {
138
+ vector<vector<int> > d(H, vector<int>(W, BIGNUM));
139
+ queue<pair<int, int> > q;
140
+ d[y][x] = 0;
141
+ q.push(make_pair(x, y));
142
+ while (!q.empty()) {
143
+ pair<int, int> p = q.front();
144
+ q.pop();
145
+ for (int k = 0; k < 4; k++) {
146
+ int nx = p.first + DX4[k];
147
+ int ny = p.second + DY4[k];
148
+ if (!INMAP(nx, ny)) continue;
149
+ if (MAP[ny][nx] == '#') continue;
150
+ if (BLK.count(make_pair(nx, ny))) continue;
151
+ if (d[ny][nx] != BIGNUM) continue;
152
+ d[ny][nx] = d[p.second][p.first] + 1;
153
+ q.push(p);
154
+ q.back() = make_pair(nx, ny);
155
+ }
156
+ }
157
+ int best = BIGNUM;
158
+ vector<pair<int, int> > cs = ALL_CHG();
159
+ for (int i = 0; i < (int)cs.size(); i++) {
160
+ if (!OK(cs[i].first, cs[i].second)) continue;
161
+ if (d[cs[i].second][cs[i].first] < best) best = d[cs[i].second][cs[i].first];
162
+ }
163
+ return best;
164
+ }
165
+
166
+ // 最近的充电桩,没有就 (-1,-1)
167
+ pair<int, int> CHG_NEAR(int x, int y) {
168
+ vector<vector<int> > d = BFS(x, y);
169
+ int best = BIGNUM;
170
+ pair<int, int> res = make_pair(-1, -1);
171
+ vector<pair<int, int> > cs = ALL_CHG();
172
+ for (int i = 0; i < (int)cs.size(); i++) {
173
+ if (!OK(cs[i].first, cs[i].second)) continue;
174
+ if (d[cs[i].second][cs[i].first] < best) {
175
+ best = d[cs[i].second][cs[i].first];
176
+ res = cs[i];
177
+ }
178
+ }
179
+ return res;
180
+ }
181
+
182
+ int findRobot(const string& name) {
183
+ for (int i = 0; i < NR; i++) {
184
+ if (RNAME(i) == name) return i;
185
+ }
186
+ return -1;
187
+ }
188
+
189
+ // 写日志
190
+ void writeLog(int t, const string& msg) {
191
+ string line = I2S(t) + " " + msg;
192
+ LOGBUF.push_back(line);
193
+ // 顺便统计一下,省得到处加
194
+ vector<string> w = SPLIT(msg);
195
+ if (w.size() > 0) {
196
+ if (w[0] == "DELIVER") {
197
+ long long lat = S2I(w[w.size() - 1]);
198
+ CNT[0]++;
199
+ CNT[4] += lat;
200
+ if (lat > CNT[5]) CNT[5] = lat;
201
+ } else if (w[0] == "LOST") {
202
+ CNT[1]++;
203
+ } else if (w[0] == "REJECT") {
204
+ CNT[2]++;
205
+ }
206
+ }
207
+ #if FLAG_VERBOSE
208
+ cerr << line << endl;
209
+ #endif
210
+ }
211
+
212
+ void writeRaw(const string& msg) {
213
+ LOGBUF.push_back(msg);
214
+ }
215
+
216
+ void dumpLog() {
217
+ for (int i = 0; i < (int)LOGBUF.size(); i++) {
218
+ cout << LOGBUF[i] << endl;
219
+ }
220
+ }
@@ -0,0 +1,119 @@
1
+ """本地测试工具:编译 src/,运行公开负载,逐字节比对输出,报告 CPU 时间。
2
+
3
+ python tools/bench.py # 编译并测试全部公开负载
4
+ python tools/bench.py --baseline # 同时报告加速比。基线耗时第一次实测(约 40 秒),之后用缓存
5
+ python tools/bench.py --baseline --fresh # 忽略缓存,重新测量基线耗时
6
+ python tools/bench.py --only pub_small --repeat 3
7
+
8
+ Build and run all public workloads, compare output byte by byte, report CPU time.
9
+ """
10
+ import argparse
11
+ import json
12
+ import os
13
+ import sys
14
+
15
+ HERE = os.path.dirname(os.path.abspath(__file__))
16
+ ROOT = os.path.dirname(HERE)
17
+ sys.path.insert(0, HERE)
18
+ import measure # noqa: E402
19
+ if hasattr(sys.stdout, 'reconfigure'):
20
+ sys.stdout.reconfigure(encoding='utf-8') # Windows 控制台默认 GBK,统一输出 UTF-8
21
+
22
+ WORKLOADS = os.path.join(ROOT, 'workloads', 'public')
23
+
24
+
25
+ CACHE = os.path.join(ROOT, 'build', 'baseline_times.json')
26
+
27
+
28
+ def baseline_stamp():
29
+ d = os.path.join(ROOT, 'baseline')
30
+ return ';'.join('%s:%d:%d' % (f, os.path.getsize(os.path.join(d, f)), int(os.path.getmtime(os.path.join(d, f))))
31
+ for f in sorted(os.listdir(d)))
32
+
33
+
34
+ def load_cache():
35
+ """基线不会变,测过一次的耗时就记下来;基线代码或负载文件有变化时自动重测。"""
36
+ try:
37
+ with open(CACHE, encoding='utf-8') as f:
38
+ data = json.load(f)
39
+ return data['times'] if data.get('stamp') == baseline_stamp() else {}
40
+ except (OSError, ValueError, KeyError):
41
+ return {}
42
+
43
+
44
+ def save_cache(times):
45
+ os.makedirs(os.path.dirname(CACHE), exist_ok=True)
46
+ with open(CACHE, 'w', encoding='utf-8') as f:
47
+ json.dump({'stamp': baseline_stamp(), 'times': times}, f, indent=1)
48
+
49
+
50
+ def first_diff(a, b):
51
+ la, lb = measure.normalized(a).split(b'\n'), measure.normalized(b).split(b'\n')
52
+ for i in range(max(len(la), len(lb))):
53
+ x = la[i] if i < len(la) else b'<EOF>'
54
+ y = lb[i] if i < len(lb) else b'<EOF>'
55
+ if x != y:
56
+ return i + 1, x.decode(errors='replace'), y.decode(errors='replace')
57
+ return None
58
+
59
+
60
+ def main():
61
+ ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
62
+ ap.add_argument('--baseline', action='store_true', help='同时测量基线耗时并计算加速比 / also time baseline/')
63
+ ap.add_argument('--only', help='只测某个负载 / only this workload')
64
+ ap.add_argument('--repeat', type=int, default=1, help='每个负载运行次数,取中位数 / runs per workload (median)')
65
+ ap.add_argument('--fresh', action='store_true', help='忽略缓存,重新测量基线耗时 / re-time baseline')
66
+ args = ap.parse_args()
67
+
68
+ names = sorted(f[:-3] for f in os.listdir(WORKLOADS) if f.endswith('.in'))
69
+ if args.only:
70
+ names = [n for n in names if n == args.only]
71
+ exe = measure.build(os.path.join(ROOT, 'src'), os.path.join(ROOT, 'build', 'sim' + measure.EXE))
72
+ base = None
73
+ if args.baseline: # 基线代码不会变,编译过就不再重新编译
74
+ bsrc, base = os.path.join(ROOT, 'baseline'), os.path.join(ROOT, 'build', 'baseline' + measure.EXE)
75
+ newest = max(os.path.getmtime(os.path.join(bsrc, f)) for f in os.listdir(bsrc))
76
+ if not os.path.exists(base) or os.path.getmtime(base) < newest:
77
+ measure.build(bsrc, base)
78
+ cache = load_cache() if base else {}
79
+
80
+ all_ok = True
81
+ total, total_base = 0.0, 0.0
82
+ print('%-14s %-6s %10s %10s %9s %s' % ('负载', '结果', 'CPU秒', '基线CPU秒', '加速比', '内存MB'))
83
+ for n in names:
84
+ inp = os.path.join(WORKLOADS, n + '.in')
85
+ with open(os.path.join(WORKLOADS, n + '.out'), 'rb') as f:
86
+ expected = f.read()
87
+ runs = [measure.run(exe, inp) for _ in range(args.repeat)]
88
+ runs.sort(key=lambda r: r['cpu'])
89
+ r = runs[len(runs) // 2]
90
+ ok = all(x['digest'] == measure.digest(expected) and x['code'] == 0 for x in runs)
91
+ all_ok &= ok
92
+ total += r['cpu']
93
+ bcpu = ''
94
+ speed = ''
95
+ if base:
96
+ key = '%s|%d|%d' % (n, os.path.getsize(inp), int(os.path.getmtime(inp)))
97
+ if args.fresh or key not in cache:
98
+ cache[key] = measure.run(base, inp)['cpu']
99
+ save_cache(cache)
100
+ total_base += cache[key]
101
+ bcpu = '%.3f' % cache[key]
102
+ speed = '%.1fx' % (cache[key] / max(r['cpu'], 1e-3))
103
+ print('%-14s %-6s %10.3f %10s %9s %.0f' % (n, '正确' if ok else '错误', r['cpu'], bcpu, speed, r['mem_mb']))
104
+ if not ok:
105
+ bad = next(x for x in runs if x['digest'] != measure.digest(expected) or x['code'] != 0)
106
+ if bad['code'] != 0:
107
+ print(' 退出码 / exit code %d' % bad['code'])
108
+ d = first_diff(expected, bad['output'])
109
+ if d:
110
+ print(' 第 %d 行不同 / first difference at line %d' % (d[0], d[0]))
111
+ print(' 期望 expected: %s' % d[1][:200])
112
+ print(' 实际 actual: %s' % d[2][:200])
113
+ print('合计 CPU 秒 / total CPU s: %.3f' % total + (' 基线 / baseline: %.3f' % total_base if base else ''))
114
+ print('全部正确 / ALL CORRECT' if all_ok else '存在错误 / SOME OUTPUTS ARE WRONG')
115
+ sys.exit(0 if all_ok else 1)
116
+
117
+
118
+ if __name__ == '__main__':
119
+ main()
@@ -0,0 +1,161 @@
1
+ """输入合法性检查器:检查一个输入文件是否符合 SPEC.md 的输入约定,以及击穿用例的规模上限。
2
+
3
+ python tools/check_input.py 用例.in # 按击穿用例的上限检查
4
+ python tools/check_input.py 用例.in --full # 按正式负载的上限检查(W,H ≤ 200 等)
5
+ python tools/check_input.py 用例.in --p2 # 第二阶段格式(PARAMS 末尾多 agingEvery carryCost)
6
+
7
+ 合法时输出 OK 并以 0 退出;否则输出第一个问题并以 1 退出。
8
+
9
+ 格式要求(比 SPEC 更严格的地方是为了避免歧义):
10
+ * 每一项之间恰好一个空格,行首行尾没有空格,没有空行;换行用 \\n 或 \\r\\n;
11
+ * 整数写成标准十进制(可以有负号,没有前导零、没有加号)。
12
+ """
13
+ import re
14
+ import sys
15
+ if hasattr(sys.stdout, 'reconfigure'):
16
+ sys.stdout.reconfigure(encoding='utf-8') # Windows 控制台默认 GBK,统一输出 UTF-8
17
+
18
+ INT = re.compile(r'^(0|-?[1-9][0-9]*)$')
19
+ POS = re.compile(r'^[1-9][0-9]*$')
20
+
21
+ LIMITS_HACK = dict(WH=60, T=3000, R=40, E=20000)
22
+ LIMITS_FULL = dict(WH=200, T=20000, R=300, E=200000)
23
+
24
+
25
+ class Bad(Exception):
26
+ pass
27
+
28
+
29
+ def check(text, full=False, p2=False):
30
+ lim = LIMITS_FULL if full else LIMITS_HACK
31
+ lines = text.replace('\r\n', '\n').split('\n')
32
+ if lines and lines[-1] == '':
33
+ lines.pop()
34
+ pos = [0]
35
+
36
+ def nxt(what):
37
+ if pos[0] >= len(lines):
38
+ raise Bad('第 %d 行:文件提前结束,缺少%s' % (pos[0] + 1, what))
39
+ s = lines[pos[0]]
40
+ pos[0] += 1
41
+ if s != s.strip(' ') or ' ' in s or '\t' in s:
42
+ raise Bad('第 %d 行:多余的空白' % pos[0])
43
+ return s
44
+
45
+ def ints(tokens, lo, hi, names):
46
+ out = []
47
+ for t, (a, b), n in zip(tokens, zip(lo, hi), names):
48
+ if not INT.match(t):
49
+ raise Bad('第 %d 行:%s 不是标准整数:%r' % (pos[0], n, t))
50
+ v = int(t)
51
+ if not a <= v <= b:
52
+ raise Bad('第 %d 行:%s=%d 超出范围 [%d, %d]' % (pos[0], n, v, a, b))
53
+ out.append(v)
54
+ return out
55
+
56
+ def expect(s, k, what):
57
+ tok = s.split(' ')
58
+ if len(tok) != k:
59
+ raise Bad('第 %d 行:%s 应有 %d 项,实际 %d 项' % (pos[0], what, k, len(tok)))
60
+ return tok
61
+
62
+ W, H, T = ints(expect(nxt('首行'), 3, '首行 W H T'), [3, 3, 1], [lim['WH'], lim['WH'], lim['T']], ['W', 'H', 'T'])
63
+ g = []
64
+ for y in range(H):
65
+ row = nxt('地图')
66
+ if len(row) != W or set(row) - set('.#C'):
67
+ raise Bad('第 %d 行:地图行必须恰好 %d 个字符,只能是 . # C' % (pos[0], W))
68
+ g.append(row)
69
+ for x in range(W):
70
+ if g[0][x] != '#' or g[H - 1][x] != '#':
71
+ raise Bad('地图四周必须是墙')
72
+ for y in range(H):
73
+ if g[y][0] != '#' or g[y][W - 1] != '#':
74
+ raise Bad('地图四周必须是墙')
75
+
76
+ tok = expect(nxt('PARAMS'), 9 if p2 else 7, 'PARAMS 行')
77
+ if tok[0] != 'PARAMS':
78
+ raise Bad('第 %d 行:应以 PARAMS 开头' % pos[0])
79
+ names = ['batteryMax', 'chargeRate', 'lowThreshold', 'safetyMargin', 'reportEvery', 'hotRadius']
80
+ lo, hi = [1, 1, 0, 0, 1, 0], [100000, 100000, 100000, 1000, 1000000, 400]
81
+ if p2:
82
+ names += ['agingEvery', 'carryCost']
83
+ lo += [1, 1]
84
+ hi += [1000000000, 100]
85
+ pv = ints(tok[1:], lo, hi, names)
86
+ bmax = pv[0]
87
+ if pv[1] > bmax or pv[2] > bmax:
88
+ raise Bad('第 %d 行:chargeRate 和 lowThreshold 不能超过 batteryMax' % pos[0])
89
+
90
+ tok = expect(nxt('ROBOTS'), 2, 'ROBOTS 行')
91
+ if tok[0] != 'ROBOTS':
92
+ raise Bad('第 %d 行:应为 ROBOTS R' % pos[0])
93
+ R, = ints(tok[1:], [1], [lim['R']], ['R'])
94
+ seen = set()
95
+ for i in range(R):
96
+ x, y = ints(expect(nxt('机器人位置'), 2, '机器人位置'), [0, 0], [W - 1, H - 1], ['x', 'y'])
97
+ if g[y][x] == '#':
98
+ raise Bad('第 %d 行:机器人初始位置是墙' % pos[0])
99
+ if (x, y) in seen:
100
+ raise Bad('第 %d 行:机器人初始位置重复' % pos[0])
101
+ seen.add((x, y))
102
+
103
+ tok = expect(nxt('EVENTS'), 2, 'EVENTS 行')
104
+ if tok[0] != 'EVENTS':
105
+ raise Bad('第 %d 行:应为 EVENTS E' % pos[0])
106
+ E, = ints(tok[1:], [0], [lim['E']], ['E'])
107
+ last = 0
108
+ ids = set()
109
+ C = 1000 # 事件里的坐标可以在地图外,但限制在 [-1000, 1000]
110
+ for i in range(E):
111
+ s = nxt('事件')
112
+ tok = s.split(' ')
113
+ if len(tok) < 2:
114
+ raise Bad('第 %d 行:事件格式错误' % pos[0])
115
+ t, = ints(tok[:1], [0], [T - 1], ['tick'])
116
+ if t < last:
117
+ raise Bad('第 %d 行:事件的 tick 必须非递减' % pos[0])
118
+ last = t
119
+ typ = tok[1]
120
+ if typ == 'ORDER':
121
+ expect(s, 8, 'ORDER 事件')
122
+ if not POS.match(tok[2]) or int(tok[2]) > 10 ** 9:
123
+ raise Bad('第 %d 行:订单编号必须是 1..10^9 的正整数,没有前导零' % pos[0])
124
+ if tok[2] in ids:
125
+ raise Bad('第 %d 行:订单编号重复' % pos[0])
126
+ ids.add(tok[2])
127
+ ints(tok[3:], [-C] * 4 + [0], [C] * 4 + [1000], ['px', 'py', 'dx', 'dy', 'priority'])
128
+ elif typ == 'CANCEL':
129
+ expect(s, 3, 'CANCEL 事件')
130
+ if not POS.match(tok[2]) or int(tok[2]) > 10 ** 9:
131
+ raise Bad('第 %d 行:CANCEL 的编号必须是 1..10^9 的正整数,没有前导零' % pos[0])
132
+ elif typ in ('BLOCK', 'UNBLOCK'):
133
+ expect(s, 4, typ + ' 事件')
134
+ ints(tok[2:], [-C, -C], [C, C], ['x', 'y'])
135
+ else:
136
+ raise Bad('第 %d 行:未知事件类型 %r' % (pos[0], typ))
137
+ if pos[0] != len(lines):
138
+ raise Bad('第 %d 行:EVENTS 之后还有多余内容' % (pos[0] + 1))
139
+
140
+
141
+ def main():
142
+ args = [a for a in sys.argv[1:] if not a.startswith('--')]
143
+ if len(args) != 1:
144
+ print(__doc__)
145
+ sys.exit(2)
146
+ with open(args[0], 'rb') as f:
147
+ data = f.read()
148
+ try:
149
+ text = data.decode('ascii')
150
+ check(text, full='--full' in sys.argv, p2='--p2' in sys.argv)
151
+ except UnicodeDecodeError:
152
+ print('不合法:文件里有非 ASCII 字符')
153
+ sys.exit(1)
154
+ except Bad as e:
155
+ print('不合法:%s' % e)
156
+ sys.exit(1)
157
+ print('OK')
158
+
159
+
160
+ if __name__ == '__main__':
161
+ main()
@@ -0,0 +1,132 @@
1
+ """仓储调度负载生成器(公开版,只包含公开负载的配置)。
2
+
3
+ 可以用不同的种子生成大量同类输入,用来和基线对拍;也可以自行修改参数。
4
+
5
+ python tools/gen.py 配置名 种子 > 输出.in
6
+ python tools/gen.py --list # 列出所有配置
7
+
8
+ 同一个 (配置名, 种子) 总是生成完全相同的文件。
9
+ """
10
+ import random
11
+ import sys
12
+
13
+ # 每个配置侧重不同的瓶颈
14
+ PROFILES = {
15
+ # ---- 公开负载(给选手,基线几秒到几十秒)
16
+ 'pub_small': dict(W=40, H=31, T=1500, R=24, chargers=4, rate=0.35, cancel=0.05, block=0.02, dur=(30, 150)),
17
+ 'pub_wide': dict(W=90, H=61, T=1500, R=40, chargers=6, rate=0.25, cancel=0.03, block=0.01, dur=(50, 200)),
18
+ 'pub_crowded': dict(W=50, H=40, T=1500, R=80, chargers=14, rate=0.8, cancel=0.03, block=0.02, dur=(30, 120)),
19
+ 'pub_dynamic': dict(W=60, H=46, T=1500, R=40, chargers=5, rate=0.4, cancel=0.05, block=0.25, dur=(10, 80)),
20
+ 'pub_flood': dict(W=50, H=37, T=1500, R=30, chargers=4, rate=1.6, cancel=0.10, block=0.02, dur=(30, 150)),
21
+ }
22
+
23
+ # 电量参数(满电、每 tick 充电量、低电量阈值、安全余量)默认按地图尺寸缩放,见 battery_for()
24
+
25
+
26
+ def battery_for(W, H):
27
+ full = 8 * (W + H)
28
+ return (full, max(10, full // 20), 2 * (W + H), 30)
29
+ REPORT_EVERY = 50
30
+ HOT_RADIUS = 3
31
+
32
+
33
+ def make_grid(W, H, n_chargers, rng):
34
+ g = [['.'] * W for _ in range(H)]
35
+ for x in range(W):
36
+ g[0][x] = g[H - 1][x] = '#'
37
+ for y in range(H):
38
+ g[y][0] = g[y][W - 1] = '#'
39
+ # 货架:每 3 行一排(1 行货架 + 2 行通道),左侧 x=1..4 是装卸区,
40
+ # 每隔 7~10 格留一个 2 格宽的横向通道
41
+ for y in range(4, H - 3, 3):
42
+ x = 6
43
+ while x < W - 4:
44
+ seg = rng.randint(6, 9)
45
+ for xx in range(x, min(x + seg, W - 4)):
46
+ g[y][xx] = '#'
47
+ x += seg + 2
48
+ # 充电桩:均匀分布在顶部通道(y=1),避开左侧和底部的装卸区
49
+ step = max(1, (W - 9) // max(1, n_chargers))
50
+ spots = [(min(W - 2, 6 + i * step), 1) for i in range(n_chargers)]
51
+ for x, y in spots:
52
+ g[y][x] = 'C'
53
+ return g
54
+
55
+
56
+ def floor_cells(g):
57
+ return [(x, y) for y in range(len(g)) for x in range(len(g[0])) if g[y][x] == '.']
58
+
59
+
60
+ def generate(name, seed):
61
+ return generate_from(name, PROFILES[name], seed)
62
+
63
+
64
+ def generate_from(name, p, seed):
65
+ """按配置字典 p 生成;p 里有 extra 时把它追加到 PARAMS 行末尾(第二阶段用)。"""
66
+ rng = random.Random('%s/%d' % (name, seed))
67
+ W, H, T, R = p['W'], p['H'], p['T'], p['R']
68
+ battery = p.get('battery') or battery_for(W, H)
69
+ g = make_grid(W, H, p['chargers'], rng)
70
+ floors = floor_cells(g)
71
+ pickups = [(x, y) for x, y in floors
72
+ if (y > 0 and g[y - 1][x] == '#' and y - 1 > 0) or (y < H - 1 and g[y + 1][x] == '#' and y + 1 < H - 1)]
73
+ docks = [(x, y) for x, y in floors if x <= 3 or y >= H - 3]
74
+ walls = [(x, y) for y in range(1, H - 1) for x in range(1, W - 1) if g[y][x] == '#']
75
+ aisles = [(x, y) for x, y in floors if 5 <= x <= W - 5]
76
+ starts = rng.sample(floors, R)
77
+
78
+ events = [] # (tick, 序号, 文本)
79
+ oid = 0
80
+ for t in range(T):
81
+ n = 0
82
+ r = p['rate']
83
+ while r > 0:
84
+ if rng.random() < min(r, 1.0):
85
+ n += 1
86
+ r -= 1.0
87
+ for _ in range(n):
88
+ oid += 1
89
+ if rng.random() < 0.01: # 少量无效订单(取货点是墙)
90
+ px, py = rng.choice(walls)
91
+ else:
92
+ px, py = rng.choice(pickups)
93
+ dx, dy = rng.choice(docks)
94
+ prio = rng.choices([0, 1, 2, 3], [60, 25, 10, 5])[0]
95
+ events.append((t, len(events), 'ORDER %d %d %d %d %d %d' % (oid, px, py, dx, dy, prio)))
96
+ if rng.random() < p['cancel']:
97
+ ct = t + rng.randint(1, 200)
98
+ if ct < T:
99
+ events.append((ct, len(events), 'CANCEL %d' % oid))
100
+ if rng.random() < 0.003: # 取消一个不存在的订单
101
+ events.append((t, len(events), 'CANCEL %d' % (oid + 100000)))
102
+ b = p['block']
103
+ while b > 0:
104
+ if rng.random() < min(b, 1.0):
105
+ x, y = rng.choice(aisles)
106
+ d = rng.randint(*p['dur'])
107
+ events.append((t, len(events), 'BLOCK %d %d' % (x, y)))
108
+ if t + d < T:
109
+ events.append((t + d, len(events), 'UNBLOCK %d %d' % (x, y)))
110
+ b -= 1.0
111
+ events.sort()
112
+
113
+ out = ['%d %d %d' % (W, H, T)]
114
+ out += [''.join(row) for row in g]
115
+ out.append('PARAMS %d %d %d %d %d %d' % (battery + (REPORT_EVERY, HOT_RADIUS)) +
116
+ ''.join(' %d' % v for v in p.get('extra', ())))
117
+ out.append('ROBOTS %d' % R)
118
+ out += ['%d %d' % s for s in starts]
119
+ out.append('EVENTS %d' % len(events))
120
+ out += ['%d %s' % (t, text) for t, _, text in events]
121
+ return '\n'.join(out) + '\n'
122
+
123
+
124
+ if __name__ == '__main__':
125
+ if len(sys.argv) == 2 and sys.argv[1] == '--list':
126
+ for k, v in PROFILES.items():
127
+ print(k, v)
128
+ sys.exit(0)
129
+ if len(sys.argv) != 3:
130
+ print(__doc__)
131
+ sys.exit(1)
132
+ sys.stdout.write(generate(sys.argv[1], int(sys.argv[2])))