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,551 @@
1
+ // bot.cpp —— v2:一层博弈搜索(我方联合命令 × 对方联合应答,tank::step 精确结算)
2
+ // + 启发式评估(基地血量、坦克价值、火力覆盖危险图、炮线攻击成本、吃星)。
3
+ #include "tank.hpp"
4
+ #include <algorithm>
5
+
6
+ using namespace tank;
7
+
8
+ namespace {
9
+
10
+ constexpr int BRICK_COST = 5;
11
+ constexpr int ATK_INF = 500;
12
+
13
+ static int ME = 0;
14
+
15
+ static int16_t dist_me[CELLS]; // 我方到对方基地的 Dijkstra 代价
16
+ static int16_t dist_opp[CELLS]; // 敌方到我基地的 Dijkstra 代价
17
+ static int16_t atk_me[CELLS]; // 从该格沿炮线打到对方基地的近似回合数
18
+ static int16_t atk_opp[CELLS]; // 从该格沿炮线打到我基地的近似回合数
19
+ static int16_t star_dist[CELLS]; // 到最近补给点的 BFS 距离(只走空地)
20
+ static int16_t hot[CELLS]; // 敌方火力覆盖
21
+
22
+ static int last_pos[3];
23
+ static int quiet_turns;
24
+ static int last_ehp;
25
+
26
+ bool aligned_dir(int a, int b, int& d) {
27
+ int ax = a % STRIDE, ay = a / STRIDE, bx = b % STRIDE, by = b / STRIDE;
28
+ if (ax == bx && ay != by) { d = by > ay ? DOWN : UP; return true; }
29
+ if (ay == by && ax != bx) { d = bx > ax ? RIGHT : LEFT; return true; }
30
+ return false;
31
+ }
32
+
33
+ // from 沿 d 到 target 之间(不含两端)全为空地/水且无坦克
34
+ bool line_clear(const State& s, int from, int d, int target) {
35
+ int c = from + DIR_DELTA[d];
36
+ while (c != target) {
37
+ uint8_t g = s.grid[c];
38
+ if (g != EMPTY && g != WATER) return false;
39
+ if (tank_at(s, c) >= 0) return false;
40
+ c += DIR_DELTA[d];
41
+ }
42
+ return true;
43
+ }
44
+
45
+ template <bool USE_HOT>
46
+ static void dijkstra_impl(const State& s, int target, int16_t out[CELLS]) {
47
+ static int heap_cost[CELLS * 8]; static int heap_cell[CELLS * 8];
48
+ int hn = 0;
49
+ auto push = [&](int cost, int cell) {
50
+ heap_cost[hn] = cost; heap_cell[hn] = cell;
51
+ int i = hn++;
52
+ while (i > 0) {
53
+ int p = (i - 1) / 2;
54
+ if (heap_cost[p] <= heap_cost[i]) break;
55
+ std::swap(heap_cost[p], heap_cost[i]);
56
+ std::swap(heap_cell[p], heap_cell[i]);
57
+ i = p;
58
+ }
59
+ };
60
+ auto pop = [&]() {
61
+ int top = heap_cell[0];
62
+ heap_cell[0] = heap_cell[--hn]; heap_cost[0] = heap_cost[hn];
63
+ int i = 0;
64
+ for (;;) {
65
+ int l = 2 * i + 1, r = l + 1, m = i;
66
+ if (l < hn && heap_cost[l] < heap_cost[m]) m = l;
67
+ if (r < hn && heap_cost[r] < heap_cost[m]) m = r;
68
+ if (m == i) break;
69
+ std::swap(heap_cost[i], heap_cost[m]); std::swap(heap_cell[i], heap_cell[m]);
70
+ i = m;
71
+ }
72
+ return top;
73
+ };
74
+ for (int c = 0; c < CELLS; ++c) out[c] = ATK_INF;
75
+ out[target] = 0;
76
+ push(0, target);
77
+ while (hn) {
78
+ int cell = pop();
79
+ int base = out[cell];
80
+ for (int k = 0; k < 4; ++k) {
81
+ int n = cell + DIR_DELTA[k];
82
+ uint8_t g = s.grid[n];
83
+ int w = (g == EMPTY) ? 1 : (g == BRICK ? BRICK_COST : -1);
84
+ if (w < 0) continue;
85
+ if (USE_HOT) w += 4 * hot[n]; // 敌方火力覆盖当寻路代价
86
+ if (base + w < out[n]) { out[n] = (int16_t)(base + w); push(out[n], n); }
87
+ }
88
+ }
89
+ }
90
+ static void dijkstra_to(const State& s, int target, int16_t out[CELLS]) {
91
+ dijkstra_impl<false>(s, target, out);
92
+ }
93
+ static void dijkstra_to_hot(const State& s, int target, int16_t out[CELLS]) {
94
+ dijkstra_impl<true>(s, target, out);
95
+ }
96
+
97
+ static void atk_map_to(const State& s, int tgt, const int16_t* d, int16_t out[CELLS]) {
98
+ for (int y = 0; y < s.P.size; ++y)
99
+ for (int x = 0; x < s.P.size; ++x) {
100
+ int c = cell_of(x, y);
101
+ uint8_t g = s.grid[c];
102
+ if (g != EMPTY && g != BRICK) { out[c] = (int16_t)ATK_INF; continue; }
103
+ int best = ATK_INF;
104
+ for (int k = 0; k < 4; ++k) {
105
+ int bricks = 0, steps = 0, cc = c;
106
+ bool ok = false;
107
+ for (;;) {
108
+ cc += DIR_DELTA[k]; ++steps;
109
+ uint8_t gg = s.grid[cc];
110
+ if (gg == OUTSIDE) break;
111
+ if (gg == WATER || gg == EMPTY) continue;
112
+ if (gg == BRICK) { ++bricks; continue; }
113
+ if (cc == tgt) ok = true;
114
+ break;
115
+ }
116
+ if (ok) {
117
+ int cost = 2 * bricks + (steps + 1) / 2;
118
+ if (cost < best) best = cost;
119
+ }
120
+ }
121
+ if (best >= ATK_INF) best = 40 + (d[c] < ATK_INF ? d[c] : 400);
122
+ out[c] = (int16_t)best;
123
+ }
124
+ }
125
+
126
+ void compute_maps(const State& s) {
127
+ const int opp = 1 - ME;
128
+
129
+ // ---- hot:敌方火力覆盖(先算,寻路要用) ----
130
+ for (int c = 0; c < CELLS; ++c) hot[c] = 0;
131
+ for (int e = 3 * opp; e < 3 * opp + 3; ++e) {
132
+ const Tank& te = s.tank[e];
133
+ if (!te.alive) continue;
134
+ int rl = reload_left(s, e);
135
+ if (rl > 1) continue; // 一回合内开不了炮的不算
136
+ int w = 1;
137
+ if (can_fire(s, e)) w = 2; // 现在就能开炮
138
+ if (s.tank[e].level >= 1 || s.P.types[type_of(e)].reload == 0) w += 1; // 能连发更危险
139
+ for (int k = 0; k < 4; ++k) {
140
+ int c = te.pos;
141
+ for (;;) {
142
+ c += DIR_DELTA[k];
143
+ uint8_t g = s.grid[c];
144
+ if (g != EMPTY && g != WATER) break; // 砖/钢/基地挡
145
+ if (tank_at(s, c) >= 0) { hot[c] += w; break; } // 第一辆坦克挡(会被打)
146
+ hot[c] += w;
147
+ }
148
+ }
149
+ }
150
+
151
+ // 我方的进攻寻路把敌方火力覆盖当额外代价:绕开架好的炮线
152
+ dijkstra_to_hot(s, s.base_pos[opp], dist_me);
153
+ dijkstra_to(s, s.base_pos[ME], dist_opp);
154
+ atk_map_to(s, s.base_pos[opp], dist_me, atk_me);
155
+ atk_map_to(s, s.base_pos[ME], dist_opp, atk_opp);
156
+
157
+ // ---- star_dist ----
158
+ static int q[CELLS];
159
+ int head = 0, tail = 0;
160
+ for (int c = 0; c < CELLS; ++c) star_dist[c] = (int16_t)ATK_INF;
161
+ for (int k = 0; k < s.n_supply; ++k) {
162
+ int sc = s.supply[k];
163
+ if (s.grid[sc] == EMPTY && star_dist[sc] > 0) { star_dist[sc] = 0; q[tail++] = sc; }
164
+ }
165
+ while (head < tail) {
166
+ int c = q[head++];
167
+ for (int k = 0; k < 4; ++k) {
168
+ int n = c + DIR_DELTA[k];
169
+ if (s.grid[n] == EMPTY && star_dist[n] > star_dist[c] + 1) {
170
+ star_dist[n] = (int16_t)(star_dist[c] + 1);
171
+ q[tail++] = n;
172
+ }
173
+ }
174
+ }
175
+ }
176
+
177
+ // ---------------- 评估 ----------------
178
+ double tank_value(const State& s, int i) {
179
+ static const int basev[3] = {260, 750, 520};
180
+ const Tank& t = s.tank[i];
181
+ if (!t.alive) return 40;
182
+ return basev[type_of(i)] + 130 * t.hp + 240 * t.level;
183
+ }
184
+
185
+ // pos 处的坦克 m 是否有敌方子弹正朝它飞来(对穿可抵消时 reduc 返回 true)
186
+ double bullet_threat(const State& s, int m, bool& cancelable) {
187
+ const Tank& tm = s.tank[m];
188
+ double worst = 0; cancelable = false;
189
+ for (int b = 0; b < s.n_bullets; ++b) {
190
+ const Bullet& bl = s.bullet[b];
191
+ if (seat_of(bl.owner) == seat_of(m) && bl.owner != m) continue; // 己方子弹也算(友伤),自己的不算
192
+ int d;
193
+ if (!aligned_dir(bl.pos, tm.pos, d) || d != bl.dir) continue;
194
+ if (!line_clear(s, bl.pos, d, tm.pos)) continue; // 中间有坦克/墙挡着
195
+ int dist = abs(cell_x(tm.pos) - cell_x(bl.pos)) + abs(cell_y(tm.pos) - cell_y(bl.pos));
196
+ // 下一回合它走 2 格;能否在我逃掉之前命中我(我站着不动就一定中)
197
+ if (dist <= 6) {
198
+ double p = dist <= 2 ? 0.85 : (dist <= 4 ? 0.6 : 0.35);
199
+ if (p > worst) { worst = p; cancelable = can_fire(s, m); }
200
+ }
201
+ }
202
+ return worst;
203
+ }
204
+
205
+ // pos 处的坦克朝目标基地的"持续打击率":炮线越清、越近,打基地越快。
206
+ // 返回 rate ∈ (0,1](每回合能打掉几血);打不到返回 0。线上的坦克会挡子弹。
207
+ double base_rate(const State& s, int i, int tgt_base) {
208
+ const Tank& t = s.tank[i];
209
+ if (!t.alive) return 0;
210
+ for (int d = 0; d < 4; ++d) {
211
+ int bricks = 0, steps = 0, cc = t.pos;
212
+ bool ok = false;
213
+ for (;;) {
214
+ cc += DIR_DELTA[d]; ++steps;
215
+ uint8_t g = s.grid[cc];
216
+ if (g == OUTSIDE) break;
217
+ if (g == WATER || g == EMPTY) {
218
+ if (tank_at(s, cc) >= 0) break; // 有坦克挡着
219
+ continue;
220
+ }
221
+ if (g == BRICK) { ++bricks; continue; }
222
+ if (cc == tgt_base) ok = true;
223
+ break;
224
+ }
225
+ if (ok) {
226
+ int delay = 2 * bricks + (steps + 1) / 2;
227
+ return 1.0 / delay;
228
+ }
229
+ }
230
+ return 0;
231
+ }
232
+
233
+ // 守家目标距离:优先追杀离我基地最近的入侵者(那才是正在打我基地的),否则回家守着。
234
+ int guard_dist_at(const State& s, int i, int pos) {
235
+ const int16_t* home = (seat_of(i) == ME ? dist_opp : dist_me);
236
+ int best_key = 99, best_dd = 99;
237
+ for (int e = 0; e < 6; ++e) {
238
+ if (seat_of(e) == seat_of(i) || !s.tank[e].alive) continue;
239
+ int hb = home[s.tank[e].pos] >= ATK_INF ? 20 : home[s.tank[e].pos];
240
+ if (hb > 8) continue; // 只追真正逼近我基地的
241
+ int dd = abs(cell_x(pos) - cell_x(s.tank[e].pos)) + abs(cell_y(pos) - cell_y(s.tank[e].pos));
242
+ if (hb < best_key || (hb == best_key && dd < best_dd)) { best_key = hb; best_dd = dd; }
243
+ }
244
+ if (best_key < 99) return best_dd;
245
+ return home[pos] >= ATK_INF ? 20 : home[pos];
246
+ }
247
+ int guard_dist(const State& s, int i) {
248
+ return s.tank[i].alive ? guard_dist_at(s, i, s.tank[i].pos) : 12;
249
+ }
250
+
251
+ double evaluate(const State& s) {
252
+ const int opp = 1 - ME;
253
+ if (s.done) return s.winner == ME ? 1e9 : (s.winner < 0 ? 0.0 : -1e9);
254
+ static const double atkw[3] = {24, 55, 30};
255
+ double sc = 3000.0 * (s.base_hp[ME] - s.base_hp[opp])
256
+ + 550.0 * (s.kills[ME] - s.kills[opp])
257
+ + 380.0 * (s.stars_eaten[ME] - s.stars_eaten[opp]);
258
+ for (int i = 0; i < 6; ++i) {
259
+ int seat = seat_of(i), tp = type_of(i);
260
+ const Tank& t = s.tank[i];
261
+ double v = tank_value(s, i);
262
+ sc += (seat == ME ? v : -0.92 * v);
263
+ double sign = (seat == ME ? -1.0 : +1.15);
264
+ if (tp == ARMOR) {
265
+ // 装甲型守家:追杀入侵者 / 守在基地旁
266
+ int g = t.alive ? guard_dist(s, i) : 12;
267
+ if (g > 14) g = 14;
268
+ sc += sign * 62.0 * g;
269
+ } else {
270
+ const int16_t* am = (seat == ME ? atk_me : atk_opp);
271
+ const int16_t* dm = (seat == ME ? dist_me : dist_opp);
272
+ int a = t.alive ? (am[t.pos] >= ATK_INF ? 200 : am[t.pos]) : 21;
273
+ if (a > 22) a = 22;
274
+ int dist = t.alive ? (dm[t.pos] >= ATK_INF ? 30 : dm[t.pos]) : 26;
275
+ if (dist > 28) dist = 28;
276
+ sc += sign * (atkw[tp] * a + 7.0 * dist);
277
+ }
278
+ if (t.alive) {
279
+ // 已进入基地炮位的持续伤害率(最能分出优劣的一项)
280
+ double rate = base_rate(s, i, seat == ME ? s.base_pos[opp] : s.base_pos[ME]);
281
+ sc += (seat == ME ? +2800.0 : -4000.0) * rate;
282
+ if (tp == FAST && t.level < 2) {
283
+ int sd = star_dist[t.pos] >= ATK_INF ? 60 : star_dist[t.pos];
284
+ sc += (seat == ME ? -42.0 : +14.0) * sd;
285
+ } else if (seat == ME && t.level < 2 && star_dist[t.pos] < 4) {
286
+ sc -= 22.0 * star_dist[t.pos];
287
+ }
288
+ }
289
+ }
290
+ // 有敌车正在打我基地(rate ≥ 1/2)时:全员向它收拢(杀死它是最大的一笔分)
291
+ for (int e = 3 * opp; e < 3 * opp + 3; ++e) {
292
+ const Tank& te = s.tank[e];
293
+ if (!te.alive) continue;
294
+ double er = base_rate(s, e, s.base_pos[ME]);
295
+ if (er >= 0.5) {
296
+ for (int m = 3 * ME; m < 3 * ME + 3; ++m) {
297
+ const Tank& tm = s.tank[m];
298
+ if (!tm.alive) continue;
299
+ int dd = abs(cell_x(tm.pos) - cell_x(te.pos)) + abs(cell_y(tm.pos) - cell_y(te.pos));
300
+ sc -= 10.0 * (dd > 14 ? 14 : dd);
301
+ }
302
+ }
303
+ }
304
+ // 拥堵惩罚:我方两辆车贴在一起(会互相挡路、挡炮线)
305
+ for (int m1 = 3 * ME; m1 < 3 * ME + 3; ++m1)
306
+ for (int m2 = m1 + 1; m2 < 3 * ME + 3; ++m2) {
307
+ if (!s.tank[m1].alive || !s.tank[m2].alive) continue;
308
+ int dd = abs(cell_x(s.tank[m1].pos) - cell_x(s.tank[m2].pos)) + abs(cell_y(s.tank[m1].pos) - cell_y(s.tank[m2].pos));
309
+ if (dd == 1) sc -= 45;
310
+ }
311
+ // 威胁:对方下一回合能沿清空炮线打到我(危险图 + 动态对齐都算)
312
+ for (int m = 3 * ME; m < 3 * ME + 3; ++m) {
313
+ const Tank& tm = s.tank[m];
314
+ if (!tm.alive) continue;
315
+ double vm = tank_value(s, m);
316
+ double danger = 0;
317
+ for (int e = 3 * opp; e < 3 * opp + 3; ++e) {
318
+ const Tank& te = s.tank[e];
319
+ if (!te.alive || !can_fire(s, e)) continue;
320
+ int d;
321
+ if (aligned_dir(te.pos, tm.pos, d) && line_clear(s, te.pos, d, tm.pos)) {
322
+ int back = d ^ 1; // 对向(U<->D, L<->R)
323
+ bool mutual = can_fire(s, m) && line_clear(s, tm.pos, back, te.pos);
324
+ danger += mutual ? 0.16 : 0.42; // 对射抵消的算轻一点
325
+ }
326
+ }
327
+ if (tm.pos >= 0 && tm.pos < CELLS) danger += 0.13 * hot[tm.pos];
328
+ // 在飞的子弹
329
+ bool canc;
330
+ danger += bullet_threat(s, m, canc);
331
+ if (danger > 0.75) danger = 0.75;
332
+ sc -= danger * vm;
333
+ }
334
+ for (int e = 3 * opp; e < 3 * opp + 3; ++e) {
335
+ const Tank& te = s.tank[e];
336
+ if (!te.alive) continue;
337
+ double ve = tank_value(s, e);
338
+ double danger = 0;
339
+ for (int m = 3 * ME; m < 3 * ME + 3; ++m) {
340
+ const Tank& tm = s.tank[m];
341
+ if (!tm.alive || !can_fire(s, m)) continue;
342
+ int d;
343
+ if (aligned_dir(tm.pos, te.pos, d) && line_clear(s, tm.pos, d, te.pos)) danger += 0.3;
344
+ }
345
+ bool canc;
346
+ danger += 0.7 * bullet_threat(s, e, canc);
347
+ if (danger > 0.75) danger = 0.75;
348
+ sc += danger * ve * 0.85;
349
+ }
350
+ return sc;
351
+ }
352
+
353
+ // ---------------- 候选生成 ----------------
354
+ struct Cand { Cmd c; double pre; };
355
+
356
+ bool base_line(const State& s, int pos, int d, int base, int& bricks) {
357
+ bricks = 0;
358
+ int c = pos;
359
+ for (;;) {
360
+ c += DIR_DELTA[d];
361
+ uint8_t g = s.grid[c];
362
+ if (g == OUTSIDE) return false;
363
+ if (g == WATER || g == EMPTY) continue;
364
+ if (g == BRICK) { ++bricks; continue; }
365
+ if (c == base) return true;
366
+ return false;
367
+ }
368
+ }
369
+
370
+ // 当前 pos 是否在某发子弹接下来的路径上(1..2 小步内)
371
+ bool in_bullet_path(const State& s, int pos) {
372
+ for (int b = 0; b < s.n_bullets; ++b) {
373
+ const Bullet& bl = s.bullet[b];
374
+ int c = bl.pos;
375
+ for (int k = 1; k <= 2; ++k) {
376
+ c += DIR_DELTA[bl.dir];
377
+ if (c == pos) return true;
378
+ }
379
+ }
380
+ return false;
381
+ }
382
+
383
+ void gen_cand(const State& s, int i, int me, Cand out[26], int& n, bool enemy, int keep) {
384
+ Cmd list[N_CMDS];
385
+ int ncmd = legal_commands(s, i, list);
386
+ const Tank& t = s.tank[i];
387
+ const int tgt_base = enemy ? s.base_pos[me] : s.base_pos[1 - me];
388
+ const int16_t* amap = enemy ? atk_opp : atk_me;
389
+ double a1 = amap[t.pos] >= ATK_INF ? 200 : amap[t.pos];
390
+ double h1 = t.alive ? hot[t.pos] : 0;
391
+ bool in_path = t.alive && in_bullet_path(s, t.pos);
392
+ n = 0;
393
+ for (int k = 0; k < ncmd; ++k) {
394
+ Cmd c = list[k];
395
+ double sc = 0;
396
+ if (is_fire(c)) {
397
+ int d = c - 5;
398
+ int bricks;
399
+ if (base_line(s, t.pos, d, tgt_base, bricks)) sc += 240 - 26 * bricks;
400
+ for (int b = 0; b < s.n_bullets; ++b) {
401
+ const Bullet& bl = s.bullet[b];
402
+ // 来弹正对着我飞(对向 = d^1),且在 1..6 格内:开炮可抵消
403
+ int ad;
404
+ if (bl.dir == (d ^ 1) && seat_of(bl.owner) != seat_of(i) &&
405
+ aligned_dir(bl.pos, t.pos, ad) && ad == d) {
406
+ int dist = abs(cell_x(bl.pos) - cell_x(t.pos)) + abs(cell_y(bl.pos) - cell_y(t.pos));
407
+ if (dist >= 1 && dist <= 6) sc += 150;
408
+ }
409
+ }
410
+ for (int e = 0; e < 6; ++e) {
411
+ if (e == i || !s.tank[e].alive) continue;
412
+ int d2;
413
+ if (aligned_dir(t.pos, s.tank[e].pos, d2) && d2 == d &&
414
+ line_clear(s, t.pos, d, s.tank[e].pos)) {
415
+ bool friendly = seat_of(e) == seat_of(i);
416
+ double bonus = friendly ? -180 : 110;
417
+ // 正在打我基地的敌车( camper):必须打
418
+ if (!friendly && base_rate(s, e, s.base_pos[seat_of(i)]) >= 0.5) bonus = 500;
419
+ sc += bonus;
420
+ }
421
+ }
422
+ int cc = t.pos;
423
+ for (;;) {
424
+ cc += DIR_DELTA[d];
425
+ uint8_t g = s.grid[cc];
426
+ if (g == OUTSIDE) break;
427
+ if (g == WATER || g == EMPTY) continue;
428
+ if (g == BRICK || g == STEEL) break;
429
+ if (cc == s.base_pos[seat_of(i)]) sc -= 260;
430
+ break;
431
+ }
432
+ } else if (c != STAY) {
433
+ int dest;
434
+ if (is_move2(c)) {
435
+ int d1 = (c - 9) >> 2, d2 = (c - 9) & 3;
436
+ int p1 = t.pos + DIR_DELTA[d1];
437
+ dest = (s.grid[p1 + DIR_DELTA[d2]] == EMPTY) ? p1 + DIR_DELTA[d2] : p1;
438
+ } else dest = t.pos + DIR_DELTA[c - 1];
439
+ if (dest != t.pos && s.grid[dest] == EMPTY) {
440
+ if (type_of(i) == ARMOR) {
441
+ int g1 = guard_dist_at(s, i, t.pos), g2 = guard_dist_at(s, i, dest);
442
+ sc += 58 * (g1 - g2); // 守家梯度
443
+ } else {
444
+ double a2 = amap[dest] >= ATK_INF ? 200 : amap[dest];
445
+ double w = enemy ? 30 : (type_of(i) == FIRE ? 55 : 24);
446
+ sc += w * (a1 - a2);
447
+ }
448
+ // 全员:靠近正在打我基地的敌车
449
+ for (int e = 0; e < 6; ++e) {
450
+ if (seat_of(e) == seat_of(i) || !s.tank[e].alive) continue;
451
+ if (base_rate(s, e, s.base_pos[seat_of(i)]) >= 0.5) {
452
+ int dd1 = abs(cell_x(t.pos) - cell_x(s.tank[e].pos)) + abs(cell_y(t.pos) - cell_y(s.tank[e].pos));
453
+ int dd2 = abs(cell_x(dest) - cell_x(s.tank[e].pos)) + abs(cell_y(dest) - cell_y(s.tank[e].pos));
454
+ sc += 20 * (dd1 - dd2);
455
+ }
456
+ }
457
+ sc -= 45 * hot[dest] * (enemy ? 0.3 : 1.0);
458
+ sc += 30 * (h1 - hot[dest]);
459
+ if (star_at(s, dest) && s.tank[i].level < 2) sc += 190;
460
+ if (type_of(i) == FAST && s.tank[i].level < 2) {
461
+ int sd1 = star_dist[t.pos] >= ATK_INF ? 60 : star_dist[t.pos];
462
+ int sd2 = star_dist[dest] >= ATK_INF ? 60 : star_dist[dest];
463
+ sc += 26 * (sd1 - sd2);
464
+ }
465
+ if (in_path && !in_bullet_path(s, dest)) sc += 220; // 逃出弹道
466
+ for (int b = 0; b < s.n_bullets; ++b) {
467
+ const Bullet& bl = s.bullet[b];
468
+ int c2 = bl.pos;
469
+ for (int kk = 1; kk <= 2; ++kk) {
470
+ c2 += DIR_DELTA[bl.dir];
471
+ if (c2 == dest) sc -= 130;
472
+ }
473
+ }
474
+ } else if (dest == t.pos) {
475
+ // 原地等一小步的两步命令
476
+ }
477
+ } else {
478
+ if (in_path) sc -= 200; // 站在弹道里别不动
479
+ }
480
+ out[n].c = c; out[n].pre = sc; ++n;
481
+ }
482
+ std::sort(out, out + n, [](const Cand& x, const Cand& y) {
483
+ if (x.pre != y.pre) return x.pre > y.pre;
484
+ return x.c < y.c;
485
+ });
486
+ if (n > keep) n = keep;
487
+ }
488
+
489
+ } // namespace
490
+
491
+ void init(const tank::State& s, int me) {
492
+ ME = me;
493
+ for (int k = 0; k < 3; ++k) last_pos[k] = -1;
494
+ quiet_turns = 0;
495
+ last_ehp = s.base_hp[1 - me];
496
+ }
497
+
498
+ Orders decide(const tank::State& s, int me) {
499
+ ME = me;
500
+ compute_maps(s);
501
+ quiet_turns = s.base_hp[1 - me] < last_ehp ? 0 : quiet_turns + 1;
502
+ last_ehp = s.base_hp[1 - me];
503
+
504
+ Orders best;
505
+ const int opp = 1 - me;
506
+
507
+ Cand mc[3][26]; int mn[3];
508
+ for (int tp = 0; tp < 3; ++tp) {
509
+ int i = tank_id(me, tp);
510
+ gen_cand(s, i, me, mc[tp], mn[tp], false, 6);
511
+ if (mn[tp] < 1) { mc[tp][0].c = STAY; mn[tp] = 1; }
512
+ }
513
+ Cand ec[3][26]; int en[3];
514
+ for (int tp = 0; tp < 3; ++tp) {
515
+ int i = tank_id(opp, tp);
516
+ gen_cand(s, i, me, ec[tp], en[tp], true, 4);
517
+ if (en[tp] < 1) { ec[tp][0].c = STAY; en[tp] = 1; }
518
+ }
519
+
520
+ double best_score = -1e18;
521
+ bool have = false;
522
+ for (int a = 0; a < mn[0]; ++a)
523
+ for (int b = 0; b < mn[1]; ++b)
524
+ for (int c = 0; c < mn[2]; ++c) {
525
+ Orders mine;
526
+ mine.cmd[0] = mc[0][a].c; mine.cmd[1] = mc[1][b].c; mine.cmd[2] = mc[2][c].c;
527
+ double mn_min = 1e18, mn_sum = 0; int cnt = 0;
528
+ for (int p = 0; p < en[0]; ++p)
529
+ for (int q = 0; q < en[1]; ++q)
530
+ for (int r = 0; r < en[2]; ++r) {
531
+ Orders theirs;
532
+ theirs.cmd[0] = ec[0][p].c; theirs.cmd[1] = ec[1][q].c; theirs.cmd[2] = ec[2][r].c;
533
+ State sim = s;
534
+ step_as(sim, me, mine, theirs);
535
+ double v = evaluate(sim);
536
+ if (v < mn_min) mn_min = v;
537
+ mn_sum += v; ++cnt;
538
+ }
539
+ double sc = 0.65 * mn_min + 0.35 * (mn_sum / cnt);
540
+ if (quiet_turns >= 14)
541
+ for (int tp = 0; tp < 3; ++tp)
542
+ if (mine.cmd[tp] != STAY) sc += 0.6;
543
+ if (!have || sc > best_score + 1e-9) { best_score = sc; best = mine; have = true; }
544
+ }
545
+
546
+ for (int tp = 0; tp < 3; ++tp) {
547
+ int i = tank_id(me, tp);
548
+ last_pos[tp] = s.tank[i].alive ? s.tank[i].pos : -1;
549
+ }
550
+ return best;
551
+ }