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,537 @@
1
+ // bot.cpp —— 坦克大战
2
+ //
3
+ // 胜负只看基地血量(每回合最多扣 1 血),本质是"攻城竞速":
4
+ // 谁先把一辆车送到对方基地的炮位上、并且每回合打一发,谁就赢。
5
+ // 评估里有三股力:攻城(approach / siege)、拦截(intercept:打掉或挡住正在攻我方
6
+ // 基地的车)、争星。用 tank::step 精确前推,在裁剪过的联合命令表上做 max-min 搜索
7
+ // (同时行动博弈,假设对手最优应答)。
8
+ #include "tank.hpp"
9
+
10
+ #include <algorithm>
11
+ #include <cstdio>
12
+ #include <cstring>
13
+ #include <queue>
14
+ #include <utility>
15
+
16
+ using namespace tank;
17
+
18
+ // 对比实验时改成正数(固定节点数,结果可复现);提交保持 0(按预算停)。
19
+ #define FIXED_NODES 0
20
+
21
+ // ---------------------------------------------------------------- 权重
22
+ static const double W_BASE = 300.0; // 基地每点血(胜负与第一顺位比较项)
23
+ static const double W_KILL = 34.0; // 击毁差(第二顺位)
24
+ static const double W_STAR = 38.0; // 吃星差(第三顺位)
25
+ static const double W_ARMY = 24.0; // 每点坦克血
26
+ static const double W_LEVEL = 30.0; // 每级
27
+ static const double W_SIEGE = 340.0; // 下回合就能打到对方基地(距离<=2 且炮线通)
28
+ static const double W_SIEGE2 = 100.0; // 距离 3..4 炮线通
29
+ static const double W_APPROACH = 11.0; // 每"回合"到攻城位的距离(负)
30
+ static const double W_THREAT = 22.0; // 每发即将命中的子弹
31
+ static const double W_EXPOSED = 0.0; // 站在对方能开炮的车的炮线上(实测:会让车变得畏缩,先关掉)
32
+ static const double W_STARPOS = 8.0; // 每回合到补给点的距离(负)
33
+ static const double W_DANGER0 = 70.0; // 对方有一辆车正指着我方基地
34
+ static const double W_DANGER1 = 15.0; // 每回合的拦截距离(负)
35
+ static const double W_JAM = 18.0; // 两辆己方车贴在一起(互相挡路)
36
+
37
+ static const int INF = 9999;
38
+
39
+ // ---------------------------------------------------------------- 场
40
+ static int g_dshot[2][CELLS]; // [seat] 从这格到"能打到对方基地的位置"的步数代价
41
+ static int g_dint[2][CELLS]; // [seat] 从这格到"需要拦截的点"的步数
42
+ static int g_dsup[CELLS]; // 到最近补给点的步数
43
+ static unsigned char g_threat[CELLS]; // bit0: 小步1后子弹到这格 bit1: 小步2后
44
+ static unsigned char g_occ[CELLS]; // 0 空 1 = 0 号方的车 2 = 1 号方的车
45
+ static unsigned char g_exp[2][CELLS]; // [seat] 这格正被对方能开炮的车瞄着(炮线通、距离<=5)
46
+ static bool g_has_dint[2];
47
+
48
+ static long g_nodes = 0;
49
+ static bool g_abort = false;
50
+
51
+ static inline void tick() {
52
+ if ((++g_nodes & 15) == 0) {
53
+ #if FIXED_NODES
54
+ if (g_nodes > FIXED_NODES) g_abort = true;
55
+ #else
56
+ if (tank::budget_left() < 3.0) g_abort = true;
57
+ #endif
58
+ }
59
+ }
60
+
61
+ static inline int spd(const State& s, int i) { return s.P.types[type_of(i)].speed; }
62
+ // 步数换成"要几个回合走到"(快速型一回合两步)。
63
+ static inline double turns(const State& s, int i, int steps) {
64
+ if (steps >= INF) steps = 60;
65
+ if (steps > 60) steps = 60;
66
+ return double(steps) / spd(s, i);
67
+ }
68
+
69
+ // ---------------------------------------------------------------- 炮线
70
+ // 连坦克一起看:路上有坦克就挡住。返回到 base 的最短距离。
71
+ static int shot_now(const State& s, int pos, int base) {
72
+ int best = INF;
73
+ for (int d = 0; d < 4; ++d) {
74
+ int c = pos, n = 0;
75
+ for (;;) {
76
+ c += DIR_DELTA[d];
77
+ if (++n > 6) break;
78
+ if (c == base) { if (n < best) best = n; break; }
79
+ const uint8_t g = s.grid[c];
80
+ if (g != EMPTY && g != WATER) break;
81
+ if (tank_at(s, c) >= 0) break;
82
+ }
83
+ }
84
+ return best;
85
+ }
86
+
87
+ // 从 pos 打到 base 的"攻城代价":最多允许一堵砖,越近越便宜;打不到返回 INF。
88
+ static int ray_cost(const State& s, int pos, int base) {
89
+ int best = INF;
90
+ for (int d = 0; d < 4; ++d) {
91
+ int c = pos, n = 0, nb = 0;
92
+ for (;;) {
93
+ c += DIR_DELTA[d];
94
+ if (++n > 5) break;
95
+ if (c == base) {
96
+ const int cost = 3 * nb + 2 * (n > 2 ? n - 2 : 0);
97
+ if (cost < best) best = cost;
98
+ break;
99
+ }
100
+ const uint8_t g = s.grid[c];
101
+ if (g == EMPTY || g == WATER) continue;
102
+ if (g == BRICK) { if (++nb > 1) break; continue; }
103
+ break;
104
+ }
105
+ }
106
+ return best;
107
+ }
108
+
109
+ // ---------------------------------------------------------------- 场计算
110
+ static void dijkstra_attack(const State& s, int seat) {
111
+ int* dist = g_dshot[seat];
112
+ for (int c = 0; c < CELLS; ++c) dist[c] = INF;
113
+ const int base = s.base_pos[1 - seat];
114
+ const unsigned char ofoe = (unsigned char)(1 - seat + 1), ome = (unsigned char)(seat + 1);
115
+ const int n = s.P.size;
116
+ std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int>>, std::greater<>> pq;
117
+ for (int y = 0; y < n; ++y)
118
+ for (int x = 0; x < n; ++x) {
119
+ const int c = cell_of(x, y);
120
+ if (s.grid[c] != EMPTY) continue;
121
+ int cost = ray_cost(s, c, base);
122
+ if (cost >= INF) continue;
123
+ if (g_occ[c] == ofoe) cost += 10; // 敌人占着炮位
124
+ else if (g_occ[c] == ome) cost += 4;
125
+ if (cost < dist[c]) { dist[c] = cost; pq.push({cost, c}); }
126
+ }
127
+ while (!pq.empty()) {
128
+ const std::pair<int, int> top = pq.top(); pq.pop();
129
+ const int d = top.first, c = top.second;
130
+ if (d != dist[c]) continue;
131
+ for (int k = 0; k < 4; ++k) {
132
+ const int nb = c + DIR_DELTA[k];
133
+ const uint8_t g = s.grid[nb];
134
+ int w;
135
+ if (g == EMPTY) w = 1;
136
+ else if (g == BRICK) w = 3;
137
+ else continue;
138
+ if (g_occ[nb] == ofoe) w += 7; // 绕开挡路的敌车
139
+ else if (g_occ[nb] == ome) w += 3; // 别挤在一起
140
+ if (dist[nb] > d + w) { dist[nb] = d + w; pq.push(std::make_pair(dist[nb], nb)); }
141
+ }
142
+ }
143
+ }
144
+
145
+ // seat 需要拦截的点:正在指 seat 基地的敌车,以及它炮线上可以挡的格子。
146
+ static void bfs_intercept(const State& s, int seat) {
147
+ static int q[CELLS];
148
+ static unsigned char src[CELLS];
149
+ for (int c = 0; c < CELLS; ++c) { g_dint[seat][c] = INF; src[c] = 0; }
150
+ const int mybase = s.base_pos[seat];
151
+ const int foe = 1 - seat;
152
+ int head = 0, tail = 0;
153
+ int ndanger = 0;
154
+ for (int i = 0; i < 6; ++i) {
155
+ if (seat_of(i) != foe || !s.tank[i].alive) continue;
156
+ const int d = shot_now(s, s.tank[i].pos, mybase);
157
+ if (d > 5) continue;
158
+ ++ndanger;
159
+ const int pc = s.tank[i].pos;
160
+ if (!src[pc]) { src[pc] = 1; g_dint[seat][pc] = 0; q[tail++] = pc; }
161
+ for (int k = 0; k < 4; ++k) { // 炮线上、我和它之间的格子:站上去就能挡
162
+ int c = pc, n = 0;
163
+ for (;;) {
164
+ c += DIR_DELTA[k];
165
+ if (++n >= d) break;
166
+ if (s.grid[c] != EMPTY) break;
167
+ if (!src[c]) { src[c] = 1; g_dint[seat][c] = 0; q[tail++] = c; }
168
+ }
169
+ }
170
+ }
171
+ g_has_dint[seat] = ndanger > 0;
172
+ if (!ndanger) return;
173
+ while (head < tail) {
174
+ const int c = q[head++];
175
+ for (int k = 0; k < 4; ++k) {
176
+ const int nb = c + DIR_DELTA[k];
177
+ const uint8_t g = s.grid[nb];
178
+ if (g != EMPTY && g != BRICK) continue;
179
+ if (g_dint[seat][nb] > g_dint[seat][c] + 1) { g_dint[seat][nb] = g_dint[seat][c] + 1; q[tail++] = nb; }
180
+ }
181
+ }
182
+ }
183
+
184
+ static void bfs_supply(const State& s) {
185
+ static int q[CELLS];
186
+ for (int c = 0; c < CELLS; ++c) g_dsup[c] = INF;
187
+ int head = 0, tail = 0;
188
+ for (int k = 0; k < s.n_supply; ++k) {
189
+ const int c = s.supply[k];
190
+ if (g_dsup[c] == INF) { g_dsup[c] = 0; q[tail++] = c; }
191
+ }
192
+ while (head < tail) {
193
+ const int c = q[head++];
194
+ for (int d = 0; d < 4; ++d) {
195
+ const int nb = c + DIR_DELTA[d];
196
+ const uint8_t g = s.grid[nb];
197
+ if (g != EMPTY && g != BRICK) continue;
198
+ if (g_dsup[nb] > g_dsup[c] + 1) { g_dsup[nb] = g_dsup[c] + 1; q[tail++] = nb; }
199
+ }
200
+ }
201
+ }
202
+
203
+ static void compute_fields(const State& s) {
204
+ for (int c = 0; c < CELLS; ++c) { g_threat[c] = 0; g_occ[c] = 0; g_exp[0][c] = 0; g_exp[1][c] = 0; }
205
+ for (int i = 0; i < 6; ++i)
206
+ if (s.tank[i].alive) g_occ[s.tank[i].pos] = (unsigned char)(seat_of(i) + 1);
207
+ for (int k = 0; k < s.n_bullets; ++k) {
208
+ const Bullet& b = s.bullet[k];
209
+ int c = b.pos;
210
+ for (int st = 0; st < 2; ++st) {
211
+ c += DIR_DELTA[b.dir];
212
+ if (c < 0 || c >= CELLS) break;
213
+ g_threat[c] |= (unsigned char)(1 << st);
214
+ const uint8_t g = s.grid[c];
215
+ if (g != EMPTY && g != WATER) break;
216
+ if (tank_at(s, c) >= 0) break;
217
+ }
218
+ }
219
+ // 对方下回合能开炮的车,它的四条炮线上前 5 格都是危险格。
220
+ // 面对面互射会永远抵消(谁也不亏谁也不赚),站在这种格子上等于把节奏让给对方。
221
+ for (int i = 0; i < 6; ++i) {
222
+ if (!s.tank[i].alive || !can_fire(s, i)) continue;
223
+ const int victim = 1 - seat_of(i);
224
+ for (int d = 0; d < 4; ++d) {
225
+ int c = s.tank[i].pos;
226
+ for (int n = 0; n < 5; ++n) {
227
+ c += DIR_DELTA[d];
228
+ if (c < 0 || c >= CELLS) break;
229
+ const uint8_t g = s.grid[c];
230
+ if (g == BRICK || g == STEEL || g == OUTSIDE || g == BASE0 || g == BASE1) break;
231
+ g_exp[victim][c] = 1;
232
+ if (tank_at(s, c) >= 0) break;
233
+ }
234
+ }
235
+ }
236
+ dijkstra_attack(s, 0);
237
+ dijkstra_attack(s, 1);
238
+ bfs_intercept(s, 0);
239
+ bfs_intercept(s, 1);
240
+ bfs_supply(s);
241
+ }
242
+
243
+ // ---------------------------------------------------------------- 评估
244
+ static int incoming_hits(const State& s, int seat) {
245
+ int hits = 0;
246
+ for (int k = 0; k < s.n_bullets; ++k) {
247
+ const Bullet& b = s.bullet[k];
248
+ int c = b.pos;
249
+ for (int st = 0; st < 2; ++st) {
250
+ c += DIR_DELTA[b.dir];
251
+ if (c < 0 || c >= CELLS) break;
252
+ const int v = tank_at(s, c);
253
+ if (v >= 0) { if (seat_of(v) == seat) ++hits; break; }
254
+ const uint8_t g = s.grid[c];
255
+ if (g != EMPTY && g != WATER) break;
256
+ }
257
+ }
258
+ return hits;
259
+ }
260
+
261
+ static double side_eval(const State& s, int seat) {
262
+ const int foe = 1 - seat;
263
+ double v = 0;
264
+ int pos[6], np = 0;
265
+ for (int i = 0; i < 6; ++i) {
266
+ if (seat_of(i) != seat) continue;
267
+ const Tank& t = s.tank[i];
268
+ if (!t.alive) continue;
269
+ pos[np++] = t.pos;
270
+ v += W_ARMY * t.hp + W_LEVEL * t.level;
271
+ // 站在对方炮线上:1 血的车尤其危险;正在攻城的車可以顶着(siege 的奖励更大)。
272
+ if (g_exp[seat][t.pos]) v -= W_EXPOSED * (t.hp <= 1 ? 1.0 : 0.55);
273
+ }
274
+ for (int a = 0; a < np; ++a) // 挤在一起会互相挡路
275
+ for (int b = a + 1; b < np; ++b) {
276
+ const int dx = cell_x(pos[a]) - cell_x(pos[b]);
277
+ const int dy = cell_y(pos[a]) - cell_y(pos[b]);
278
+ const int m = (dx < 0 ? -dx : dx) + (dy < 0 ? -dy : dy);
279
+ if (m <= 1) v -= W_JAM;
280
+ }
281
+
282
+ // 攻城:有车能直接打到对方基地就给大额;否则所有车都按"还要几个回合到炮位"扣分。
283
+ double b1 = -1e9, b2 = -1e9;
284
+ bool any = false, can_shoot = false;
285
+ double sum_app = 0;
286
+ for (int i = 0; i < 6; ++i) {
287
+ if (seat_of(i) != seat || !s.tank[i].alive) continue;
288
+ double a;
289
+ const int d = shot_now(s, s.tank[i].pos, s.base_pos[foe]);
290
+ if (d <= 2) { a = W_SIEGE * (can_fire(s, i) ? 1.0 : 0.5); can_shoot = true; }
291
+ else if (d <= 4) { a = W_SIEGE2; can_shoot = true; }
292
+ else { a = -W_APPROACH * turns(s, i, g_dshot[seat][s.tank[i].pos]); sum_app += a; }
293
+ any = true;
294
+ if (a > b1) { b2 = b1; b1 = a; } else if (a > b2) b2 = a;
295
+ }
296
+ if (any) v += can_shoot ? (b1 + 0.3 * (b2 > -1e8 ? b2 : 0.0)) : sum_app;
297
+
298
+ // 拦截:对方有车指着我方基地时,按最近的车还要几个回合赶到扣分。
299
+ if (g_has_dint[seat]) {
300
+ int nd = 0;
301
+ for (int i = 0; i < 6; ++i)
302
+ if (seat_of(i) == foe && s.tank[i].alive && shot_now(s, s.tank[i].pos, s.base_pos[seat]) <= 5) ++nd;
303
+ double best = 1e9;
304
+ for (int i = 0; i < 6; ++i) {
305
+ if (seat_of(i) != seat || !s.tank[i].alive) continue;
306
+ const double tt = turns(s, i, g_dint[seat][s.tank[i].pos]);
307
+ if (tt < best) best = tt;
308
+ }
309
+ if (best > 1e8) best = 40;
310
+ if (best > 24) best = 24;
311
+ v -= W_DANGER0 * nd + W_DANGER1 * best;
312
+ }
313
+ return v;
314
+ }
315
+
316
+ // 星星是这个游戏的胜负手:升到 2 级 = 当前血 +4、不再装填(火力型子弹上限 2)、能破钢。
317
+ // 一辆 2 级快速型(5 血、一回合两步)能把对面三辆 1 血的车当猎物打。
318
+ // 而且没被吃掉的星星会一直留在场上,所以"场上有星就去吃"任何时候都成立。
319
+ static double star_eval(const State& s, int seat) {
320
+ const int nt = s.turn + 1;
321
+ const bool supply_soon = nt >= s.P.supply_first && (nt - s.P.supply_first) % s.P.supply_period == 0;
322
+ const bool any_star = s.star_mask != 0;
323
+ if (!any_star && !supply_soon) return 0;
324
+ const double w = any_star ? 1.0 : 0.45;
325
+ double best = 0;
326
+ for (int i = 0; i < 6; ++i) {
327
+ if (seat_of(i) != seat || !s.tank[i].alive) continue;
328
+ if (s.tank[i].level >= s.P.max_level) continue;
329
+ const double v = w * (58.0 - W_STARPOS * turns(s, i, g_dsup[s.tank[i].pos]));
330
+ if (v > best) best = v;
331
+ }
332
+ return best;
333
+ }
334
+
335
+ static double eval(const State& s, int me) {
336
+ if (s.done) {
337
+ if (s.winner == me) return 1e7 + (400 - s.turn);
338
+ if (s.winner < 0) return 0.0;
339
+ return -1e7 - (400 - s.turn);
340
+ }
341
+ const int foe = 1 - me;
342
+ double v = 0;
343
+ v += W_BASE * (s.base_hp[foe] - s.base_hp[me]);
344
+ v += W_KILL * (s.kills[me] - s.kills[foe]);
345
+ v += W_STAR * (s.stars_eaten[me] - s.stars_eaten[foe]);
346
+ v += side_eval(s, me) - side_eval(s, foe);
347
+ v += W_THREAT * (incoming_hits(s, foe) - incoming_hits(s, me));
348
+ v += star_eval(s, me) - star_eval(s, foe);
349
+ return v;
350
+ }
351
+
352
+ // ---------------------------------------------------------------- 命令生成
353
+ static int fire_score(const State& s, int i, int d) {
354
+ const int me = seat_of(i), foe = 1 - me;
355
+ int c = s.tank[i].pos;
356
+ int sc = 0, n = 0, bricks = 0;
357
+ for (;;) {
358
+ c += DIR_DELTA[d];
359
+ if (++n > 18) break;
360
+ const int v = tank_at(s, c);
361
+ if (v >= 0) {
362
+ if (seat_of(v) != me) {
363
+ sc += 100 - 5 * n;
364
+ if (s.tank[v].hp <= 1) sc += 45;
365
+ } else {
366
+ sc -= 150;
367
+ }
368
+ break;
369
+ }
370
+ if (c == s.base_pos[foe]) { sc += bricks == 0 ? (440 - 12 * n) : 175; break; }
371
+ if (c == s.base_pos[me]) { sc -= 480; break; }
372
+ const uint8_t g = s.grid[c];
373
+ if (g == BRICK) { if (++bricks > 1) { sc += 6; break; } continue; }
374
+ if (g == STEEL) { sc += (s.tank[i].level >= 2 && bricks == 0) ? 12 : -8; break; }
375
+ if (g == OUTSIDE) { sc -= 4; break; }
376
+ }
377
+ return sc;
378
+ }
379
+
380
+ // 这辆车的"目标距离":主要朝攻城位走;我方基地正被指时,把拦截点也算进去。
381
+ static inline int goal_of(const State& s, int i, int c) {
382
+ const int me = seat_of(i);
383
+ int g = g_dshot[me][c];
384
+ if (g >= INF) g = 90;
385
+ if (g > 60) g = 60;
386
+ if (g_has_dint[me]) {
387
+ int d = g_dint[me][c];
388
+ if (d >= INF) d = 90;
389
+ if (d > 60) d = 60;
390
+ if (d < g) g = d;
391
+ }
392
+ // 场上有星、这辆车又没满级、而且补给点比攻城位近得多时,才把补给点当目标。
393
+ if (s.star_mask && s.tank[i].alive && s.tank[i].level < s.P.max_level) {
394
+ int d = g_dsup[c];
395
+ if (d >= INF) d = 90;
396
+ if (d > 60) d = 60;
397
+ if (d + 4 < g) g = d;
398
+ }
399
+ return g;
400
+ }
401
+
402
+ static int qscore(const State& s, int i, Cmd cmd) {
403
+ const Tank& t = s.tank[i];
404
+ if (cmd == STAY) return g_threat[t.pos] ? -26 : 0;
405
+ if (is_fire(cmd)) return fire_score(s, i, cmd - 5);
406
+ const int d1 = is_move(cmd) ? cmd - 1 : (cmd - 9) >> 2;
407
+ const int p = t.pos;
408
+ const int p1 = p + DIR_DELTA[d1];
409
+ int fin = p1;
410
+ if (is_move2(cmd)) {
411
+ const int d2 = (cmd - 9) & 3;
412
+ const int base = (s.grid[p1] == EMPTY) ? p1 : p;
413
+ const int p2 = base + DIR_DELTA[d2];
414
+ fin = (s.grid[p2] == EMPTY) ? p2 : base;
415
+ }
416
+ if (s.grid[fin] != EMPTY) fin = p;
417
+ int sc = 8 * (goal_of(s, i, p) - goal_of(s, i, fin));
418
+ if (g_threat[fin]) sc -= 34;
419
+ if (g_threat[p]) sc += 22;
420
+ if (star_at(s, fin)) sc += 45;
421
+ return sc;
422
+ }
423
+
424
+ struct Cand {
425
+ Cmd c[3];
426
+ int sc;
427
+ Orders to_orders() const { Orders o; o.cmd[0] = c[0]; o.cmd[1] = c[1]; o.cmd[2] = c[2]; return o; }
428
+ };
429
+
430
+ static const int MAXC = 240;
431
+
432
+ static int gen_side(const State& s, int seat, Cand* out, int maxout) {
433
+ Cmd list[3][N_CMDS];
434
+ Cmd keep[3][10];
435
+ int ksc[3][10];
436
+ int nk[3];
437
+ for (int t = 0; t < 3; ++t) {
438
+ const int i = tank_id(seat, t);
439
+ const int nl = legal_commands(s, i, list[t]);
440
+ if (nl == 1) { nk[t] = 1; keep[t][0] = STAY; ksc[t][0] = 0; continue; }
441
+ int sc[N_CMDS], idx[N_CMDS];
442
+ for (int k = 0; k < nl; ++k) { sc[k] = qscore(s, i, list[t][k]); idx[k] = k; }
443
+ int K = (t == FAST) ? 7 : 5;
444
+ if (K > nl) K = nl;
445
+ std::nth_element(idx, idx + K, idx + nl, [&](int a, int b) { return sc[a] > sc[b]; });
446
+ std::sort(idx, idx + K, [&](int a, int b) { return sc[a] > sc[b]; });
447
+ bool has_stay = false;
448
+ for (int k = 0; k < K; ++k) if (list[t][idx[k]] == STAY) has_stay = true;
449
+ if (!has_stay && K < 10) { idx[K] = 0; ++K; } // list[t][0] 就是 STAY
450
+ nk[t] = K;
451
+ for (int k = 0; k < K; ++k) { keep[t][k] = list[t][idx[k]]; ksc[t][k] = sc[idx[k]]; }
452
+ }
453
+ int n = 0;
454
+ for (int a = 0; a < nk[0]; ++a)
455
+ for (int b = 0; b < nk[1]; ++b)
456
+ for (int c = 0; c < nk[2]; ++c) {
457
+ if (n >= MAXC) break;
458
+ out[n].c[0] = keep[0][a]; out[n].c[1] = keep[1][b]; out[n].c[2] = keep[2][c];
459
+ out[n].sc = ksc[0][a] + ksc[1][b] + ksc[2][c];
460
+ ++n;
461
+ }
462
+ std::stable_sort(out, out + n, [](const Cand& x, const Cand& y) { return x.sc > y.sc; });
463
+ return n < maxout ? n : maxout;
464
+ }
465
+
466
+ // ---------------------------------------------------------------- 搜索
467
+ static const int WM[4] = {0, 16, 10, 8};
468
+ static const int WO[4] = {0, 9, 6, 5};
469
+
470
+ static double rec(const State& s, int me, int depth) {
471
+ tick();
472
+ if (g_abort) return 0.0;
473
+ if (s.done || depth <= 0) return eval(s, me);
474
+ Cand myc[MAXC], opc[MAXC];
475
+ const int dm = depth > 3 ? 3 : depth;
476
+ const int nm = gen_side(s, me, myc, WM[dm]);
477
+ const int no = gen_side(s, 1 - me, opc, WO[dm]);
478
+ double best = -1e18;
479
+ for (int a = 0; a < nm; ++a) {
480
+ double worst = 1e18;
481
+ const Orders mo = myc[a].to_orders();
482
+ for (int b = 0; b < no; ++b) {
483
+ State t = s;
484
+ step_as(t, me, mo, opc[b].to_orders());
485
+ const double v = rec(t, me, depth - 1);
486
+ if (g_abort) return 0.0;
487
+ if (v < worst) worst = v;
488
+ if (worst <= best) break;
489
+ }
490
+ if (worst > best) best = worst;
491
+ }
492
+ return best;
493
+ }
494
+
495
+ // ---------------------------------------------------------------- 对外接口
496
+ void init(const State& s, int me) { (void)s; (void)me; g_nodes = 0; g_abort = false; }
497
+
498
+ Orders decide(const State& s, int me) {
499
+ g_nodes = 0;
500
+ g_abort = false;
501
+ compute_fields(s);
502
+
503
+ static Cand root[MAXC];
504
+ const int nroot = gen_side(s, me, root, MAXC);
505
+ Orders best = nroot > 0 ? root[0].to_orders() : Orders{};
506
+ if (s.done) return best;
507
+
508
+ static Cand opc[MAXC];
509
+ for (int depth = 1; depth <= 3; ++depth) {
510
+ g_abort = false;
511
+ double bestv = -1e18;
512
+ Orders local = best;
513
+ bool completed = true;
514
+ const int nm = gen_side(s, me, root, depth == 1 ? 20 : (depth == 2 ? 12 : 9));
515
+ const int no = gen_side(s, 1 - me, opc, depth == 1 ? 10 : (depth == 2 ? 7 : 5));
516
+ for (int a = 0; a < nm; ++a) {
517
+ double worst = 1e18;
518
+ const Orders mo = root[a].to_orders();
519
+ for (int b = 0; b < no; ++b) {
520
+ State t = s;
521
+ step_as(t, me, mo, opc[b].to_orders());
522
+ const double v = (depth == 1) ? eval(t, me) : rec(t, me, depth - 1);
523
+ if (g_abort) { completed = false; break; }
524
+ if (v < worst) worst = v;
525
+ if (worst <= bestv) break;
526
+ }
527
+ if (!completed) break;
528
+ if (worst > bestv) { bestv = worst; local = mo; }
529
+ }
530
+ if (completed && bestv > -1e17) best = local;
531
+ if (g_abort) break;
532
+ #if !FIXED_NODES
533
+ if (tank::budget_left() < 12.0) break;
534
+ #endif
535
+ }
536
+ return best;
537
+ }