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,336 @@
1
+ // bot.cpp —— 评估驱动 + 联合搜索(v2)
2
+ // 1) 每回合预计算到双方基地 / 补给点的 BFS 距离
3
+ // 2) 对手模型:对方按"单坦克贪心 + 评估函数"行动
4
+ // 3) 我方:每辆车单坦克模拟排序取 top-K,联合枚举 K^3,1-ply 评估后对 top-M 做 2-ply rollout 重排
5
+ // 4) 评估:基地血量为纲(打基地/防基地为第一要务),含子弹落点、射线能力、防守牵引、抢星
6
+ // 全部工作量固定 → 完全确定性,便于对比实验。
7
+ #include "tank.hpp"
8
+ #include <algorithm>
9
+
10
+ using namespace tank;
11
+
12
+ namespace {
13
+
14
+ constexpr int K_TOP = 8; // 每辆车进入联合枚举的候选数
15
+ constexpr int M_ROLLOUT = 288; // 做 rollout 重排的组合数
16
+
17
+ double dist_base[2][CELLS]; // [seat] 到该座位基地的步数(砖墙当可走)
18
+ double dist_supply[CELLS]; // 到最近补给点的步数
19
+
20
+ // ---------------------------------------------------------------- 距离
21
+ void bfs_dist(const State& s, const int16_t* seeds, int nseeds, double* out) {
22
+ static int q[CELLS];
23
+ for (int c = 0; c < CELLS; ++c) out[c] = 1e9;
24
+ int head = 0, tail = 0;
25
+ for (int k = 0; k < nseeds; ++k) { out[seeds[k]] = 0; q[tail++] = seeds[k]; }
26
+ while (head < tail) {
27
+ const int c = q[head++];
28
+ const double nd = out[c] + 1.0;
29
+ for (int d = 0; d < 4; ++d) {
30
+ const int n = c + DIR_DELTA[d];
31
+ const uint8_t g = s.grid[n];
32
+ if ((g == EMPTY || g == BRICK) && out[n] > nd) { out[n] = nd; q[tail++] = n; }
33
+ }
34
+ }
35
+ }
36
+
37
+ void prepare(const State& s) {
38
+ int16_t seed0 = s.base_pos[0], seed1 = s.base_pos[1];
39
+ bfs_dist(s, &seed0, 1, dist_base[0]);
40
+ bfs_dist(s, &seed1, 1, dist_base[1]);
41
+ bfs_dist(s, s.supply, s.n_supply, dist_supply);
42
+ }
43
+
44
+ inline int manh(int a, int b) {
45
+ int dx = cell_x(a) - cell_x(b), dy = cell_y(a) - cell_y(b);
46
+ return (dx < 0 ? -dx : dx) + (dy < 0 ? -dy : dy);
47
+ }
48
+
49
+ // ---------------------------------------------------------------- 射线
50
+ // 从 pos 沿 dir 看:返回第一个"挡子弹或有坦克"的格子,dist 是格数。
51
+ int ray_first(const State& s, int pos, int dir, int& dist) {
52
+ int d = 0;
53
+ for (int c = pos + DIR_DELTA[dir];; c += DIR_DELTA[dir]) {
54
+ ++d;
55
+ const uint8_t g = s.grid[c];
56
+ if (g == OUTSIDE) { dist = d; return c; }
57
+ if (tank_at(s, c) >= 0) { dist = d; return c; }
58
+ if (g == BRICK || g == STEEL || g == BASE0 || g == BASE1) { dist = d; return c; }
59
+ }
60
+ }
61
+
62
+ inline bool is_base_cell(const State& s, int c, int seat) {
63
+ return s.grid[c] == BASE0 + seat;
64
+ }
65
+
66
+ // 基地每回合最多扣 1 血、子弹上限 1:离基地越近开火频率越高(≤2 格每回合一发,越远越稀疏)
67
+ inline double rate_factor(int d) {
68
+ return d <= 2 ? 1.5 : (d <= 4 ? 1.0 : (d <= 6 ? 0.6 : 0.35));
69
+ }
70
+
71
+ // ---------------------------------------------------------------- 评估
72
+ double eval_state(const State& s, int me) {
73
+ const int opp = 1 - me;
74
+ if (s.done) {
75
+ if (s.winner == me) return 1e7;
76
+ if (s.winner < 0) return 0.0;
77
+ return -1e7;
78
+ }
79
+ double sc = 0.0;
80
+ sc += 35.0 * (s.base_hp[me] - s.base_hp[opp]);
81
+ sc += 45.0 * (s.kills[me] - s.kills[opp]);
82
+ sc += 28.0 * (s.stars_eaten[me] - s.stars_eaten[opp]);
83
+
84
+ // 坦克材质 + 走位(朝敌方基地推进)
85
+ for (int i = 0; i < 6; ++i) {
86
+ const Tank& t = s.tank[i];
87
+ if (!t.alive) continue;
88
+ const bool mine = seat_of(i) == me;
89
+ double v = 6.0 + 3.5 * t.hp + 10.0 * t.level;
90
+ double d = dist_base[1 - seat_of(i)][t.pos];
91
+ if (d > 26.0) d = 26.0;
92
+ v -= 3.0 * d;
93
+ sc += mine ? v : -v;
94
+ }
95
+
96
+ // 在飞的子弹:先撞上什么
97
+ for (int k = 0; k < s.n_bullets; ++k) {
98
+ const Bullet& b = s.bullet[k];
99
+ int d = 0;
100
+ const int hitc = ray_first(s, b.pos, b.dir, d);
101
+ const bool mine = seat_of(b.owner) == me;
102
+ const double w = d <= 2 ? 1.0 : (d <= 4 ? 0.6 : 0.3);
103
+ double v = 0.0;
104
+ const int tgt = tank_at(s, hitc);
105
+ if (tgt >= 0) {
106
+ const bool tgt_mine = seat_of(tgt) == me;
107
+ if (tgt_mine == mine) v = -10.0; // 打队友
108
+ else if (s.tank[tgt].hp <= 1) v = 14.0; // 能击杀
109
+ else v = 8.0;
110
+ } else if (s.grid[hitc] == BASE0 || s.grid[hitc] == BASE1) {
111
+ const bool base_mine = (s.grid[hitc] - BASE0) == me;
112
+ v = base_mine == mine ? -22.0 : 38.0;
113
+ } else if (s.grid[hitc] == BRICK) {
114
+ const double dg = dist_base[1 - seat_of(b.owner)][hitc];
115
+ v = 2.0 + (dg <= 2.0 ? 14.0 : (dg <= 5.0 ? 5.0 : 0.0)); // 拆敌方基地围墙加钱
116
+ } else if (s.grid[hitc] == STEEL) {
117
+ v = 0.2;
118
+ }
119
+ sc += mine ? w * v : -w * v;
120
+ }
121
+
122
+ // 射线能力:现在能打到谁 / 能打到基地(持续输出的能力)
123
+ for (int i = 0; i < 6; ++i) {
124
+ const Tank& t = s.tank[i];
125
+ if (!t.alive || !can_fire(s, i)) continue;
126
+ const bool mine = seat_of(i) == me;
127
+ for (int dir = 0; dir < 4; ++dir) {
128
+ int d = 0;
129
+ const int hitc = ray_first(s, t.pos, dir, d);
130
+ double v = 0.0;
131
+ const int tgt = tank_at(s, hitc);
132
+ if (tgt >= 0) {
133
+ if (seat_of(tgt) == seat_of(i)) v = -4.0;
134
+ else v = s.tank[tgt].hp <= 1 ? 8.0 : 4.5;
135
+ } else if (s.grid[hitc] == BASE0 || s.grid[hitc] == BASE1) {
136
+ v = ((s.grid[hitc] - BASE0) == seat_of(i)) ? -24.0 : 36.0;
137
+ }
138
+ const double w = d <= 4 ? 1.0 : 0.6;
139
+ sc += mine ? w * v : -w * v;
140
+ }
141
+ }
142
+
143
+ // 防守牵引:谁的基地被瞄准 / 被子弹盯上,谁靠基地最近的 1-2 辆车回防(其余继续进攻)
144
+ for (int X = 0; X < 2; ++X) {
145
+ int tc[8];
146
+ int nt = 0;
147
+ for (int i = 0; i < 6 && nt < 8; ++i) {
148
+ const Tank& t = s.tank[i];
149
+ if (!t.alive || seat_of(i) == X || !can_fire(s, i)) continue;
150
+ for (int dir = 0; dir < 4 && nt < 8; ++dir) {
151
+ int d;
152
+ const int hitc = ray_first(s, t.pos, dir, d);
153
+ if (is_base_cell(s, hitc, X)) tc[nt++] = t.pos;
154
+ }
155
+ }
156
+ for (int k = 0; k < s.n_bullets && nt < 8; ++k) {
157
+ const Bullet& b = s.bullet[k];
158
+ if (seat_of(b.owner) == X) continue;
159
+ int d;
160
+ const int hitc = ray_first(s, b.pos, b.dir, d);
161
+ if (is_base_cell(s, hitc, X)) tc[nt++] = b.pos;
162
+ }
163
+ if (!nt) continue;
164
+ // 按离自家基地的远近排序,最近的车全额牵引、第二辆半额
165
+ const int home = s.base_pos[X];
166
+ int order[3] = {0, 1, 2};
167
+ int md[3];
168
+ for (int k = 0; k < 3; ++k) {
169
+ const int i = tank_id(X, k);
170
+ md[k] = s.tank[i].alive ? manh(s.tank[i].pos, home) : 999;
171
+ }
172
+ for (int a = 0; a < 3; ++a)
173
+ for (int b2 = a + 1; b2 < 3; ++b2)
174
+ if (md[order[b2]] < md[order[a]]) std::swap(order[a], order[b2]);
175
+ for (int r = 0; r < 2; ++r) {
176
+ const int i = tank_id(X, order[r]);
177
+ if (!s.tank[i].alive) continue;
178
+ const int dd = manh(s.tank[i].pos, home);
179
+ const double v = (r == 0 ? 3.2 : 1.4) * (dd < 12 ? 12 - dd : 0);
180
+ sc += X == me ? v : -v;
181
+ }
182
+ }
183
+
184
+ // 星星:走近补给点(未满级)。有星时争抢,没星时小幅提前落位
185
+ {
186
+ const double wgt = s.star_mask ? 2.2 : 0.8;
187
+ for (int i = 0; i < 6; ++i) {
188
+ const Tank& t = s.tank[i];
189
+ if (!t.alive || t.level >= s.P.max_level) continue;
190
+ double d = dist_supply[t.pos];
191
+ if (d > 10.0) d = 10.0;
192
+ const double v = -wgt * d;
193
+ sc += (seat_of(i) == me) ? v : -v;
194
+ }
195
+ }
196
+ // 避免压在对方出生点上被复活炸毁(白送击毁)
197
+ for (int i = 0; i < 6; ++i) {
198
+ const Tank& t = s.tank[i];
199
+ if (!t.alive) continue;
200
+ for (int e = 0; e < 3; ++e) {
201
+ const int j = tank_id(1 - seat_of(i), e);
202
+ if (s.tank[j].alive) continue;
203
+ if (s.spawn[j] == t.pos && respawn_in(s, j) <= 2) {
204
+ sc += (seat_of(i) == me) ? -25.0 : 25.0;
205
+ }
206
+ }
207
+ }
208
+ return sc;
209
+ }
210
+
211
+ // ---------------------------------------------------------------- 贪心(对手模型 / rollout)
212
+ // opp_orders:假定对手这一回合怎么动(默认不动)
213
+ Orders greedy(const State& s, int seat, const Orders& opp_orders) {
214
+ Orders res;
215
+ for (int type = 0; type < 3; ++type) {
216
+ const int i = tank_id(seat, type);
217
+ if (!s.tank[i].alive) continue;
218
+ Cmd list[N_CMDS];
219
+ const int n = legal_commands(s, i, list);
220
+ double best = -1e18;
221
+ Cmd bc = STAY;
222
+ for (int k = 0; k < n; ++k) {
223
+ State tmp = s;
224
+ Orders mine;
225
+ mine.cmd[type] = list[k];
226
+ step_as(tmp, seat, mine, opp_orders);
227
+ const double sc = eval_state(tmp, seat);
228
+ if (sc > best) { best = sc; bc = list[k]; }
229
+ }
230
+ res.cmd[type] = bc;
231
+ }
232
+ return res;
233
+ }
234
+
235
+ struct Cand { Cmd cmd; double sc; };
236
+ struct Combo { double sc; Cmd c[3]; };
237
+
238
+ } // namespace
239
+
240
+ void init(const State& s, int me) { (void)s; (void)me; }
241
+
242
+ Orders decide(const State& s, int me) {
243
+ prepare(s);
244
+ const int opp = 1 - me;
245
+ const Orders stay_orders;
246
+ const Orders enemy_pred = greedy(s, opp, stay_orders);
247
+
248
+ // ---- 每辆车候选排序(别人不动,敌人按 enemy_pred)----
249
+ Cand rk[3][N_CMDS];
250
+ int rn[3] = {0, 0, 0};
251
+ for (int type = 0; type < 3; ++type) {
252
+ const int i = tank_id(me, type);
253
+ if (!s.tank[i].alive) { rk[type][0] = {STAY, 0.0}; rn[type] = 1; continue; }
254
+ Cmd list[N_CMDS];
255
+ const int n = legal_commands(s, i, list);
256
+ for (int k = 0; k < n; ++k) {
257
+ State tmp = s;
258
+ Orders mine;
259
+ mine.cmd[type] = list[k];
260
+ step_as(tmp, me, mine, enemy_pred);
261
+ rk[type][k] = {list[k], eval_state(tmp, me)};
262
+ }
263
+ rn[type] = n;
264
+ std::sort(rk[type], rk[type] + n, [](const Cand& a, const Cand& b) {
265
+ return a.sc != b.sc ? a.sc > b.sc : a.cmd < b.cmd;
266
+ });
267
+ if (rn[type] > K_TOP) rn[type] = K_TOP;
268
+ }
269
+
270
+ // ---- 联合枚举:1-ply(对 enemy_pred 与敌方不动各评一次,取平均)----
271
+ static Combo combos[512];
272
+ int ncombo = 0;
273
+ for (int a = 0; a < rn[0]; ++a) {
274
+ for (int b = 0; b < rn[1]; ++b) {
275
+ for (int c = 0; c < rn[2]; ++c) {
276
+ Orders mine;
277
+ mine.cmd[0] = rk[0][a].cmd;
278
+ mine.cmd[1] = rk[1][b].cmd;
279
+ mine.cmd[2] = rk[2][c].cmd;
280
+ State t1 = s;
281
+ step_as(t1, me, mine, enemy_pred);
282
+ double sc = eval_state(t1, me);
283
+ State t2 = s;
284
+ step_as(t2, me, mine, stay_orders);
285
+ sc = 0.5 * sc + 0.5 * eval_state(t2, me);
286
+ combos[ncombo].sc = sc;
287
+ combos[ncombo].c[0] = mine.cmd[0];
288
+ combos[ncombo].c[1] = mine.cmd[1];
289
+ combos[ncombo].c[2] = mine.cmd[2];
290
+ ++ncombo;
291
+ }
292
+ }
293
+ }
294
+ std::sort(combos, combos + ncombo, [](const Combo& x, const Combo& y) { return x.sc > y.sc; });
295
+
296
+ // ---- 2/3-ply rollout 重排 top-M:我动 → 双方按贪心再走(对方先按我不动出招,我再应招)----
297
+ const int m = ncombo < M_ROLLOUT ? ncombo : M_ROLLOUT;
298
+ double best_final = -1e18;
299
+ int best_idx = 0;
300
+ for (int j = 0; j < m; ++j) {
301
+ // 预算保险:机器慢、局面大时及早收手,用已有的最好组合(至少看 8 个)
302
+ if (j >= 8 && budget_left() < 14.0) break;
303
+ Orders mine;
304
+ mine.cmd[0] = combos[j].c[0];
305
+ mine.cmd[1] = combos[j].c[1];
306
+ mine.cmd[2] = combos[j].c[2];
307
+ State t1 = s;
308
+ step_as(t1, me, mine, enemy_pred);
309
+ double final;
310
+ if (t1.done) {
311
+ final = eval_state(t1, me);
312
+ } else {
313
+ const Orders eg = greedy(t1, opp, stay_orders);
314
+ const Orders mg = greedy(t1, me, eg);
315
+ State t2 = t1;
316
+ step_as(t2, me, mg, eg);
317
+ double e2 = eval_state(t2, me);
318
+ if (!t2.done) {
319
+ const Orders eg2 = greedy(t2, opp, stay_orders);
320
+ const Orders mg2 = greedy(t2, me, eg2);
321
+ State t3 = t2;
322
+ step_as(t3, me, mg2, eg2);
323
+ final = 0.3 * eval_state(t1, me) + 0.4 * e2 + 0.3 * eval_state(t3, me);
324
+ } else {
325
+ final = 0.4 * eval_state(t1, me) + 0.6 * e2;
326
+ }
327
+ }
328
+ if (final > best_final) { best_final = final; best_idx = j; }
329
+ }
330
+
331
+ Orders out;
332
+ out.cmd[0] = combos[best_idx].c[0];
333
+ out.cmd[1] = combos[best_idx].c[1];
334
+ out.cmd[2] = combos[best_idx].c[2];
335
+ return out;
336
+ }