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,210 @@
1
+ // bot.cpp —— 两步前瞻(我方连续两回合),对手用两种模型(不动 / 朝我方可打的目标开炮),取平均。
2
+ #include "tank.hpp"
3
+
4
+ using namespace tank;
5
+
6
+ static int dist_enemy[CELLS]; // 每个格子到对方基地的步数(砖墙当作可走)
7
+
8
+ static void compute_distances(const State& s, int me) {
9
+ static int queue[CELLS];
10
+ for (int c = 0; c < CELLS; ++c) dist_enemy[c] = 9999;
11
+ int head = 0, tail = 0;
12
+ const int target = s.base_pos[1 - me];
13
+ dist_enemy[target] = 0;
14
+ queue[tail++] = target;
15
+ while (head < tail) {
16
+ const int c = queue[head++];
17
+ for (int d = 0; d < 4; ++d) {
18
+ const int next = c + DIR_DELTA[d];
19
+ const uint8_t g = s.grid[next];
20
+ if ((g == EMPTY || g == BRICK) && dist_enemy[next] > dist_enemy[c] + 1) {
21
+ dist_enemy[next] = dist_enemy[c] + 1;
22
+ queue[tail++] = next;
23
+ }
24
+ }
25
+ }
26
+ }
27
+
28
+ // 子弹从 pos 沿 d 飞出去,先碰到什么:>=0 坦克编号;-2 基地 seat 0;-3 基地 seat 1;-1 墙或界外。
29
+ static int first_hit(const State& s, int pos, int d) {
30
+ for (int c = pos + DIR_DELTA[d];; c += DIR_DELTA[d]) {
31
+ const uint8_t g = s.grid[c];
32
+ const int t = tank_at(s, c);
33
+ if (t >= 0) return t;
34
+ if (g == BASE0) return -2;
35
+ if (g == BASE1) return -3;
36
+ if (g == BRICK || g == STEEL || g == OUTSIDE) return -1;
37
+ }
38
+ }
39
+
40
+ // 对手的"开炮模型":每辆能开炮的车,朝第一个会打到我方坦克或我方基地的方向开炮。
41
+ static Orders enemy_greedy(const State& s, int me) {
42
+ Orders o;
43
+ const int eb = 1 - me;
44
+ for (int type = 0; type < 3; ++type) {
45
+ const int j = tank_id(eb, type);
46
+ if (!s.tank[j].alive || !can_fire(s, j)) continue;
47
+ for (int d = 0; d < 4; ++d) {
48
+ const int h = first_hit(s, s.tank[j].pos, d);
49
+ const bool ours = (h >= 0 && seat_of(h) == me) || (h <= -2 && (-2 - h) == me);
50
+ if (ours) { o.cmd[type] = cmd_fire(d); break; }
51
+ }
52
+ }
53
+ return o;
54
+ }
55
+
56
+ static double evaluate(const State& s, int me) {
57
+ const int eb = 1 - me;
58
+ if (s.done) {
59
+ if (s.winner == me) return 1e7;
60
+ if (s.winner == eb) return -1e7;
61
+ return 0;
62
+ }
63
+ double v = 0;
64
+ v += 250.0 * s.base_hp[me];
65
+ v -= 250.0 * s.base_hp[eb];
66
+ for (int type = 0; type < 3; ++type) {
67
+ const int i = tank_id(me, type), j = tank_id(eb, type);
68
+ if (s.tank[i].alive) {
69
+ v += 80 + 20 * s.tank[i].hp;
70
+ v -= 6.0 * dist_enemy[s.tank[i].pos];
71
+ if (can_fire(s, i))
72
+ for (int d = 0; d < 4; ++d) {
73
+ const int h = first_hit(s, s.tank[i].pos, d);
74
+ if (h >= 0 && seat_of(h) == eb) { v += 30; break; }
75
+ }
76
+ }
77
+ if (s.tank[j].alive) v -= 80 + 20 * s.tank[j].hp;
78
+ }
79
+ // 飞行中的子弹:看它最终会打到什么。基地每回合最多扣 1 血,同一基地的第二发及之后价值很低。
80
+ int hits_eb = 0, hits_me = 0;
81
+ for (int k = 0; k < s.n_bullets; ++k) {
82
+ const Bullet& b = s.bullet[k];
83
+ const int owner_seat = seat_of(b.owner);
84
+ const int h = first_hit(s, b.pos, b.dir);
85
+ if (h >= 0) {
86
+ if (seat_of(h) == owner_seat) v -= (owner_seat == me ? 25 : -25);
87
+ else if (owner_seat == me) v += 40;
88
+ else v -= 40 + 10 * s.tank[h].hp;
89
+ } else if (h == -2 - eb) {
90
+ if (owner_seat == me) v += hits_eb++ == 0 ? 90 : 20;
91
+ } else if (h == -2 - me) {
92
+ if (owner_seat == eb) v -= hits_me++ == 0 ? 90 : 20;
93
+ }
94
+ }
95
+ // 站位:沿某个方向看过去,能打到某个基地(中间只有空地、水,最多一块砖)
96
+ for (int seat = 0; seat < 2; ++seat) {
97
+ const int target_base = seat == me ? eb : me;
98
+ for (int type = 0; type < 3; ++type) {
99
+ const int i = tank_id(seat, type);
100
+ if (!s.tank[i].alive) continue;
101
+ for (int d = 0; d < 4; ++d) {
102
+ int bricks = 0;
103
+ int c = s.tank[i].pos;
104
+ for (;;) {
105
+ c += DIR_DELTA[d];
106
+ const uint8_t g = s.grid[c];
107
+ if (g == BASE0 || g == BASE1) {
108
+ if (g == BASE0 + target_base) {
109
+ const double bonus = bricks == 0 ? 12.0 : 5.0;
110
+ v += seat == me ? bonus : -bonus;
111
+ }
112
+ break;
113
+ }
114
+ if (g == BRICK) { if (++bricks > 1) break; continue; }
115
+ if (g != EMPTY && g != WATER) break;
116
+ }
117
+ }
118
+ }
119
+ }
120
+ return v;
121
+ }
122
+
123
+ // 我方候选命令(死车只有 STAY)
124
+ static void candidates(const State& s, int me, Cmd cand[3][N_CMDS], int nc[3]) {
125
+ for (int type = 0; type < 3; ++type) {
126
+ const int i = tank_id(me, type);
127
+ if (!s.tank[i].alive) { cand[type][0] = STAY; nc[type] = 1; }
128
+ else nc[type] = legal_commands(s, i, cand[type]);
129
+ }
130
+ }
131
+
132
+ // 用两种对手模型各自前推一回合,返回平均值
133
+ static double value_after(const State& s, int me, const Orders& mine, const Orders& eo) {
134
+ State t = s;
135
+ if (me == 0) step(t, mine, eo);
136
+ else step(t, eo, mine);
137
+ return evaluate(t, me);
138
+ }
139
+
140
+ // 一步搜索:返回所有组合的平均值(对两种对手模型)和最优组合
141
+ struct Node { double v; Orders o; };
142
+
143
+ static const int MAX_TOP = 3;
144
+
145
+ void init(const State& s, int me) {
146
+ (void)s; (void)me;
147
+ }
148
+
149
+ Orders decide(const State& s, int me) {
150
+ compute_distances(s, me);
151
+ Cmd cand[3][N_CMDS];
152
+ int nc[3];
153
+ candidates(s, me, cand, nc);
154
+
155
+ const Orders greedy = enemy_greedy(s, me);
156
+ const Orders stay{};
157
+
158
+ // 第一层:所有组合,对两种对手模型取平均
159
+ static Node all[25 * 25 * 25];
160
+ int n = 0;
161
+ for (int a = 0; a < nc[0]; ++a)
162
+ for (int b = 0; b < nc[1]; ++b)
163
+ for (int c = 0; c < nc[2]; ++c) {
164
+ Orders mine;
165
+ mine.cmd[0] = cand[0][a];
166
+ mine.cmd[1] = cand[1][b];
167
+ mine.cmd[2] = cand[2][c];
168
+ const double v = 0.5 * value_after(s, me, mine, stay) + 0.5 * value_after(s, me, mine, greedy);
169
+ all[n++] = Node{v, mine};
170
+ }
171
+ // 取前 K 个
172
+ int top_n = n < MAX_TOP ? n : MAX_TOP;
173
+ for (int k = 0; k < top_n; ++k) {
174
+ int bi = k;
175
+ for (int j = k + 1; j < n; ++j) if (all[j].v > all[bi].v) bi = j;
176
+ Node tmp = all[k]; all[k] = all[bi]; all[bi] = tmp;
177
+ }
178
+
179
+ Orders best = all[0].o;
180
+ double best_v = -1e300;
181
+ for (int k = 0; k < top_n; ++k) {
182
+ if (k > 0 && tank::budget_left() < 8) break;
183
+ const Orders mine = all[k].o;
184
+ double worst_sum = 0;
185
+ for (const Orders* eo : {&stay, &greedy}) {
186
+ State t = s;
187
+ if (me == 0) step(t, mine, *eo);
188
+ else step(t, *eo, mine);
189
+ // 第二层:我方在 t 上做一步搜索,对手同样两种模型
190
+ Cmd c2[3][N_CMDS];
191
+ int nc2[3];
192
+ candidates(t, me, c2, nc2);
193
+ const Orders g2 = enemy_greedy(t, me);
194
+ double inner = -1e300;
195
+ for (int a = 0; a < nc2[0]; ++a)
196
+ for (int b = 0; b < nc2[1]; ++b)
197
+ for (int c = 0; c < nc2[2]; ++c) {
198
+ Orders m2;
199
+ m2.cmd[0] = c2[0][a];
200
+ m2.cmd[1] = c2[1][b];
201
+ m2.cmd[2] = c2[2][c];
202
+ const double v = 0.5 * value_after(t, me, m2, stay) + 0.5 * value_after(t, me, m2, g2);
203
+ if (v > inner) inner = v;
204
+ }
205
+ worst_sum += 0.5 * inner;
206
+ }
207
+ if (worst_sum > best_v) { best_v = worst_sum; best = mine; }
208
+ }
209
+ return best;
210
+ }
@@ -0,0 +1,353 @@
1
+ // bot.cpp —— 一层 maxmin 精确前推 + 启发式评估。
2
+ #include "tank.hpp"
3
+ #include <algorithm>
4
+ #include <cstdio>
5
+ #include <cstdlib>
6
+
7
+ using namespace tank;
8
+
9
+ static int g_me;
10
+ static int dist_base[2][CELLS]; // dist_base[seat][c]:c 到 seat 的基地要走几步(砖墙当可走)
11
+ static bool danger[CELLS]; // 敌方子弹本回合两个小步内会经过的格子
12
+ static bool g_threat; // 有敌车能直接打到 / 贴近我的基地
13
+
14
+ static void bfs(const State& s, int src, int* dist) {
15
+ static int queue[CELLS];
16
+ for (int c = 0; c < CELLS; ++c) dist[c] = 9999;
17
+ int head = 0, tail = 0;
18
+ dist[src] = 0;
19
+ queue[tail++] = src;
20
+ while (head < tail) {
21
+ const int c = queue[head++];
22
+ for (int d = 0; d < 4; ++d) {
23
+ const int nx = c + DIR_DELTA[d];
24
+ const uint8_t g = s.grid[nx];
25
+ if ((g == EMPTY || g == BRICK) && dist[nx] > dist[c] + 1) {
26
+ dist[nx] = dist[c] + 1;
27
+ queue[tail++] = nx;
28
+ }
29
+ }
30
+ }
31
+ }
32
+
33
+ // 从 pos 朝 d 看炮线(对 seat 而言)。bricks 输出途中砖墙数。
34
+ static double fire_line_score(const State& s, int seat, int pos, int d, int level, int* bricks = nullptr) {
35
+ int nb = 0;
36
+ for (int c = pos + DIR_DELTA[d];; c += DIR_DELTA[d]) {
37
+ const uint8_t g = s.grid[c];
38
+ if (g == EMPTY || g == WATER) {
39
+ const int t = tank_at(s, c);
40
+ if (t >= 0) {
41
+ if (bricks) *bricks = nb;
42
+ return seat_of(t) == seat ? -70.0 : 55.0;
43
+ }
44
+ continue;
45
+ }
46
+ if (bricks) *bricks = nb;
47
+ if (g == BRICK) { ++nb; continue; }
48
+ if (g == STEEL) return (level >= 2) ? 6.0 : -2.0;
49
+ if (g == BASE0 + (1 - seat)) return 45.0 - 6.0 * nb; // 隔砖也能打穿,慢一些
50
+ if (g == BASE0 + seat) return -120.0;
51
+ return -2.0;
52
+ }
53
+ }
54
+
55
+ // 从 pos 朝某方向看不隔着任何墙就到 target_base(纯空地/水)。
56
+ static bool clear_line_to_base(const State& s, int pos, int base_cell) {
57
+ const int bx = cell_x(base_cell), by = cell_y(base_cell);
58
+ const int px = cell_x(pos), py = cell_y(pos);
59
+ if (px != bx && py != by) return false;
60
+ int d;
61
+ if (px == bx) d = by > py ? DOWN : UP;
62
+ else d = bx > px ? RIGHT : LEFT;
63
+ for (int c = pos + DIR_DELTA[d];; c += DIR_DELTA[d]) {
64
+ if (c == base_cell) return true;
65
+ const uint8_t g = s.grid[c];
66
+ if (g != EMPTY && g != WATER) return false;
67
+ }
68
+ }
69
+
70
+ // 从 pos 到 foe 基地的四条直线里,最少隔几块砖(到不了返回 99;0 = 无墙直通)。
71
+ static int min_bricks_to_base(const State& s, int seat, int pos) {
72
+ const int base = s.base_pos[1 - seat];
73
+ if (cell_x(pos) != cell_x(base) && cell_y(pos) != cell_y(base)) return 99;
74
+ int d;
75
+ if (cell_x(pos) == cell_x(base)) d = cell_y(base) > cell_y(pos) ? DOWN : UP;
76
+ else d = cell_x(base) > cell_x(pos) ? RIGHT : LEFT;
77
+ int nb = 0;
78
+ for (int c = pos + DIR_DELTA[d];; c += DIR_DELTA[d]) {
79
+ if (c == base) return nb;
80
+ const uint8_t g = s.grid[c];
81
+ if (g == BRICK) { ++nb; continue; }
82
+ if (g == EMPTY || g == WATER) continue;
83
+ return 99;
84
+ }
85
+ }
86
+
87
+ static double evaluate(const State& s) {
88
+ const int me = g_me, opp = 1 - me;
89
+ if (s.done) {
90
+ if (s.winner == me) return 1e6 - s.turn;
91
+ if (s.winner == opp) return -1e6 + s.turn;
92
+ return 0.0;
93
+ }
94
+ double v = 0;
95
+ v += 140.0 * (s.base_hp[me] - s.base_hp[opp]);
96
+ v += 40.0 * (s.kills[me] - s.kills[opp]);
97
+ v += 10.0 * (s.stars_eaten[me] - s.stars_eaten[opp]);
98
+ for (int i = 0; i < 6; ++i) {
99
+ const Tank& t = s.tank[i];
100
+ const int seat = seat_of(i), foe = 1 - seat;
101
+ double tv;
102
+ if (t.alive) {
103
+ tv = 20.0 + 5.0 * t.hp + 14.0 * t.level;
104
+ const int dfb = dist_base[foe][t.pos];
105
+ tv -= 4.0 * dfb;
106
+ tv += 4.0 * (12 - dfb < 0 ? 0 : 12 - dfb); // 贴近对方基地额外加分
107
+ tv += 5.0 * (7 - dfb < 0 ? 0 : 7 - dfb); // 非常贴近再加(攻其基地/防我基地都覆盖)
108
+ double best_line = -1e9;
109
+ for (int d = 0; d < 4; ++d) {
110
+ const double ls = fire_line_score(s, seat, t.pos, d, t.level);
111
+ if (ls > best_line) best_line = ls;
112
+ }
113
+ tv += 0.25 * best_line;
114
+ // 能持续轰对方基地的位置极其关键:值一大段未来伤害
115
+ const int nb2 = min_bricks_to_base(s, seat, t.pos);
116
+ if (nb2 == 0) tv += 250.0;
117
+ else if (nb2 < 99) tv += 60.0 - 8.0 * nb2;
118
+ // 站在对方出生点上、对方下回合末复活:会被炸毁
119
+ for (int j = 3 * foe; j < 3 * foe + 3; ++j)
120
+ if (!s.tank[j].alive && t.pos == s.spawn[j] && respawn_in(s, j) <= 1) tv -= 30.0;
121
+ } else {
122
+ tv = -2.0 * respawn_in(s, i);
123
+ }
124
+ v += (seat == me ? tv : -tv);
125
+ }
126
+ return v;
127
+ }
128
+
129
+ struct Ranked { Cmd c; double sc; };
130
+
131
+ // 按类别挑候选命令:保证开炮、推进、躲避/回防都有代表进入模拟,而不是被单一分数剪没。
132
+ static int rank_commands(const State& s, int i, int seat, Ranked* out, int k) {
133
+ Cmd list[N_CMDS];
134
+ const int n = legal_commands(s, i, list);
135
+ const Tank& t = s.tank[i];
136
+ const int foe = 1 - seat;
137
+ int m = 0;
138
+ auto add = [&](Cmd c) {
139
+ for (int j = 0; j < m; ++j) if (out[j].c == c) return;
140
+ if (m < k) out[m++] = Ranked{c, 0};
141
+ };
142
+ add(STAY);
143
+ // 移动落点(假定走成)
144
+ auto endpoint = [&](Cmd c) {
145
+ int d1, d2 = -1;
146
+ if (is_move(c)) d1 = c - 1;
147
+ else { d1 = (c - 9) >> 2; d2 = (c - 9) & 3; }
148
+ int p1 = t.pos + DIR_DELTA[d1];
149
+ if (s.grid[p1] != EMPTY) p1 = t.pos;
150
+ int p2 = p1;
151
+ if (d2 >= 0 && s.grid[p1 + DIR_DELTA[d2]] == EMPTY) p2 = p1 + DIR_DELTA[d2];
152
+ return p2;
153
+ };
154
+ // 1) 开炮:炮线得分前 2
155
+ for (int pick = 0; pick < 2; ++pick) {
156
+ Cmd bc = 0; double bs = -1e9;
157
+ for (int j = 0; j < n; ++j) {
158
+ if (!is_fire(list[j])) continue;
159
+ bool dup = false;
160
+ for (int q = 0; q < m; ++q) if (out[q].c == list[j]) dup = true;
161
+ if (dup) continue;
162
+ const double sc = fire_line_score(s, seat, t.pos, list[j] - 5, t.level);
163
+ if (sc > bs) { bs = sc; bc = list[j]; }
164
+ }
165
+ if (bc && bs > -5.0) add(bc);
166
+ }
167
+ // 2) 推进:向对方基地靠近最多的 3 步(危险落点扣一点,但不至于剪光)
168
+ for (int pick = 0; pick < 3; ++pick) {
169
+ Cmd bc = 0; double bs = -1e9;
170
+ for (int j = 0; j < n; ++j) {
171
+ if (!is_move(list[j]) && !is_move2(list[j])) continue;
172
+ bool dup = false;
173
+ for (int q = 0; q < m; ++q) if (out[q].c == list[j]) dup = true;
174
+ if (dup) continue;
175
+ const int p2 = endpoint(list[j]);
176
+ double sc = 3.0 * (dist_base[foe][t.pos] - dist_base[foe][p2]);
177
+ if (is_move2(list[j])) sc += 1.0;
178
+ if (danger[p2]) sc -= 20.0;
179
+ if (sc > bs) { bs = sc; bc = list[j]; }
180
+ }
181
+ if (bc) add(bc);
182
+ }
183
+ // 3) 躲避 / 回防 / 吃星:再补 2 条
184
+ for (int pick = 0; pick < 2; ++pick) {
185
+ Cmd bc = 0; double bs = -1e9;
186
+ for (int j = 0; j < n; ++j) {
187
+ if (!is_move(list[j]) && !is_move2(list[j])) continue;
188
+ bool dup = false;
189
+ for (int q = 0; q < m; ++q) if (out[q].c == list[j]) dup = true;
190
+ if (dup) continue;
191
+ const int p2 = endpoint(list[j]);
192
+ double sc = 0;
193
+ if (danger[t.pos] && !danger[p2]) sc += 30.0;
194
+ if (danger[p2]) sc -= 30.0;
195
+ if (g_threat) sc += 3.0 * (dist_base[seat][t.pos] - dist_base[seat][p2]);
196
+ for (int q = 0; q < s.n_supply; ++q)
197
+ if ((s.star_mask >> q) & 1) {
198
+ const int sp = s.supply[q];
199
+ sc += 6.0 * (std::abs(cell_x(sp) - cell_x(t.pos)) + std::abs(cell_y(sp) - cell_y(t.pos))
200
+ - std::abs(cell_x(sp) - cell_x(p2)) - std::abs(cell_y(sp) - cell_y(p2)));
201
+ }
202
+ if (sc > bs) { bs = sc; bc = list[j]; }
203
+ }
204
+ if (bc) add(bc);
205
+ }
206
+ // 给候选补一个粗分数,供联合命令排序(强对手命令优先,利于剪枝)
207
+ for (int j = 0; j < m; ++j) {
208
+ const Cmd c = out[j].c;
209
+ if (c == STAY) out[j].sc = 0;
210
+ else if (is_fire(c)) out[j].sc = fire_line_score(s, seat, t.pos, c - 5, t.level);
211
+ else {
212
+ const int p2 = endpoint(c);
213
+ out[j].sc = 3.0 * (dist_base[foe][t.pos] - dist_base[foe][p2]) - (danger[p2] ? 20.0 : 0.0);
214
+ }
215
+ }
216
+ return m;
217
+ }
218
+
219
+ static void compute_danger(const State& s, int me) {
220
+ for (int c = 0; c < CELLS; ++c) danger[c] = false;
221
+ for (int k = 0; k < s.n_bullets; ++k) {
222
+ const Bullet& b = s.bullet[k];
223
+ if (seat_of(b.owner) == me) continue;
224
+ int c = b.pos;
225
+ for (int stepn = 0; stepn < 2; ++stepn) {
226
+ c += DIR_DELTA[b.dir];
227
+ danger[c] = true;
228
+ const uint8_t g = s.grid[c];
229
+ if (g != EMPTY && g != WATER) break;
230
+ }
231
+ }
232
+ }
233
+
234
+ // rollout 用的贪心策略:每辆车独立挑一条"其它车不动时评估最好"的命令。
235
+ static Orders greedy_joint(const State& s, int seat, int k) {
236
+ Orders o;
237
+ for (int type = 0; type < 3; ++type) {
238
+ const int i = tank_id(seat, type);
239
+ o.cmd[type] = STAY;
240
+ if (!s.tank[i].alive) continue;
241
+ Ranked rc[9];
242
+ const int m = rank_commands(s, i, seat, rc, k);
243
+ double bv = -1e18;
244
+ for (int j = 0; j < m; ++j) {
245
+ State nx = s;
246
+ Orders solo, zero;
247
+ solo.cmd[type] = rc[j].c;
248
+ step_as(nx, seat, solo, zero);
249
+ const double v = seat == g_me ? evaluate(nx) : -evaluate(nx);
250
+ if (v > bv) { bv = v; o.cmd[type] = rc[j].c; }
251
+ }
252
+ }
253
+ return o;
254
+ }
255
+
256
+ // 从局面 s 起双方都用贪心策略打 depth 回合,返回终局评估(g_me 视角)。
257
+ static double rollout(State s, int depth) {
258
+ for (int t = 0; t < depth && !s.done; ++t) {
259
+ const Orders o0 = greedy_joint(s, 0, 6);
260
+ const Orders o1 = greedy_joint(s, 1, 6);
261
+ step(s, o0, o1);
262
+ }
263
+ return evaluate(s);
264
+ }
265
+
266
+ void init(const State& s, int me) {
267
+ g_me = me;
268
+ bfs(s, s.base_pos[0], dist_base[0]);
269
+ bfs(s, s.base_pos[1], dist_base[1]);
270
+ }
271
+
272
+ Orders decide(const State& s, int me) {
273
+ g_me = me;
274
+ bfs(s, s.base_pos[0], dist_base[0]);
275
+ bfs(s, s.base_pos[1], dist_base[1]);
276
+ compute_danger(s, me);
277
+
278
+ g_threat = false;
279
+ for (int i = 3 * (1 - me); i < 3 * (1 - me) + 3; ++i)
280
+ if (s.tank[i].alive &&
281
+ (dist_base[me][s.tank[i].pos] <= 4 || clear_line_to_base(s, s.tank[i].pos, s.base_pos[me])))
282
+ g_threat = true;
283
+
284
+ const int K_MINE = 8, K_OPP = 6;
285
+ Ranked mine[3][9], opps[3][9];
286
+ int nm[3] = {1, 1, 1}, no[3] = {1, 1, 1};
287
+ for (int type = 0; type < 3; ++type) {
288
+ const int im = tank_id(me, type), io = tank_id(1 - me, type);
289
+ if (s.tank[im].alive) nm[type] = rank_commands(s, im, me, mine[type], K_MINE);
290
+ else { mine[type][0] = Ranked{STAY, 0}; }
291
+ if (s.tank[io].alive) no[type] = rank_commands(s, io, 1 - me, opps[type], K_OPP);
292
+ else { opps[type][0] = Ranked{STAY, 0}; }
293
+ }
294
+
295
+ struct Joint { Orders o; double sc; };
296
+ static Joint opp_joint[1000];
297
+ int njo = 0;
298
+ for (int a = 0; a < no[0]; ++a)
299
+ for (int b = 0; b < no[1]; ++b)
300
+ for (int c = 0; c < no[2]; ++c) {
301
+ Orders o;
302
+ o.cmd[0] = opps[0][a].c; o.cmd[1] = opps[1][b].c; o.cmd[2] = opps[2][c].c;
303
+ opp_joint[njo++] = Joint{o, opps[0][a].sc + opps[1][b].sc + opps[2][c].sc};
304
+ }
305
+ std::sort(opp_joint, opp_joint + njo, [](const Joint& x, const Joint& y) { return x.sc > y.sc; });
306
+
307
+ Orders best;
308
+ double best_val = -1e18;
309
+ int checked = 0;
310
+ // 主循环里记录前 12 个候选,稍后对它们做 rollout 再选
311
+ static Orders top_ord[12];
312
+ static double top_val[12];
313
+ int n_top = 0;
314
+ auto consider_top = [&](const Orders& mo, double v) {
315
+ if (n_top < 12) {
316
+ top_ord[n_top] = mo; top_val[n_top] = v; ++n_top;
317
+ } else {
318
+ int wi = 0;
319
+ for (int q = 1; q < 12; ++q) if (top_val[q] < top_val[wi]) wi = q;
320
+ if (v > top_val[wi]) { top_ord[wi] = mo; top_val[wi] = v; }
321
+ }
322
+ };
323
+ for (int a = 0; a < nm[0]; ++a)
324
+ for (int b = 0; b < nm[1]; ++b)
325
+ for (int c = 0; c < nm[2]; ++c) {
326
+ Orders mo;
327
+ mo.cmd[0] = mine[0][a].c; mo.cmd[1] = mine[1][b].c; mo.cmd[2] = mine[2][c].c;
328
+ double worst = 1e18, v_top = 0;
329
+ int j = 0;
330
+ for (; j < njo; ++j) {
331
+ State nx = s;
332
+ step_as(nx, me, mo, opp_joint[j].o);
333
+ const double v = evaluate(nx);
334
+ if (j == 0) v_top = v;
335
+ if (v < worst) worst = v;
336
+ if (worst < best_val && v_top < best_val) break;
337
+ if ((j & 15) == 15 && budget_left() < 8.0) break;
338
+ }
339
+ const double combined = 0.4 * worst + 0.6 * v_top;
340
+ if (combined > best_val) {
341
+ best_val = combined;
342
+ best = mo;
343
+ }
344
+ if (j == njo) consider_top(mo, combined); // 只收完整评估过的
345
+ if (++checked >= 12) {
346
+ checked = 0;
347
+ if (budget_left() < 8.0) goto done;
348
+ }
349
+ }
350
+ done:
351
+ (void)n_top;
352
+ return best;
353
+ }