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,392 @@
1
+ // bot.cpp —— 坦克大战(策略型,不做搜索)
2
+ //
3
+ // 上半场的教训:带 max-min 搜索的版本每回合烧 26 CPU 毫秒却排在基准之下,而成绩表里
4
+ // 第 5 名每回合只用 0.5 毫秒。这个游戏的胜负手是"攻城竞速 + 星星等级差",是长期规划
5
+ // 问题,2~3 层的战术搜索看不见它,反而让车变得畏缩(任何动作都被假想的全知对手惩罚)。
6
+ // 所以这一版完全不做搜索:分工 + 明确的优先级规则,每回合只用几十微秒。
7
+ //
8
+ // 三条主线:
9
+ // 攻城 —— 一辆车去对方基地的炮位(<=2 格且炮线通 = 每回合稳定 1 点伤害),路上先用炮开砖。
10
+ // 防守 —— 对方有车瞄着我方基地时,派一辆车去挡炮线 / 打那辆车。
11
+ // 争星 —— 场上有星就派一辆确实更近的车去吃;2 级快速型(5 血、不装填、两步)能碾压 1 血车。
12
+ #include "tank.hpp"
13
+
14
+ #include <algorithm>
15
+ #include <cstring>
16
+
17
+ using namespace tank;
18
+
19
+ static const int INF = 9999;
20
+
21
+ // ---------------------------------------------------------------- 场
22
+ static int g_datt[2][CELLS]; // [seat] 到"能打到对方基地的炮位"的代价(砖算 3 步)
23
+ static int g_dsup[CELLS]; // 到最近补给点的步数
24
+ static int g_dsp[MAX_SUPPLY][CELLS]; // 到第 k 个补给点的步数(分工时按点分配,避免都挤一个点)
25
+ static int g_tgt[6]; // R_STAR 的车去哪个补给点
26
+ static int g_dhunt[2][CELLS]; // [seat] 到最近敌方坦克的步数
27
+ static int g_ddef[2][CELLS]; // [seat] 到拦截点(挡炮线 / 打攻城车)的步数
28
+ static bool g_need_def[2];
29
+ static int g_attacker_dist[2]; // [seat] 对方攻城车到我方基地的距离(没有则 INF)
30
+ static unsigned char g_threat[CELLS]; // 本回合会有子弹飞到这格
31
+ static unsigned char g_exp[2][CELLS]; // [seat] 这格在对方能开炮的车的炮线上
32
+ static unsigned char g_mine[CELLS]; // 这格有我方车
33
+ static bool g_has_star;
34
+
35
+ enum Role { R_SIEGE = 0, R_DEF, R_STAR, R_HUNT };
36
+ static int g_role[6];
37
+
38
+ // ---------------------------------------------------------------- 基础几何
39
+ // 连坦克一起看:从 pos 到 base 的炮线是否通,返回距离。
40
+ static int shot_now(const State& s, int pos, int base) {
41
+ int best = INF;
42
+ for (int d = 0; d < 4; ++d) {
43
+ int c = pos, n = 0;
44
+ for (;;) {
45
+ c += DIR_DELTA[d];
46
+ if (++n > 8) break;
47
+ if (c == base) { if (n < best) best = n; break; }
48
+ const uint8_t g = s.grid[c];
49
+ if (g != EMPTY && g != WATER) break;
50
+ if (tank_at(s, c) >= 0) break;
51
+ }
52
+ }
53
+ return best;
54
+ }
55
+
56
+ // 只看地形:从 pos 打到 base 的攻城代价(最多允许一堵砖),打不到返回 INF。
57
+ static int ray_cost(const State& s, int pos, int base) {
58
+ int best = INF;
59
+ for (int d = 0; d < 4; ++d) {
60
+ int c = pos, n = 0, nb = 0;
61
+ for (;;) {
62
+ c += DIR_DELTA[d];
63
+ if (++n > 5) break;
64
+ if (c == base) {
65
+ const int cost = 3 * nb + 2 * (n > 2 ? n - 2 : 0);
66
+ if (cost < best) best = cost;
67
+ break;
68
+ }
69
+ const uint8_t g = s.grid[c];
70
+ if (g == EMPTY || g == WATER) continue;
71
+ if (g == BRICK) { if (++nb > 1) break; continue; }
72
+ break;
73
+ }
74
+ }
75
+ return best;
76
+ }
77
+
78
+ // ---------------------------------------------------------------- 场计算
79
+ static void bfs_cost(const State& s, const unsigned char* src, int* dist) {
80
+ static int q[CELLS];
81
+ for (int c = 0; c < CELLS; ++c) dist[c] = INF;
82
+ int head = 0, tail = 0;
83
+ for (int c = 0; c < CELLS; ++c)
84
+ if (src[c]) { dist[c] = 0; q[tail++] = c; }
85
+ while (head < tail) {
86
+ const int c = q[head++];
87
+ for (int k = 0; k < 4; ++k) {
88
+ const int nb = c + DIR_DELTA[k];
89
+ const uint8_t g = s.grid[nb];
90
+ if (g != EMPTY && g != BRICK) continue;
91
+ if (dist[nb] > dist[c] + 1) { dist[nb] = dist[c] + 1; q[tail++] = nb; }
92
+ }
93
+ }
94
+ }
95
+
96
+ static void compute_fields(const State& s, int me) {
97
+ static unsigned char src[CELLS];
98
+ for (int c = 0; c < CELLS; ++c) {
99
+ g_threat[c] = 0; g_exp[0][c] = 0; g_exp[1][c] = 0; g_mine[c] = 0; src[c] = 0;
100
+ }
101
+ for (int i = 0; i < 6; ++i)
102
+ if (s.tank[i].alive && seat_of(i) == me) g_mine[s.tank[i].pos] = 1;
103
+
104
+ // 已在飞的子弹:本回合两个小步会飞到哪
105
+ for (int k = 0; k < s.n_bullets; ++k) {
106
+ const Bullet& b = s.bullet[k];
107
+ int c = b.pos;
108
+ for (int st = 0; st < 2; ++st) {
109
+ c += DIR_DELTA[b.dir];
110
+ if (c < 0 || c >= CELLS) break;
111
+ g_threat[c] = 1;
112
+ const uint8_t g = s.grid[c];
113
+ if (g != EMPTY && g != WATER) break;
114
+ if (tank_at(s, c) >= 0) break;
115
+ }
116
+ }
117
+ // 对方能开炮的车,炮线前 5 格是危险格(站上去就是面对面互射,子弹互相抵消、白送节奏)
118
+ for (int i = 0; i < 6; ++i) {
119
+ if (!s.tank[i].alive || !can_fire(s, i)) continue;
120
+ const int victim = 1 - seat_of(i);
121
+ for (int d = 0; d < 4; ++d) {
122
+ int c = s.tank[i].pos;
123
+ for (int n = 0; n < 5; ++n) {
124
+ c += DIR_DELTA[d];
125
+ if (c < 0 || c >= CELLS) break;
126
+ const uint8_t g = s.grid[c];
127
+ if (g == BRICK || g == STEEL || g == OUTSIDE || g == BASE0 || g == BASE1) break;
128
+ g_exp[victim][c] = 1;
129
+ if (tank_at(s, c) >= 0) break;
130
+ }
131
+ }
132
+ }
133
+
134
+ // 攻城场:炮位 = 从这格沿某方向最多 5 格、途中最多一堵砖就能打到对方基地
135
+ for (int seat = 0; seat < 2; ++seat) {
136
+ const int base = s.base_pos[1 - seat];
137
+ for (int c = 0; c < CELLS; ++c) src[c] = 0;
138
+ const int n = s.P.size;
139
+ for (int y = 0; y < n; ++y)
140
+ for (int x = 0; x < n; ++x) {
141
+ const int c = cell_of(x, y);
142
+ if (s.grid[c] != EMPTY) continue;
143
+ if (ray_cost(s, c, base) < INF) src[c] = 1;
144
+ }
145
+ bfs_cost(s, src, g_datt[seat]);
146
+ }
147
+
148
+ // 补给点场:既算"到最近补给点",也算"到第 k 个补给点"
149
+ for (int c = 0; c < CELLS; ++c) src[c] = 0;
150
+ for (int k = 0; k < s.n_supply; ++k) src[s.supply[k]] = 1;
151
+ bfs_cost(s, src, g_dsup);
152
+ for (int k = 0; k < s.n_supply; ++k) {
153
+ for (int c = 0; c < CELLS; ++c) src[c] = 0;
154
+ src[s.supply[k]] = 1;
155
+ bfs_cost(s, src, g_dsp[k]);
156
+ }
157
+ g_has_star = s.star_mask != 0;
158
+
159
+ // 猎杀场:到最近的敌方坦克
160
+ for (int seat = 0; seat < 2; ++seat) {
161
+ for (int c = 0; c < CELLS; ++c) src[c] = 0;
162
+ for (int i = 0; i < 6; ++i)
163
+ if (s.tank[i].alive && seat_of(i) != seat) src[s.tank[i].pos] = 1;
164
+ bfs_cost(s, src, g_dhunt[seat]);
165
+ }
166
+
167
+ // 拦截场:对方有车瞄着 seat 的基地时,把那些车和它们炮线上的格子当目标
168
+ for (int seat = 0; seat < 2; ++seat) {
169
+ for (int c = 0; c < CELLS; ++c) src[c] = 0;
170
+ const int mybase = s.base_pos[seat];
171
+ int nd = 0, bestd = INF;
172
+ for (int i = 0; i < 6; ++i) {
173
+ if (!s.tank[i].alive || seat_of(i) == seat) continue;
174
+ const int d = shot_now(s, s.tank[i].pos, mybase);
175
+ if (d > 5) continue;
176
+ ++nd;
177
+ if (d < bestd) bestd = d;
178
+ src[s.tank[i].pos] = 1;
179
+ for (int k = 0; k < 4; ++k) { // 炮线上、我和它之间的空格:站上去就挡住这一炮
180
+ int c = s.tank[i].pos, n = 0;
181
+ for (;;) {
182
+ c += DIR_DELTA[k];
183
+ if (++n >= d) break;
184
+ if (s.grid[c] != EMPTY) break;
185
+ src[c] = 1;
186
+ }
187
+ }
188
+ }
189
+ g_need_def[seat] = nd > 0;
190
+ g_attacker_dist[seat] = bestd;
191
+ if (nd) bfs_cost(s, src, g_ddef[seat]);
192
+ else for (int c = 0; c < CELLS; ++c) g_ddef[seat][c] = INF;
193
+ }
194
+ }
195
+
196
+ // ---------------------------------------------------------------- 打分
197
+ // 这一炮打出去会打到什么。
198
+ static int fire_score(const State& s, int i, int d) {
199
+ const int me = seat_of(i), foe = 1 - me;
200
+ int c = s.tank[i].pos;
201
+ int n = 0, bricks = 0;
202
+ for (;;) {
203
+ c += DIR_DELTA[d];
204
+ if (++n > 16) break;
205
+ const int v = tank_at(s, c);
206
+ if (v >= 0) {
207
+ if (seat_of(v) != me) {
208
+ int sc = 900 - 40 * n;
209
+ if (s.tank[v].hp <= 1) sc += 500; // 一发带走
210
+ if (s.tank[v].level >= 1) sc += 200; // 打掉对面的等级更值
211
+ return sc;
212
+ }
213
+ return -4000; // 打队友
214
+ }
215
+ if (c == s.base_pos[foe]) return bricks == 0 ? (20000 - 300 * n) : 1200;
216
+ if (c == s.base_pos[me]) return -20000; // 打自己基地
217
+ const uint8_t g = s.grid[c];
218
+ if (g == BRICK) {
219
+ if (++bricks > 1) return 40;
220
+ // 打掉这堵砖之后炮线就通了?继续往前看能不能到对方基地
221
+ int cc = c, nn = n;
222
+ for (;;) {
223
+ cc += DIR_DELTA[d];
224
+ if (++nn > 6) break;
225
+ if (cc == s.base_pos[foe]) return 1700 - 120 * nn;
226
+ if (cc == s.base_pos[me]) return -3000;
227
+ const uint8_t g2 = s.grid[cc];
228
+ if (g2 == EMPTY || g2 == WATER) continue;
229
+ if (tank_at(s, cc) >= 0) break;
230
+ break;
231
+ }
232
+ return 90;
233
+ }
234
+ if (g == STEEL) return (s.tank[i].level >= 2) ? 60 : -60;
235
+ if (g == OUTSIDE) return -30;
236
+ // 空地 / 水:继续飞
237
+ }
238
+ return -20;
239
+ }
240
+
241
+ static inline int goal_dist(const State& s, int i, int c) {
242
+ int g;
243
+ switch (g_role[i]) {
244
+ case R_DEF: g = g_ddef[seat_of(i)][c]; break;
245
+ case R_STAR: g = (g_tgt[i] < s.n_supply) ? g_dsp[g_tgt[i]][c] : g_dsup[c]; break;
246
+ case R_HUNT: g = g_dhunt[seat_of(i)][c]; break;
247
+ default: g = g_datt[seat_of(i)][c]; break;
248
+ }
249
+ if (g >= INF) g = 120;
250
+ if (g > 120) g = 120;
251
+ return g;
252
+ }
253
+
254
+ static int score_cmd(const State& s, int i, Cmd cmd) {
255
+ const Tank& t = s.tank[i];
256
+ const int me = seat_of(i);
257
+
258
+ if (is_fire(cmd)) return fire_score(s, i, cmd - 5);
259
+
260
+ if (cmd == STAY) {
261
+ int sc = 0;
262
+ if (g_threat[t.pos]) sc -= 700;
263
+ if (g_exp[me][t.pos]) sc -= 90;
264
+ if (g_has_star && star_at(s, t.pos) && t.level < s.P.max_level) sc += 3000;
265
+ // 已经占住补给点、就等这批星出在脚下:别动
266
+ if (g_role[i] == R_STAR && g_tgt[i] < s.n_supply && t.pos == s.supply[g_tgt[i]]) sc += 1500;
267
+ return sc;
268
+ }
269
+
270
+ const int d1 = is_move(cmd) ? cmd - 1 : (cmd - 9) >> 2;
271
+ const int p = t.pos;
272
+ const int p1 = p + DIR_DELTA[d1];
273
+ int mid = p1, fin = p1;
274
+ if (s.grid[p1] != EMPTY) { mid = p; fin = p; } // 第一步走不成
275
+ if (is_move2(cmd)) {
276
+ const int d2 = (cmd - 9) & 3;
277
+ const int p2 = mid + DIR_DELTA[d2];
278
+ if (s.grid[p2] == EMPTY) fin = p2;
279
+ }
280
+
281
+ int sc = 130 * (goal_dist(s, i, p) - goal_dist(s, i, fin));
282
+
283
+ if (g_threat[fin]) sc -= 900; // 走进子弹的落点(必中,一定要躲)
284
+ if (g_threat[mid]) sc -= 500;
285
+ if (g_threat[p]) sc += 400; // 离开子弹的落点
286
+ // 面对面互射 = 子弹互相抵消、谁也不赚。但这只是"不划算",不能压过推进:
287
+ // 上半场就是这一项给得太大,结果三辆车在原地横着挪、永远到不了对方基地。
288
+ if (g_exp[me][fin]) sc -= 0;
289
+ if (g_exp[me][p]) sc += 0;
290
+
291
+ if (g_mine[fin]) sc -= 200; // 和队友抢格(会双双走不成)
292
+ if (g_has_star && star_at(s, fin) && t.level < s.P.max_level) sc += 2500;
293
+ if (is_move2(cmd) && fin != mid) sc += 25; // 快速型一回合两步
294
+ return sc;
295
+ }
296
+
297
+ // ---------------------------------------------------------------- 分工
298
+ // 下一批星星还要几个回合末才出(已经出在场上则返回 0)。
299
+ static int star_wait(const State& s) {
300
+ if (s.star_mask) return 0;
301
+ const int nt = s.turn + 1;
302
+ if (nt < s.P.supply_first) return s.P.supply_first - nt;
303
+ const int k = (nt - s.P.supply_first) % s.P.supply_period;
304
+ return k == 0 ? 0 : s.P.supply_period - k;
305
+ }
306
+
307
+ static void assign_roles(const State& s, int me) {
308
+ for (int k = 0; k < 6; ++k) { g_role[k] = R_HUNT; g_tgt[k] = 0; }
309
+ int alive[3], na = 0;
310
+ for (int t = 0; t < 3; ++t) {
311
+ const int i = tank_id(me, t);
312
+ if (s.tank[i].alive) alive[na++] = i;
313
+ }
314
+ if (na == 0) return;
315
+
316
+ auto tt = [&](int i, const int* field) {
317
+ int d = field[s.tank[i].pos];
318
+ if (d >= INF) d = 200;
319
+ return double(d) / s.P.types[type_of(i)].speed;
320
+ };
321
+ bool used[6] = {};
322
+
323
+ // ---- 1) 攻城永远先留一辆 ----
324
+ // 基地每回合最多掉 1 血,两边都在攻城就是平局节奏;只有我攻城、对方不攻才是赢。
325
+ // 所以哪怕基地正挨打,也必须留一辆去啃对方基地,否则会输在基地血量这个第一顺位比较项上。
326
+ {
327
+ int best = -1; double bv = 1e18;
328
+ for (int k = 0; k < na; ++k) { const int i = alive[k]; const double v = tt(i, g_datt[me]); if (v < bv) { bv = v; best = i; } }
329
+ if (best >= 0) { g_role[best] = R_SIEGE; used[best] = true; }
330
+ }
331
+
332
+ // ---- 2) 防守:对方有车瞄着我方基地 ----
333
+ if (g_need_def[me]) {
334
+ int best = -1; double bv = 1e18;
335
+ for (int k = 0; k < na; ++k) { const int i = alive[k]; if (used[i]) continue; const double v = tt(i, g_ddef[me]); if (v < bv) { bv = v; best = i; } }
336
+ if (best >= 0) { g_role[best] = R_DEF; used[best] = true; }
337
+ }
338
+
339
+ // ---- 3) 争星:只用剩下的一辆 ----
340
+ // 这是本局的胜负手(2 级车能碾压 1 血车),而且必须按"点"分配,否则几辆车挤同一个点。
341
+ // 没出星但一批快到了也要提前占位:出星那回合站在点上,下一回合末就吃到。
342
+ const int wait = star_wait(s);
343
+ if (wait <= 12) {
344
+ int nk = 0, ks[MAX_SUPPLY];
345
+ for (int k = 0; k < s.n_supply; ++k) {
346
+ const bool has = (s.star_mask >> k) & 1;
347
+ if (has || wait <= 12) ks[nk++] = k;
348
+ }
349
+ bool taken[MAX_SUPPLY] = {}; int sent = 0;
350
+ for (int a = 0; a < na; ++a) {
351
+ const int i = alive[a];
352
+ if (used[i] || s.tank[i].level >= s.P.max_level) continue;
353
+ int bk = -1; double bv = 1e18;
354
+ for (int q = 0; q < nk; ++q) {
355
+ const int k = ks[q];
356
+ if (taken[k]) continue;
357
+ const double v = tt(i, g_dsp[k]);
358
+ const double adj = ((s.star_mask >> k) & 1) ? v : v + 1.0; // 已出星的点更急
359
+ if (adj < bv) { bv = adj; bk = k; }
360
+ }
361
+ if (bk < 0) continue;
362
+ taken[bk] = true;
363
+ g_role[i] = R_STAR; g_tgt[i] = bk; used[i] = true;
364
+ if (++sent >= 2) break;
365
+ }
366
+ }
367
+ // 剩下的:也去攻城(打基地才是胜负),别在中路和对面换命
368
+ for (int k = 0; k < na; ++k) if (!used[alive[k]]) g_role[alive[k]] = R_SIEGE;
369
+ }
370
+
371
+ // ---------------------------------------------------------------- 对外接口
372
+ void init(const State& s, int me) { (void)s; (void)me; }
373
+
374
+ Orders decide(const State& s, int me) {
375
+ Orders orders;
376
+ compute_fields(s, me);
377
+ assign_roles(s, me);
378
+
379
+ for (int t = 0; t < 3; ++t) {
380
+ const int i = tank_id(me, t);
381
+ if (!s.tank[i].alive) { orders.cmd[t] = STAY; continue; }
382
+ Cmd list[N_CMDS];
383
+ const int n = legal_commands(s, i, list);
384
+ int bestk = 0, bestsc = score_cmd(s, i, list[0]);
385
+ for (int k = 1; k < n; ++k) {
386
+ const int sc = score_cmd(s, i, list[k]);
387
+ if (sc > bestsc) { bestsc = sc; bestk = k; }
388
+ }
389
+ orders.cmd[t] = list[bestk];
390
+ }
391
+ return orders;
392
+ }