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,402 @@
1
+ // bot.cpp —— 坦克大战 bot
2
+ //
3
+ // 设计:脚本化角色策略(决定"该干什么")+ 单步精确 maximin(决定"这一回合怎么安全地干")。
4
+ //
5
+ // 角色:
6
+ // 火力型 FIRE:主攻。沿"侧翼开炮走廊"(与敌方基地同行的一行)冲到敌基旁,清砖、点基地。
7
+ // 装甲型 ARMOR:防守反击。守在己方基地侧翼的开炮位:谁靠近我方基地就打谁、挡炮线;
8
+ // 我方基地安全时再沿第二条走廊前压。
9
+ // 快速型 FAST:抢星(场上星→最近补给点),顺手点杀/绕后;满级后转侧翼火力。
10
+ //
11
+ // 每辆车每回合的命令由"策略建议"(开炮优先级:敌车>敌基地>挡路砖;否则沿走廊移动)给出,
12
+ // 再用精确 step 做一层 maximin:在双方候选命令的笛卡尔积上,
13
+ // 只允许"无论对方怎么下、本回合都不会被白白打中"的我方联合命令,
14
+ // 在其中选角色目标推进最多 / 评估值最高的一条。搜索量很小,预算充裕。
15
+ #include "tank.hpp"
16
+
17
+ #include <algorithm>
18
+
19
+ using namespace tank;
20
+
21
+ namespace {
22
+
23
+ constexpr int INF = 1000000;
24
+ constexpr int MAXJ = 400;
25
+
26
+ int G_ME;
27
+
28
+ // 距离场
29
+ int g_atk[2][CELLS]; // 到某基地"开炮位"的代价(砖代价 2)
30
+ int g_walk[CELLS]; // 纯空地 BFS(不穿砖)
31
+ int g_star[MAX_SUPPLY][CELLS];
32
+ uint64_t g_wall_hash = ~0ULL;
33
+
34
+ // 每个基地的两条侧翼开炮走廊:lane_cell[base][side] = 与基地同行、x = base.x±k 的开炮格
35
+ int g_lane[2][2]; // [base][0/1 左右]
36
+ int g_basecol_dir[2][2]; // 朝基地的开炮方向(LEFT/RIGHT)
37
+
38
+ // ---------------- 距离场 ----------------
39
+ void dijkstra(const State& s, const int* seeds, const int* costs, int nseed, int* dist, int brickW) {
40
+ bool done[CELLS] = {};
41
+ for (int c = 0; c < CELLS; ++c) dist[c] = INF;
42
+ for (int k = 0; k < nseed; ++k) dist[seeds[k]] = std::min(dist[seeds[k]], costs[k]);
43
+ for (int it = 0; it < CELLS; ++it) {
44
+ int u = -1, best = INF;
45
+ for (int c = 0; c < CELLS; ++c)
46
+ if (!done[c] && dist[c] < best) { best = dist[c]; u = c; }
47
+ if (u < 0) break;
48
+ done[u] = true;
49
+ for (int d = 0; d < 4; ++d) {
50
+ const int v = u + DIR_DELTA[d];
51
+ const uint8_t g = s.grid[v];
52
+ int w = 0;
53
+ if (g == EMPTY) w = 1;
54
+ else if (g == BRICK && brickW > 0) w = brickW;
55
+ if (w && dist[u] + w < dist[v]) dist[v] = dist[u] + w;
56
+ }
57
+ }
58
+ }
59
+
60
+ void bfs_walk(const State& s, int src, int* dist) {
61
+ for (int c = 0; c < CELLS; ++c) dist[c] = INF;
62
+ int q[CELLS], head = 0, tail = 0;
63
+ dist[src] = 0; q[tail++] = src;
64
+ while (head < tail) {
65
+ const int u = q[head++];
66
+ for (int d = 0; d < 4; ++d) {
67
+ const int v = u + DIR_DELTA[d];
68
+ if (s.grid[v] == EMPTY && dist[v] == INF) { dist[v] = dist[u] + 1; q[tail++] = v; }
69
+ }
70
+ }
71
+ }
72
+
73
+ // 找到基地同行(侧翼)最近的可站立开炮格;返回格子,朝基地的方向写回 fd。
74
+ // 只用当前地形:格子本身空或砖(砖可以清),与基地之间无钢墙/水以外的阻挡(砖可清,水不挡弹)。
75
+ int find_lane_cell(const State& s, int bcell, int lr, int& fd) {
76
+ const int bx = cell_x(bcell), by = cell_y(bcell);
77
+ const int dx = lr == 0 ? -1 : 1;
78
+ for (int k = 2; k < 14; ++k) {
79
+ const int x = bx + dx * k;
80
+ if (x < 0 || x > 14) break;
81
+ const int c = cell_of(x, by);
82
+ const uint8_t g = s.grid[c];
83
+ if (g != EMPTY && g != BRICK) break; // 钢/水/界外,这条走廊断
84
+ // 与基地之间不能有钢墙
85
+ bool steel = false;
86
+ for (int xx = bx + dx; xx != x; xx += dx)
87
+ if (s.grid[cell_of(xx, by)] == STEEL) { steel = true; break; }
88
+ if (steel) continue;
89
+ fd = dx < 0 ? RIGHT : LEFT;
90
+ return c;
91
+ }
92
+ return NO_CELL;
93
+ }
94
+
95
+ // 进攻距离场:seed = 所有"炮线能到基地"的格子,代价 = 清掉射线上砖的开炮回合数
96
+ void build_attack(const State& s, int bcell, int* dist) {
97
+ int seeds[CELLS], costs[CELLS], n = 0;
98
+ const int sz = s.P.size;
99
+ for (int y = 0; y < sz; ++y)
100
+ for (int x = 0; x < sz; ++x) {
101
+ const int c = cell_of(x, y);
102
+ const uint8_t g = s.grid[c];
103
+ if (g != EMPTY && g != BRICK) continue;
104
+ int dir = -1;
105
+ if (x == cell_x(bcell) && y != cell_y(bcell)) dir = cell_y(bcell) < y ? UP : DOWN;
106
+ else if (y == cell_y(bcell) && x != cell_x(bcell)) dir = cell_x(bcell) < x ? LEFT : RIGHT;
107
+ else continue;
108
+ int bricks = 0; bool bad = false;
109
+ for (int p = c + DIR_DELTA[dir]; p != bcell; p += DIR_DELTA[dir]) {
110
+ const uint8_t gg = s.grid[p];
111
+ if (gg == STEEL || gg == OUTSIDE || gg == BASE0 || gg == BASE1) { bad = true; break; }
112
+ if (gg == BRICK) ++bricks;
113
+ }
114
+ if (bad) continue;
115
+ seeds[n] = c; costs[n] = bricks + (g == BRICK ? 1 : 0); ++n;
116
+ }
117
+ dijkstra(s, seeds, costs, n, dist, 2);
118
+ }
119
+
120
+ void ensure_dist(const State& s) {
121
+ if (s.wall_hash == g_wall_hash) return;
122
+ g_wall_hash = s.wall_hash;
123
+ build_attack(s, s.base_pos[0], g_atk[0]);
124
+ build_attack(s, s.base_pos[1], g_atk[1]);
125
+ bfs_walk(s, s.base_pos[0], g_walk); // 占位,后面每回合按我方基地重算
126
+ for (int k = 0; k < s.n_supply; ++k) bfs_walk(s, s.supply[k], g_star[k]);
127
+ for (int b = 0; b < 2; ++b)
128
+ for (int lr = 0; lr < 2; ++lr) {
129
+ int fd = -1;
130
+ g_lane[b][lr] = find_lane_cell(s, s.base_pos[b], lr, fd);
131
+ g_basecol_dir[b][lr] = fd;
132
+ }
133
+ }
134
+
135
+ inline int adist(int base, int c) { int d = g_atk[base][c]; return d >= INF ? 99 : d; }
136
+
137
+ // ---------------- 射线 ----------------
138
+ struct Ray { int kind, tank, seat, cell, dist; };
139
+ Ray raycast(const State& s, int pos, int d) {
140
+ int c = pos;
141
+ for (int k = 1; k <= 16; ++k) {
142
+ c += DIR_DELTA[d];
143
+ const int tv = tank_at(s, c);
144
+ if (tv >= 0) return {1, tv, -1, c, k};
145
+ const uint8_t g = s.grid[c];
146
+ if (g == BASE0 || g == BASE1) return {2, -1, g - BASE0, c, k};
147
+ if (g == BRICK) return {3, -1, -1, c, k};
148
+ if (g == STEEL) return {4, -1, -1, c, k};
149
+ if (g == OUTSIDE) return {6, -1, -1, c, k};
150
+ }
151
+ return {0, -1, -1, NO_CELL, 0};
152
+ }
153
+
154
+ // 一辆坦克在我方某方命令 o 下、对方全不动,结算后是否会被打中
155
+ bool tank_hit(const State& s, int i, const Orders& mine) {
156
+ State t = s;
157
+ const int side = seat_of(i);
158
+ Orders blank;
159
+ if (side == 0) step(t, mine, blank); else step(t, blank, mine);
160
+ return !t.tank[i].alive || t.tank[i].hp < s.tank[i].hp;
161
+ }
162
+
163
+ // ---------------- 角色:单辆车的建议命令 ----------------
164
+ // 距离场(空地 BFS)每回合在我家基地附近重算,用于防守走位
165
+ int g_home[CELLS];
166
+
167
+ // 朝距离场最小的可走邻格走;快速型给两步。返回 true 给了移动命令。
168
+ bool step_toward(const State& s, int i, const int* field, Cmd& out, int cap) {
169
+ const int pos = s.tank[i].pos;
170
+ int bestD = field[pos], bd = -1;
171
+ for (int d = 0; d < 4; ++d) {
172
+ const int c = pos + DIR_DELTA[d];
173
+ if (s.grid[c] != EMPTY) continue;
174
+ if (tank_at(s, c) >= 0) continue;
175
+ if (field[c] < bestD) { bestD = field[c]; bd = d; }
176
+ }
177
+ if (bd < 0) return false;
178
+ if (type_of(i) != FAST || cap < 2) { out = cmd_move(bd); return true; }
179
+ // 第二步继续沿场走
180
+ const int p1 = pos + DIR_DELTA[bd];
181
+ int d2 = -1, b2 = field[p1];
182
+ for (int d = 0; d < 4; ++d) {
183
+ int c = p1 + DIR_DELTA[d];
184
+ if (s.grid[c] != EMPTY) continue;
185
+ if (tank_at(s, c) >= 0) continue;
186
+ if (field[c] < b2) { b2 = field[c]; d2 = d; }
187
+ }
188
+ out = d2 >= 0 ? cmd_move2(bd, d2) : cmd_move(bd);
189
+ return true;
190
+ }
191
+
192
+ // 高价值开炮方向:敌车 / 敌基地 / 挡路砖。返回 -1 表示没有。
193
+ int aim_dir(const State& s, int i, bool allowBrick, const int* brickField) {
194
+ const int me = seat_of(i), foe = 1 - me;
195
+ int best = -1; double bestV = -1e18;
196
+ for (int d = 0; d < 4; ++d) {
197
+ if (!can_fire(s, i)) break;
198
+ const Ray r = raycast(s, s.tank[i].pos, d);
199
+ double v = -1e18;
200
+ if (r.kind == 1) {
201
+ const int tv = r.tank;
202
+ if (seat_of(tv) == foe) v = 1000 - r.dist * 10 + s.tank[tv].hp * 20;
203
+ else v = -1e9; // 队友
204
+ } else if (r.kind == 2) {
205
+ if (r.seat == foe) v = 900 - r.dist;
206
+ else v = -1e9; // 自己基地
207
+ } else if (r.kind == 3 && allowBrick && brickField) {
208
+ if (brickField[r.cell] < brickField[s.tank[i].pos]) v = 500 - r.dist;
209
+ }
210
+ if (v > bestV) { bestV = v; best = d; }
211
+ }
212
+ return bestV > 0 ? best : -1;
213
+ }
214
+
215
+ // 每辆车的候选命令:策略建议 + 合法移动 + STAY
216
+ int policy_candidates(const State& s, int i, Cmd* out, const int homeBfs[CELLS]) {
217
+ Cmd leg[N_CMDS];
218
+ const int L = legal_commands(s, i, leg);
219
+ Cmd set[40]; int n = 0;
220
+ auto add = [&](Cmd c) {
221
+ for (int k = 0; k < n; ++k) if (set[k] == c) return;
222
+ if (n < 40) set[n++] = c;
223
+ };
224
+ add(STAY);
225
+ const int me = seat_of(i), foe = 1 - me, ty = type_of(i);
226
+
227
+ // 选这一回合跟随的距离场
228
+ const int* field = nullptr;
229
+ bool brickOk = true;
230
+ if (ty == FAST) {
231
+ int sk = -1;
232
+ for (int k = 0; k < s.n_supply; ++k)
233
+ if (((s.star_mask >> k) & 1) && (sk < 0 || g_star[k][s.tank[i].pos] < g_star[sk][s.tank[i].pos])) sk = k;
234
+ if (sk < 0) {
235
+ if (s.tank[i].level < 2) {
236
+ for (int k = 0; k < s.n_supply; ++k) if (sk < 0 || g_star[k][s.tank[i].pos] < g_star[sk][s.tank[i].pos]) sk = k;
237
+ field = sk >= 0 ? g_star[sk] : g_atk[foe];
238
+ } else field = g_atk[foe];
239
+ } else field = g_star[sk];
240
+ brickOk = false;
241
+ } else if (ty == FIRE) {
242
+ field = g_atk[foe];
243
+ } else {
244
+ // ARMOR:敌方威胁近则守家(homeBfs 以我方基地侧翼开炮位为源),否则进攻
245
+ int threat = INF;
246
+ for (int j = 3 * foe; j < 3 * foe + 3; ++j)
247
+ if (s.tank[j].alive) threat = std::min(threat, adist(me, s.tank[j].pos));
248
+ field = (threat <= 22) ? homeBfs : g_atk[foe];
249
+ brickOk = true;
250
+ }
251
+
252
+ // 开炮建议
253
+ const int ad = aim_dir(s, i, brickOk && field == g_atk[foe], g_atk[foe]);
254
+ if (ad >= 0) add(cmd_fire(ad));
255
+ // 装甲守家时:朝靠近我方基地的敌车方向开砖/敌车已在 aim_dir 覆盖;再允许守家位补位移动
256
+
257
+ // 移动建议(沿场)
258
+ Cmd mv = STAY;
259
+ if (step_toward(s, i, field, mv, 2)) add(mv);
260
+
261
+ // 补充:所有直接朝敌车/敌基地的炮
262
+ for (int d = 0; d < 4; ++d) {
263
+ if (!can_fire(s, i)) break;
264
+ const Ray r = raycast(s, s.tank[i].pos, d);
265
+ if (r.kind == 1 && seat_of(r.tank) == foe) add(cmd_fire(d));
266
+ if (r.kind == 2 && r.seat == foe) add(cmd_fire(d));
267
+ }
268
+ // 补充:所有安全的合法移动(躲闪选项),最多再加几条
269
+ for (int k = 0; k < L && n < 12; ++k)
270
+ if (is_move(leg[k]) || is_move2(leg[k])) add(leg[k]);
271
+ // 所有能打的炮(含清砖)
272
+ for (int k = 0; k < L && n < 14; ++k)
273
+ if (is_fire(leg[k])) add(leg[k]);
274
+
275
+ for (int k = 0; k < n; ++k) out[k] = set[k];
276
+ return n;
277
+ }
278
+
279
+ // ---------------- 评估(用于在安全命令里挑) ----------------
280
+ double evaluate(const State& t, int me) {
281
+ if (t.done) return t.winner < 0 ? 0.0 : (t.winner == me ? 1e9 - t.turn : -1e9 + t.turn);
282
+ const int foe = 1 - me;
283
+ double v = 300.0 * (t.base_hp[me] - t.base_hp[foe]);
284
+ for (int i = 0; i < 6; ++i) {
285
+ const Tank& tk = t.tank[i];
286
+ const double sg = seat_of(i) == me ? 1 : -1;
287
+ if (tk.alive) v += sg * (60 + 40 * tk.hp + 50 * tk.level);
288
+ }
289
+ v += 10.0 * (t.kills[me] - t.kills[foe]);
290
+ v += 24.0 * (t.stars_eaten[me] - t.stars_eaten[foe]);
291
+ for (int i = 0; i < 6; ++i) {
292
+ const Tank& tk = t.tank[i];
293
+ if (!tk.alive) continue;
294
+ if (seat_of(i) == me) v -= 4.0 * adist(foe, tk.pos);
295
+ else v += 4.0 * adist(me, tk.pos);
296
+ }
297
+ // 在飞子弹
298
+ for (int b = 0; b < t.n_bullets; ++b) {
299
+ const Bullet& bu = t.bullet[b];
300
+ int c = bu.pos;
301
+ for (int k = 1; k <= 4; ++k) {
302
+ c += DIR_DELTA[bu.dir];
303
+ const int tv = tank_at(t, c);
304
+ if (tv >= 0) {
305
+ const double sg = seat_of(tv) == me ? -1 : 1;
306
+ v += sg * (120 - 12 * k) * (seat_of(bu.owner) == seat_of(tv) ? 1.3 : 1.0);
307
+ break;
308
+ }
309
+ const uint8_t g = t.grid[c];
310
+ if (g == BASE0 || g == BASE1) {
311
+ const int seat = g - BASE0;
312
+ const double sg = seat == me ? -1 : 1;
313
+ v += sg * (200 - 14 * k);
314
+ break;
315
+ }
316
+ if (g == BRICK || g == STEEL || g == OUTSIDE) break;
317
+ }
318
+ }
319
+ return v;
320
+ }
321
+
322
+ struct SC { Cmd c[3][20]; int n[3]; };
323
+
324
+ void side_candidates(const State& s, int side, SC& sc, const int homeBfs[CELLS]) {
325
+ for (int ty = 0; ty < 3; ++ty) {
326
+ const int i = tank_id(side, ty);
327
+ if (!s.tank[i].alive) { sc.c[ty][0] = STAY; sc.n[ty] = 1; }
328
+ else sc.n[ty] = policy_candidates(s, i, sc.c[ty], homeBfs);
329
+ }
330
+ }
331
+
332
+ } // namespace
333
+
334
+ void init(const State& s, int me) {
335
+ G_ME = me;
336
+ g_wall_hash = ~0ULL;
337
+ ensure_dist(s);
338
+ }
339
+
340
+ Orders decide(const tank::State& s, int me) {
341
+ G_ME = me;
342
+ ensure_dist(s);
343
+
344
+ // 防守距离场:以我方基地两条侧翼开炮行为源(走到侧翼行即可)
345
+ {
346
+ int seeds[6], costs[6], n = 0;
347
+ for (int lr = 0; lr < 2; ++lr) {
348
+ const int c = g_lane[me][lr];
349
+ if (c != NO_CELL) { seeds[n] = c; costs[n] = 0; ++n; }
350
+ }
351
+ // 加上基地正前一列的几个防守格
352
+ dijkstra(s, seeds, costs, n, g_home, 2);
353
+ }
354
+
355
+ SC mine, theirs;
356
+ side_candidates(s, me, mine, g_home);
357
+ side_candidates(s, 1 - me, theirs, g_home);
358
+
359
+ static Orders A[MAXJ], B[MAXJ];
360
+ int na = 0, nb = 0;
361
+ for (int a = 0; a < mine.n[0] && na < MAXJ; ++a)
362
+ for (int b = 0; b < mine.n[1] && na < MAXJ; ++b)
363
+ for (int c = 0; c < mine.n[2] && na < MAXJ; ++c) {
364
+ A[na].cmd[0] = mine.c[0][a]; A[na].cmd[1] = mine.c[1][b]; A[na].cmd[2] = mine.c[2][c]; ++na;
365
+ }
366
+ for (int a = 0; a < theirs.n[0] && nb < MAXJ; ++a)
367
+ for (int b = 0; b < theirs.n[1] && nb < MAXJ; ++b)
368
+ for (int c = 0; c < theirs.n[2] && nb < MAXJ; ++c) {
369
+ B[nb].cmd[0] = theirs.c[0][a]; B[nb].cmd[1] = theirs.c[1][b]; B[nb].cmd[2] = theirs.c[2][c]; ++nb;
370
+ }
371
+
372
+ // 一层 maximin:worst[a] = 对方最优应对下的评估值;同时记录我方坦克是否在某分支挨打
373
+ double worst[MAXJ];
374
+ int danger[MAXJ]; // 该命令下,对方是否能让我方某辆"本可以不死"的车挨打
375
+ for (int a = 0; a < na; ++a) { worst[a] = 1e18; danger[a] = 0; }
376
+
377
+ // 基线(双方不动)下我方每辆车的存活/血量,用于区分"这一回合白挨打"
378
+ State base = s; Orders blank; step(base, blank, blank);
379
+
380
+ for (int b = 0; b < nb; ++b)
381
+ for (int a = 0; a < na; ++a) {
382
+ State t = s;
383
+ if (me == 0) step(t, A[a], B[b]); else step(t, B[b], A[a]);
384
+ const double v = evaluate(t, me);
385
+ if (v < worst[a]) worst[a] = v;
386
+ for (int ty = 0; ty < 3; ++ty) {
387
+ const int i = tank_id(me, ty);
388
+ if (s.tank[i].alive && (!t.tank[i].alive || t.tank[i].hp < base.tank[i].hp)) {
389
+ // 我方比"不动"时状态更差 → 危险
390
+ if (!t.tank[i].alive || t.tank[i].hp < base.tank[i].hp) danger[a] |= 1 << ty;
391
+ }
392
+ }
393
+ }
394
+
395
+ // 选 worst 最大、且不白挨打的命令;危险命令加巨大罚分
396
+ int best = 0; double bv = -1e18;
397
+ for (int a = 0; a < na; ++a) {
398
+ double v = worst[a] - (danger[a] ? 100000.0 * __builtin_popcount((unsigned)danger[a]) : 0);
399
+ if (v > bv) { bv = v; best = a; }
400
+ }
401
+ return A[best];
402
+ }