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,87 @@
1
+ // Final second-half bot. Rapid upgrade and siege, two flexible defenders.
2
+ // Supply assignment distinguishes travel from waiting, including before turn 10.
3
+ // A destructible cell can itself be a future firing position: seed its ray cost
4
+ // BEFORE charging for that wall, then charge demolition when walking into it.
5
+ // Enemy-occupied firing cells are excluded, and blockers add siege-ray cost.
6
+ // Distant shots at mobile tanks have exponentially declining expected value.
7
+ // Weighted path fields include the cost of opening brick/steel corridors.
8
+ // All tactical transitions use the authoritative simultaneous-action engine.
9
+ // Opponent response weights come from regret matching; risk tolerance follows
10
+ // its demonstrated upgrade progress. The final choice includes a second turn.
11
+ #include "tank.hpp"
12
+ #include <algorithm>
13
+ #include <cmath>
14
+ #include <queue>
15
+ #include <utility>
16
+ #include <vector>
17
+ using namespace tank;
18
+ namespace {
19
+ constexpr float INF=10000;
20
+ struct Context { const State* root; float attack[6][CELLS],supply[6][MAX_SUPPLY][CELLS],wall[2][CELLS],defend[6][CELLS]; int goal[6],threat[6]; };
21
+ static Context ctx;
22
+ static int priorGoal[6],visits[6][CELLS];
23
+ float walkCost(const State&s,int i,int c){int g=s.grid[c];float v=i%3==FAST?.56f:1.f;if(g==EMPTY)return v;if(g==BRICK||(g==STEEL&&s.tank[i].level>=2))return v+3.4f;return INF;}
24
+ void flood(const State&s,int i,float*dis){using Node=std::pair<float,int>;std::priority_queue<Node,std::vector<Node>,std::greater<Node>>q;for(int y=0;y<s.P.size;y++)for(int x=0;x<s.P.size;x++){int c=cell_of(x,y);if(dis[c]<INF)q.push({dis[c],c});}while(!q.empty()){auto[v,c]=q.top();q.pop();if(v!=dis[c])continue;float cost=walkCost(s,i,c);if(cost>=INF)continue;for(int d=0;d<4;d++){int n=c+DIR_DELTA[d];if(walkCost(s,i,n)>=INF)continue;float z=v+cost;if(z<dis[n]){dis[n]=z;q.push({z,n});}}}}
25
+ // Goals: -1 attacks the base; -2 intercepts an assigned threat; 0..2 seek supplies.
26
+ void prepare(const State&s){ctx.root=&s;for(int i=0;i<6;i++){float*a=ctx.attack[i];std::fill(a,a+CELLS,INF);int base=s.base_pos[1-i/3];for(int d=0;d<4;d++){float walls=0;int r=0;for(int c=base+DIR_DELTA[d];s.grid[c]!=OUTSIDE;c+=DIR_DELTA[d]){r++;int g=s.grid[c];if(g==BASE0||g==BASE1||(g==STEEL&&s.tank[i].level<2))break;int j=tank_at(s,c);
27
+ if((g==EMPTY||g==BRICK||g==STEEL)&&(j<0||j==i))a[c]=walls+.85f*r;
28
+ if(g==BRICK||g==STEEL)walls+=3.4f;
29
+ if(j>=0&&j!=i)walls+=j/3==i/3?2.f:5.f+1.5f*s.tank[j].hp;}}flood(s,i,a);for(int k=0;k<s.n_supply;k++){float*b=ctx.supply[i][k];std::fill(b,b+CELLS,INF);b[s.supply[k]]=0;flood(s,i,b);}}
30
+ int wait=s.turn<10?10-s.turn:20-(s.turn-10)%20;
31
+ for(int seat=0;seat<2;seat++){float best=-1e9;int bg[3]={-1,-1,-1};for(int a=-1;a<s.n_supply;a++)for(int b=-1;b<s.n_supply;b++)for(int c=-1;c<s.n_supply;c++){int gs[3]={a,b,c};if((a>=0&&a==b)||(a>=0&&a==c)||(b>=0&&b==c))continue;float v=0;bool valid=true;for(int t=0;t<3;t++){int i=3*seat+t,k=gs[t];const auto&tk=s.tank[i];int p=tk.alive?tk.pos:s.spawn[i];float av=18-1.35f*ctx.attack[i][p];if(k<0){v+=av*(t==0?2.5f:1.f);continue;}if(tk.level>=2||(t==0&&tk.level>=1)){valid=false;break;}float dis=ctx.supply[i][k][p];int w=(s.star_mask>>k&1)?0:wait;float sv=(tk.level==0?30.f:24.f)-2.4f*dis-(s.turn<10?.7f:2.4f)*std::max(0.f,float(w)-dis)-.6f*dis;if(priorGoal[i]==k)sv+=1.1f;if(!tk.alive)sv-=4;v+=sv*(t==0?2.5f:1.f);}if(valid&&v>best){best=v;for(int t=0;t<3;t++)bg[t]=gs[t];}}for(int t=0;t<3;t++)ctx.goal[3*seat+t]=bg[t];}
32
+ for(int seat=0;seat<2;seat++) {
33
+ std::vector<std::pair<float,int>> threats;
34
+ for(int i=3*seat;i<3*seat+3;i++)ctx.threat[i]=-1;
35
+ for(int j=3*(1-seat);j<3*(1-seat)+3;j++)if(s.tank[j].alive) {
36
+ int p=s.tank[j].pos,base=s.base_pos[seat],dy=std::abs(cell_y(p)-cell_y(base));float severity=8-dy;
37
+ if(cell_x(p)==cell_x(base)||cell_y(p)==cell_y(base)) {
38
+ int de=cell_x(p)==cell_x(base)?(base>p?STRIDE:-STRIDE):(base>p?1:-1);int walls=0;
39
+ for(int c=p+de;c!=base;c+=de){if(s.grid[c]==BRICK)walls++;if(s.grid[c]==STEEL)walls+=5;}if(walls<=1)severity+=9;
40
+ }if(dy<8&&severity>0)threats.push_back({severity,j});
41
+ }std::sort(threats.rbegin(),threats.rend());
42
+ for(int k=0;k<std::min(2,int(threats.size()));k++){
43
+ int threat=threats[k].second;if(k>0&&threats[k].first<4)break;
44
+ int besti=-1;float bestcost=1e9;
45
+ for(int i=3*seat;i<3*seat+3;i++)if(ctx.goal[i]!=-2){
46
+ float dis[CELLS];std::fill(dis,dis+CELLS,INF);int target=s.tank[threat].pos;
47
+ for(int d=0;d<4;d++){float walls=0;for(int c=target+DIR_DELTA[d],r=1;r<=8;c+=DIR_DELTA[d],r++) {
48
+ int g=s.grid[c];if(g==OUTSIDE||g==BASE0||g==BASE1||(g==STEEL&&s.tank[i].level<2))break;
49
+ if(g==BRICK||g==STEEL)walls+=3.4f;if(g==EMPTY)dis[c]=walls+.6f*r;
50
+ }}flood(s,i,dis);int p=s.tank[i].alive?s.tank[i].pos:s.spawn[i];float cost=dis[p];
51
+ if(!s.tank[i].alive)cost+=respawn_in(s,i);if(ctx.attack[i][p]<2.5f&&s.tank[i].level>=1)cost+=16;if(i%3==FAST)cost+=20;
52
+ if(cost<bestcost){bestcost=cost;besti=i;std::copy(dis,dis+CELLS,ctx.defend[i]);}
53
+ }if(besti>=0&&bestcost<20){ctx.goal[besti]=-2;ctx.threat[besti]=threat;}
54
+ }
55
+ }
56
+ for(int seat=0;seat<2;seat++){std::fill(ctx.wall[seat],ctx.wall[seat]+CELLS,0);for(int i=3*seat;i<3*seat+3;i++){int p=s.tank[i].alive?s.tank[i].pos:s.spawn[i];const float*dis=ctx.goal[i]==-2?ctx.defend[i]:(ctx.goal[i]<0?ctx.attack[i]:ctx.supply[i][ctx.goal[i]]);for(int d=0;d<4;d++){int n=p+DIR_DELTA[d];if(s.grid[n]==BRICK||(s.grid[n]==STEEL&&s.tank[i].level>=2))ctx.wall[seat][n]=std::max(ctx.wall[seat][n],std::max(0.f,dis[p]-dis[n])*.85f);}int base=ctx.goal[i]==-2?s.tank[ctx.threat[i]].pos:s.base_pos[1-seat];if(cell_x(p)==cell_x(base)||cell_y(p)==cell_y(base)){int delta=cell_x(p)==cell_x(base)?(base>p?STRIDE:-STRIDE):(base>p?1:-1);for(int c=p+delta;c!=base;c+=delta){if(s.grid[c]==OUTSIDE||(s.grid[c]==STEEL&&s.tank[i].level<2))break;if(s.grid[c]==BRICK||s.grid[c]==STEEL)ctx.wall[seat][c]=std::max(ctx.wall[seat][c],ctx.goal[i]==-2?12.f:4.5f);}}}}}
57
+ float position(const State&s,int i){const auto&t=s.tank[i];if(!t.alive)return -3.f*respawn_in(s,i);float v=18-1.35f*ctx.attack[i][t.pos];int k=ctx.goal[i];if(k==-2)v=60-4.2f*ctx.defend[i][t.pos];if(k>=0&&t.level<2&&t.level==ctx.root->tank[i].level)v=(t.level==0?30.f:24.f)-2.4f*ctx.supply[i][k][t.pos];v=std::max(-35.f,v);if(t.pos!=ctx.root->tank[i].pos)v-=std::min(2.f,visits[i][t.pos]*.12f);for(int j=3*(1-i/3);j<3*(1-i/3)+3;j++)if(!s.tank[j].alive&&s.spawn[j]==t.pos&&respawn_in(s,j)<=2)v-=280;return v;}
58
+ float evaluation(const State&s,int me){if(s.done)return s.winner<0?0:(s.winner==me?100000.f:-100000.f);float vals[2]={};for(int seat=0;seat<2;seat++){vals[seat]=70.f*s.base_hp[seat]+10.f*s.kills[seat]+1.5f*s.stars_eaten[seat];for(int i=3*seat;i<3*seat+3;i++){const Tank&t=s.tank[i];if(t.alive)vals[seat]+=35+48.f*t.level+18.f*t.hp+position(s,i);else vals[seat]+=position(s,i);}}
59
+ int occ[CELLS];std::fill(occ,occ+CELLS,-1);for(int i=0;i<6;i++)if(s.tank[i].alive)occ[s.tank[i].pos]=i;for(int b=0;b<s.n_bullets;b++){const auto&bl=s.bullet[b];int seat=bl.owner/3;for(int c=bl.pos+DIR_DELTA[bl.dir],r=1;r<=s.P.size+1;c+=DIR_DELTA[bl.dir],r++){int g=s.grid[c];if(g==OUTSIDE)break;if(g==BRICK||g==STEEL){if(g==BRICK||bl.steel)vals[seat]+=ctx.wall[seat][c]/(1+.12f*r);break;}if(g==BASE0||g==BASE1){int target=g-BASE0;vals[target]-=30.f/(1+.18f*r);break;}int j=occ[c];if(j>=0){float danger=(s.tank[j].hp<=1?25.f:12.f)*std::pow(.55f,float(r-1));int exits=0;for(int d=0;d<4;d++)if((d/2)!=(bl.dir/2)&&s.grid[c+DIR_DELTA[d]]==EMPTY&&occ[c+DIR_DELTA[d]]<0)exits++;if(exits==0)danger=std::max(danger,(s.tank[j].hp<=1?20.f:10.f)/(1+.25f*r));vals[j/3]-=danger;break;}}}
60
+ if(s.wall_hash!=ctx.root->wall_hash)for(int c=0;c<CELLS;c++)if((ctx.root->grid[c]==BRICK||ctx.root->grid[c]==STEEL)&&s.grid[c]==EMPTY)for(int seat=0;seat<2;seat++)vals[seat]+=ctx.wall[seat][c];return vals[me]-vals[1-me];}
61
+ struct Choice{Orders o;float value;};
62
+ std::vector<Choice>choices(const State&s,int seat,int limit,const Orders&other){std::vector<std::pair<float,Cmd>>ls[3];for(int t=0;t<3;t++){Cmd legal[N_CMDS];int n=legal_commands(s,3*seat+t,legal);for(int j=0;j<n;j++){Orders o;o.cmd[t]=legal[j];State z=s;step_as(z,seat,o,other);ls[t].push_back({evaluation(z,seat),legal[j]});}std::stable_sort(ls[t].begin(),ls[t].end(),[](auto a,auto b){return a.first>b.first;});if(ls[t].size()>7)ls[t].resize(7);}std::vector<Choice>out;for(auto a:ls[0])for(auto b:ls[1])for(auto c:ls[2]){Orders o;o.cmd[0]=a.second;o.cmd[1]=b.second;o.cmd[2]=c.second;State z=s;step_as(z,seat,o,other);out.push_back({o,evaluation(z,seat)});}std::stable_sort(out.begin(),out.end(),[](const Choice&a,const Choice&b){return a.value>b.value;});if(int(out.size())>limit)out.resize(limit);return out;}
63
+ Orders greedy(const State&s,int seat,const Orders&other){Orders o;for(int pass=0;pass<2;pass++)for(int t=0;t<3;t++){Cmd legal[N_CMDS];int n=legal_commands(s,3*seat+t,legal);float best=-1e9;Cmd bc=0;for(int k=0;k<n;k++){Orders a=o;a.cmd[t]=legal[k];State z=s;step_as(z,seat,a,other);float v=evaluation(z,seat);if(v>best){best=v;bc=legal[k];}}o.cmd[t]=bc;}return o;}
64
+ }
65
+ void init(const State&,int){std::fill(priorGoal,priorGoal+6,-1);for(auto&row:visits)std::fill(row,row+CELLS,0);}
66
+ Orders decide(const State&s,int me){prepare(s);float sophistication=std::clamp((s.stars_eaten[1-me]-1)*.5f,0.f,1.f); float rootRisk=.75f-.45f*sophistication, continuationRisk=.8f-.4f*sophistication;Orders idle;auto enemy=choices(s,1-me,24,idle);auto seeds=enemy;
67
+ for(int b=0;b<std::min(2,int(seeds.size()));b++)for(int t=0;t<3;t++) {
68
+ Cmd legal[N_CMDS];int n=legal_commands(s,3*(1-me)+t,legal);
69
+ for(int k=0;k<n;k++){Orders o=seeds[b].o;o.cmd[t]=legal[k];
70
+ if(std::none_of(enemy.begin(),enemy.end(),[&](const Choice&z){return z.o==o;}))enemy.push_back({o,0});
71
+ }
72
+ }
73
+ auto mine=choices(s,me,60,enemy[0].o);auto quiet=choices(s,me,10,idle);for(auto a:quiet)if(std::none_of(mine.begin(),mine.end(),[&](const Choice&b){return a.o==b.o;}))mine.push_back(a);struct Ranked{int index;float value;int response;};std::vector<Ranked>rank;
74
+ int n=mine.size(),m=enemy.size();std::vector<float> matrix(n*m);
75
+ for(int a=0;a<n;a++)for(int b=0;b<m;b++){State z=s;step_as(z,me,mine[a].o,enemy[b].o);matrix[a*m+b]=evaluation(z,me);}
76
+ std::vector<float> r1(n),r2(m),p1(n),p2(m),avg2(m),u1(n),u2(m);
77
+ for(int iter=0;iter<80;iter++) {
78
+ float sum1=0,sum2=0;for(int a=0;a<n;a++)sum1+=std::max(0.f,r1[a]);for(int b=0;b<m;b++)sum2+=std::max(0.f,r2[b]);
79
+ for(int a=0;a<n;a++)p1[a]=sum1>1e-5?std::max(0.f,r1[a])/sum1:1.f/n;
80
+ for(int b=0;b<m;b++){p2[b]=sum2>1e-5?std::max(0.f,r2[b])/sum2:1.f/m;avg2[b]+=p2[b];}
81
+ std::fill(u1.begin(),u1.end(),0);std::fill(u2.begin(),u2.end(),0);float v=0;
82
+ for(int a=0;a<n;a++)for(int b=0;b<m;b++){float x=matrix[a*m+b];u1[a]+=p2[b]*x;u2[b]-=p1[a]*x;}
83
+ for(int a=0;a<n;a++)v+=p1[a]*u1[a];for(int a=0;a<n;a++)r1[a]+=u1[a]-v;for(int b=0;b<m;b++)r2[b]+=u2[b]+v;
84
+ }
85
+ for(int a=0;a<n;a++){float worst=1e9,sum=0;int wi=0;for(int b=0;b<m;b++){float v=matrix[a*m+b];if(v<worst){worst=v;wi=b;}sum+=v*avg2[b]/80;}
86
+ rank.push_back({a,rootRisk*worst+(1-rootRisk)*sum,wi});}
87
+ std::stable_sort(rank.begin(),rank.end(),[](auto a,auto b){return a.value>b.value;});Orders result=mine[rank[0].index].o;float best=-1e9;for(int k=0;k<std::min(8,int(rank.size()));k++){if(budget_left()<12)break;auto r=rank[k];float worst=1e9,sum=0;int responses[3]={r.response,0,int(enemy.size())>1?1:0};for(int b:responses){if(budget_left()<6)return result;State z=s;step_as(z,me,mine[r.index].o,enemy[b].o);float v=evaluation(z,me);if(!z.done){Orders eo=greedy(z,1-me,idle),mo=greedy(z,me,eo);eo=greedy(z,1-me,mo);mo=greedy(z,me,eo);State q=z;step_as(q,me,mo,eo);v=.35f*v+.65f*evaluation(q,me);}worst=std::min(worst,v);sum+=v;}float value=.8f*(continuationRisk*worst+(1-continuationRisk)*sum/3)+.2f*r.value;if(value>best){best=value;result=mine[r.index].o;}}for(int i=0;i<6;i++){priorGoal[i]=ctx.goal[i];if(s.tank[i].alive)visits[i][s.tank[i].pos]++;}return result;}
@@ -0,0 +1,476 @@
1
+ // 策略:每辆车逐条枚举合法命令,用 tank::step 精确前推一回合(对手取三种模型:不动 / 见线就打 /
2
+ // 预判我落点开炮),再做第二层"是否存在安全应对"的检查;局面评估 = 基地血量差 + 坦克得失 + 到目标的距离场。
3
+ // 目标:攻击手走到离敌方基地 2 格内的炮位;敌人逼近我方基地时最近的车转为猎杀。
4
+ #include "tank.hpp"
5
+ #include <queue>
6
+ #include <vector>
7
+ #include <cstring>
8
+ #include <algorithm>
9
+
10
+ using namespace tank;
11
+
12
+ #define FIXED_PASSES 0
13
+
14
+ static const int INF = 100000;
15
+ static int ME, OP;
16
+ static int atkField[2][CELLS]; // atkField[seat]:seat 方去打对方基地的代价场
17
+ static int huntField[3][CELLS]; // 针对敌方第 k 辆车的猎杀场
18
+ static int starField[CELLS];
19
+ static int goalOf[3]; // 0 = 攻击,1..3 = 猎杀敌车 k-1,4 = 吃星
20
+ static const int* goalField[3];
21
+
22
+ // 调参
23
+ static int FAR_ATK = 1000, FAR_K = 5, HUNT_KEEP = 4;
24
+ static double L2 = 0.5;
25
+ static int PREP_T1 = 10, PREP_T = 4, PREP_LIM1 = 9, PREP_LIM = 5, PREP_N1 = 3, PREP_N = 3;
26
+ static int BRICK_K = 2;
27
+ static int W_HUNT = 14, W_HUNT_HOT = 40;
28
+ static int W_STEP = 10, V_BASE = 100, V_MYBASE = 110, V_DEATH = 400, V_UNSAFE = 150, V_KILL = 20, V_KILLY = 12;
29
+ static int V_HP = 40, V_LEVEL = 120, B_BRICK = 6, B_FAR = 5, B_FARSTILL = 15, B_BASEFAR = 8;
30
+ static double M_STAY = 0.55, M_THREAT = 0.45, M_ANT = 0.0;
31
+
32
+ static int danger[CELLS]; // 敌车视线内的格子,寻路时加代价
33
+ static bool useDanger = false;
34
+ static int wStep[3] = {10, 10, 10};
35
+ static int DANGER_NEAR = 0, DANGER_FAR = 0;
36
+ static uint8_t lane[CELLS]; // 敌方炮线(打我基地的线、飞行中的敌弹前方)
37
+ static void dijkstra(const State& s, int* f, bool useLane = false) {
38
+ typedef std::pair<int, int> PI;
39
+ std::priority_queue<PI, std::vector<PI>, std::greater<PI>> pq;
40
+ for (int c = 0; c < CELLS; ++c) if (f[c] < INF) pq.push({f[c], c});
41
+ while (!pq.empty()) {
42
+ PI p = pq.top(); pq.pop();
43
+ if (p.first > f[p.second]) continue;
44
+ for (int d = 0; d < 4; ++d) {
45
+ int n = p.second + DIR_DELTA[d];
46
+ uint8_t g = s.grid[n];
47
+ int w;
48
+ if (g == EMPTY) w = 1; else if (g == BRICK) w = 3; else continue;
49
+ if (useLane && lane[n]) w += 8;
50
+ if (useDanger) w += danger[n];
51
+ if (f[n] > p.first + w) { f[n] = p.first + w; pq.push({f[n], n}); }
52
+ }
53
+ }
54
+ }
55
+
56
+ static bool buildAtk(const State& s, int seat, int* f, int only = -1) {
57
+ for (int c = 0; c < CELLS; ++c) f[c] = INF;
58
+ int B = s.base_pos[1 - seat];
59
+ bool any = false;
60
+ for (int d = 0; d < 4; ++d) {
61
+ if (only >= 0 && d != only) continue;
62
+ int bricks = 0;
63
+ for (int k = 1; k <= 9; ++k) {
64
+ int c = B + k * DIR_DELTA[d];
65
+ uint8_t g = s.grid[c];
66
+ int far = k > 2 ? (k - 2) * 2 : 0;
67
+ if (g == EMPTY) { f[c] = std::min(f[c], 2 * bricks + far); any = true; }
68
+ else if (g == BRICK) { f[c] = std::min(f[c], 2 * bricks + far + 2); ++bricks; any = true; }
69
+ else if (g == WATER) continue;
70
+ else break;
71
+ }
72
+ }
73
+ useDanger = only >= 0;
74
+ dijkstra(s, f);
75
+ useDanger = false;
76
+ return any;
77
+ }
78
+ static int sideField[3][CELLS];
79
+ static bool sideOk[3];
80
+ static int sideOf[3] = {-1, -1, -1};
81
+ static int lastBest[3] = {INF, INF, INF}, stuckCnt[3];
82
+ static int sidePen[3][3], sidePenUntil[3][3];
83
+
84
+ static void buildHunt(const State& s, int e, int* f) {
85
+ for (int c = 0; c < CELLS; ++c) f[c] = INF;
86
+ int P = s.tank[e].pos;
87
+ for (int d = 0; d < 4; ++d) {
88
+ for (int k = 1; k <= 7; ++k) {
89
+ int c = P + k * DIR_DELTA[d];
90
+ uint8_t g = s.grid[c];
91
+ if (g == EMPTY) { if (!lane[c]) f[c] = (k == 1 ? 2 : (k == 2 ? 0 : (k - 2))); }
92
+ else if (g == WATER) continue;
93
+ else break;
94
+ }
95
+ }
96
+ dijkstra(s, f, true);
97
+ }
98
+
99
+ static void buildLane(const State& s) {
100
+ memset(lane, 0, sizeof lane);
101
+ memset(danger, 0, sizeof danger);
102
+ for (int t = 0; t < 3; ++t) {
103
+ int e = tank_id(OP, t);
104
+ if (!s.tank[e].alive) continue;
105
+ for (int d = 0; d < 4; ++d)
106
+ for (int k = 1; k <= 5; ++k) {
107
+ int c = s.tank[e].pos + k * DIR_DELTA[d];
108
+ if (s.grid[c] != EMPTY && s.grid[c] != WATER) break;
109
+ danger[c] += k <= 2 ? DANGER_NEAR : DANGER_FAR;
110
+ }
111
+ }
112
+ for (int t = 0; t < 3; ++t) {
113
+ int e = tank_id(OP, t);
114
+ if (!s.tank[e].alive) continue;
115
+ for (int d = 0; d < 4; ++d) {
116
+ int k = 1; bool hit = false;
117
+ for (; k <= 14; ++k) {
118
+ uint8_t g = s.grid[s.tank[e].pos + k * DIR_DELTA[d]];
119
+ if (g == BASE0 + ME) { hit = true; break; }
120
+ if (g != EMPTY && g != WATER && g != BRICK) break;
121
+ }
122
+ if (hit) for (int j = 1; j < k; ++j) lane[s.tank[e].pos + j * DIR_DELTA[d]] = 1;
123
+ }
124
+ }
125
+ for (int b = 0; b < s.n_bullets; ++b) {
126
+ if (seat_of(s.bullet[b].owner) == ME) continue;
127
+ for (int k = 1; k <= 6; ++k) {
128
+ int c = s.bullet[b].pos + k * DIR_DELTA[s.bullet[b].dir];
129
+ if (s.grid[c] != EMPTY && s.grid[c] != WATER) break;
130
+ lane[c] = 1;
131
+ }
132
+ }
133
+ }
134
+
135
+ // 敌车 e(任意一方的车)"见线就打"的命令:打 foe 方的车或基地
136
+ static Cmd threatCmd(const State& s, int e) {
137
+ const Tank& t = s.tank[e];
138
+ if (!t.alive || !can_fire(s, e)) return STAY;
139
+ int side = seat_of(e);
140
+ int best = -1, bestP = 0;
141
+ for (int d = 0; d < 4; ++d) {
142
+ int bricks = 0;
143
+ for (int k = 1; k <= 14; ++k) {
144
+ int c = t.pos + k * DIR_DELTA[d];
145
+ uint8_t g = s.grid[c];
146
+ if (g == WATER) continue;
147
+ if (g == BRICK) { ++bricks; continue; }
148
+ if (g == BASE0 + (1 - side)) { int p = 20 - bricks; if (p > bestP) { bestP = p; best = d; } break; }
149
+ if (g != EMPTY) break;
150
+ int v = tank_at(s, c);
151
+ if (v >= 0) {
152
+ if (seat_of(v) != side && bricks == 0 && k <= 8) { int p = 100 - k; if (p > bestP) { bestP = p; best = d; } }
153
+ break;
154
+ }
155
+ }
156
+ }
157
+ return best < 0 ? STAY : cmd_fire(best);
158
+ }
159
+
160
+ static Orders threatOrders(const State& s, int side) {
161
+ Orders o;
162
+ for (int t = 0; t < 3; ++t) o.cmd[t] = threatCmd(s, tank_id(side, t));
163
+ return o;
164
+ }
165
+
166
+ static int destOf(const State& s, int i, Cmd c) {
167
+ int p = s.tank[i].pos;
168
+ auto go = [&](int d) { int n = p + DIR_DELTA[d]; if (s.grid[n] == EMPTY && tank_at(s, n) < 0) p = n; };
169
+ if (is_move(c)) go(c - 1);
170
+ else if (is_move2(c)) { go((c - 9) / 4); go((c - 9) % 4); }
171
+ return p;
172
+ }
173
+
174
+ // 预判模型:能开炮的敌车朝我车 i 的落点(2 格内同线)开炮
175
+ static Orders anticipateOrders(const State& s, int side, int i, int dest) {
176
+ Orders o = threatOrders(s, side);
177
+ for (int t = 0; t < 3; ++t) {
178
+ int e = tank_id(side, t);
179
+ if (!s.tank[e].alive || !can_fire(s, e)) continue;
180
+ for (int d = 0; d < 4; ++d) {
181
+ for (int k = 1; k <= 2; ++k) {
182
+ int c = s.tank[e].pos + k * DIR_DELTA[d];
183
+ uint8_t g = s.grid[c];
184
+ if (g != EMPTY && g != WATER) break;
185
+ if (c == dest) { o.cmd[t] = cmd_fire(d); break; }
186
+ int v = tank_at(s, c);
187
+ if (v >= 0 && v != i) break;
188
+ }
189
+ }
190
+ }
191
+ return o;
192
+ }
193
+
194
+ static double evalState(const State& s0, const State& s) {
195
+ double v = 0;
196
+ if (s.done) {
197
+ if (s.winner == ME) v += 100000; else if (s.winner == OP) v -= 100000;
198
+ }
199
+ v += V_BASE * (s0.base_hp[OP] - s.base_hp[OP]);
200
+ v -= V_MYBASE * (s0.base_hp[ME] - s.base_hp[ME]);
201
+ for (int t = 0; t < 3; ++t) {
202
+ int i = tank_id(ME, t), e = tank_id(OP, t);
203
+ const Tank& a0 = s0.tank[i]; const Tank& a = s.tank[i];
204
+ if (a0.alive) {
205
+ if (!a.alive) v -= V_DEATH + 60 * a0.level;
206
+ else {
207
+ int dh = a.hp - 2 * (a.level - a0.level) - a0.hp;
208
+ v += V_HP * dh;
209
+ v += V_LEVEL * (a.level - a0.level);
210
+ int f = goalField[t][a.pos];
211
+ if (f >= INF) f = 60;
212
+ v -= wStep[t] * f;
213
+ }
214
+ }
215
+ const Tank& b0 = s0.tank[e]; const Tank& b = s.tank[e];
216
+ if (b0.alive) {
217
+ if (!b.alive) {
218
+ int dy = std::abs(cell_y(b0.pos) - cell_y(s.spawn[e]));
219
+ v += V_KILL + V_KILLY * dy + 60 * b0.level;
220
+ } else {
221
+ int dh = b.hp - 2 * (b.level - b0.level) - b0.hp;
222
+ v -= V_HP * dh;
223
+ v -= V_LEVEL * (b.level - b0.level);
224
+ }
225
+ }
226
+ }
227
+ return v;
228
+ }
229
+
230
+ // 命令本身的启发加分(前推一回合看不到的远期收益)
231
+ static double cmdBonus(const State& s, int i, Cmd c, const int* field) {
232
+ if (!is_fire(c)) return 0;
233
+ int d = c - 5;
234
+ const Tank& t = s.tank[i];
235
+ for (int k = 1; k <= 14; ++k) {
236
+ int cc = t.pos + k * DIR_DELTA[d];
237
+ uint8_t g = s.grid[cc];
238
+ if (g == WATER) continue;
239
+ if (g == BRICK) {
240
+ if (k <= BRICK_K && field[cc] <= field[t.pos] && field[cc] < INF) return B_BRICK;
241
+ // 炮线后面是敌方基地
242
+ for (int kk = k + 1; kk <= 14; ++kk) {
243
+ uint8_t g2 = s.grid[t.pos + kk * DIR_DELTA[d]];
244
+ if (g2 == BASE0 + OP) return B_BRICK;
245
+ if (g2 != EMPTY && g2 != WATER && g2 != BRICK) break;
246
+ }
247
+ return -3;
248
+ }
249
+ if (g == BASE0 + OP) return k > 2 ? B_BASEFAR : 0;
250
+ if (g == BASE0 + ME) return -400;
251
+ if (g != EMPTY) return -3;
252
+ int v = tank_at(s, cc);
253
+ if (v >= 0) {
254
+ if (seat_of(v) == ME) return -300;
255
+ if (k <= 2) return 0;
256
+ if (field[t.pos] <= FAR_ATK && k > FAR_K) return -3;
257
+ bool still = s.tank[v].last_fire >= s.turn - 1;
258
+ return (still ? B_FARSTILL : B_FAR) - k;
259
+ }
260
+ }
261
+ return -3;
262
+ }
263
+
264
+ static bool safeNext(const State& s1, int i) {
265
+ const Tank& t = s1.tank[i];
266
+ if (!t.alive) return true;
267
+ Orders eo = threatOrders(s1, OP);
268
+ Cmd list[N_CMDS];
269
+ int n = legal_commands(s1, i, list);
270
+ for (int k = 0; k < n; ++k) {
271
+ State s2 = s1;
272
+ Orders mo; mo.cmd[type_of(i)] = list[k];
273
+ step_as(s2, ME, mo, eo);
274
+ if (s2.tank[i].alive && s2.tank[i].hp >= t.hp) return true;
275
+ if (s2.done && s2.winner == ME) return true;
276
+ }
277
+ return false;
278
+ }
279
+
280
+ void init(const State& s, int me) { ME = me; OP = 1 - me; }
281
+
282
+ Orders decide(const State& s, int me) {
283
+ ME = me; OP = 1 - me;
284
+ Orders orders;
285
+ buildAtk(s, ME, atkField[ME]);
286
+ buildAtk(s, OP, atkField[OP]);
287
+ buildLane(s);
288
+
289
+ // 吃星场
290
+ bool anyStar = false;
291
+ for (int c = 0; c < CELLS; ++c) starField[c] = INF;
292
+ for (int k = 0; k < s.n_supply; ++k) if (s.star_mask >> k & 1) { starField[s.supply[k]] = 0; anyStar = true; }
293
+ if (anyStar) dijkstra(s, starField);
294
+
295
+ // 三个方向的炮位分配:尽量一车一个方向,避免挤在同一条路上互相挡
296
+ {
297
+ const int sd[3] = {ME == 0 ? DOWN : UP, LEFT, RIGHT};
298
+ for (int k = 0; k < 3; ++k) sideOk[k] = buildAtk(s, ME, sideField[k], sd[k]);
299
+ for (int t = 0; t < 3; ++t) {
300
+ int i = tank_id(ME, t);
301
+ if (!s.tank[i].alive) { stuckCnt[t] = 0; lastBest[t] = INF; continue; }
302
+ if (sideOf[t] >= 0) {
303
+ int f = sideField[sideOf[t]][s.tank[i].pos];
304
+ if (f < lastBest[t]) { lastBest[t] = f; stuckCnt[t] = 0; }
305
+ else if (f > 0 && ++stuckCnt[t] >= 8) {
306
+ sidePen[t][sideOf[t]] = 25; sidePenUntil[t][sideOf[t]] = s.turn + 30;
307
+ stuckCnt[t] = 0; lastBest[t] = INF;
308
+ }
309
+ }
310
+ }
311
+ int bestTot = INF, bestA[3] = {0, 0, 0};
312
+ for (int a = 0; a < 27; ++a) {
313
+ int as[3] = {a % 3, a / 3 % 3, a / 9};
314
+ int tot = 0, cnt[3] = {0, 0, 0};
315
+ for (int t = 0; t < 3; ++t) {
316
+ int i = tank_id(ME, t);
317
+ int k = as[t];
318
+ if (!sideOk[k]) { tot += 1000; continue; }
319
+ cnt[k]++;
320
+ int pos = s.tank[i].alive ? s.tank[i].pos : s.spawn[i];
321
+ int f = sideField[k][pos]; if (f >= INF) f = 200;
322
+ if (t == FAST) f = (f + 1) / 2;
323
+ if (s.turn < sidePenUntil[t][k]) f += sidePen[t][k];
324
+ if (k != sideOf[t]) f += 3;
325
+ tot += f;
326
+ }
327
+ for (int k = 0; k < 3; ++k) if (cnt[k] > 1) tot += 12 * (cnt[k] - 1);
328
+ if (tot < bestTot) { bestTot = tot; for (int t = 0; t < 3; ++t) bestA[t] = as[t]; }
329
+ }
330
+ for (int t = 0; t < 3; ++t) {
331
+ if (bestA[t] != sideOf[t]) { sideOf[t] = bestA[t]; stuckCnt[t] = 0; lastBest[t] = INF; }
332
+ goalOf[t] = 0;
333
+ goalField[t] = sideOk[sideOf[t]] ? sideField[sideOf[t]] : atkField[ME];
334
+ }
335
+ }
336
+
337
+ // 猎杀分配
338
+ for (int t = 0; t < 3; ++t) wStep[t] = W_STEP;
339
+ bool huntBuilt[3] = {false, false, false};
340
+ int order[3] = {0, 1, 2};
341
+ std::sort(order, order + 3, [&](int a, int b) {
342
+ int ea = tank_id(OP, a), eb = tank_id(OP, b);
343
+ int fa = s.tank[ea].alive ? atkField[OP][s.tank[ea].pos] : INF;
344
+ int fb = s.tank[eb].alive ? atkField[OP][s.tank[eb].pos] : INF;
345
+ return fa < fb;
346
+ });
347
+ bool used[3] = {false, false, false};
348
+ int nHunt = 0;
349
+ for (int oi = 0; oi < 3; ++oi) {
350
+ int k = order[oi], e = tank_id(OP, k);
351
+ if (!s.tank[e].alive) continue;
352
+ int thr = atkField[OP][s.tank[e].pos];
353
+ if (thr > 7) continue;
354
+ buildHunt(s, e, huntField[k]); huntBuilt[k] = true;
355
+ int best = -1, bd = INF;
356
+ for (int t = 0; t < 3; ++t) {
357
+ int i = tank_id(ME, t);
358
+ if (used[t] || !s.tank[i].alive) continue;
359
+ int dd = huntField[k][s.tank[i].pos];
360
+ if (t == FAST) dd = (dd + 1) / 2;
361
+ if (goalField[t][s.tank[i].pos] <= HUNT_KEEP && dd > 2) continue;
362
+ if (dd < bd) { bd = dd; best = t; }
363
+ }
364
+ if (best >= 0 && bd <= 14 && nHunt < (s.base_hp[ME] > s.base_hp[OP] + 6 ? 0 : 1)) { ++nHunt; used[best] = true; goalOf[best] = 1 + k; goalField[best] = huntField[k]; wStep[best] = thr <= 1 ? W_HUNT_HOT : W_HUNT; }
365
+ }
366
+ // 出星前抢占补给点(每批出星前 PREP_T 回合起)
367
+ {
368
+ int W = s.turn < 10 ? 10 : 10 + ((s.turn - 10 + 19) / 20) * 20;
369
+ bool first = W == 10;
370
+ if (W - s.turn <= (first ? PREP_T1 : PREP_T) && W <= 290) {
371
+ static int spf[MAX_SUPPLY][CELLS];
372
+ for (int k = 0; k < s.n_supply; ++k) {
373
+ for (int c = 0; c < CELLS; ++c) spf[k][c] = INF;
374
+ spf[k][s.supply[k]] = 0;
375
+ dijkstra(s, spf[k]);
376
+ }
377
+ int lim = first ? PREP_LIM1 : PREP_LIM;
378
+ int maxN = first ? PREP_N1 : PREP_N;
379
+ int bestTot = INF, bestA[3] = {-1, -1, -1};
380
+ // 每辆车选一个补给点或不去(-1)
381
+ for (int a = 0; a < 64; ++a) {
382
+ int as[3] = {a % 4 - 1, a / 4 % 4 - 1, a / 16 % 4 - 1};
383
+ int tot = 0, cnt = 0; bool ok = true;
384
+ for (int t = 0; t < 3 && ok; ++t) {
385
+ int k = as[t];
386
+ if (k < 0) { tot += lim + 1; continue; }
387
+ int i = tank_id(ME, t);
388
+ if (k >= s.n_supply || !s.tank[i].alive || goalOf[t] != 0 || s.tank[i].level >= 2) { ok = false; break; }
389
+ for (int u = 0; u < t; ++u) if (as[u] == k) ok = false;
390
+ int d = spf[k][s.tank[i].pos];
391
+ if (t == FAST) d = (d + 1) / 2;
392
+ if (d > lim) ok = false;
393
+ tot += d; ++cnt;
394
+ }
395
+ if (!ok || cnt > maxN) continue;
396
+ if (tot < bestTot) { bestTot = tot; for (int t = 0; t < 3; ++t) bestA[t] = as[t]; }
397
+ }
398
+ static int prepF[3][CELLS];
399
+ for (int t = 0; t < 3; ++t) if (bestA[t] >= 0) {
400
+ memcpy(prepF[t], spf[bestA[t]], sizeof prepF[t]);
401
+ goalOf[t] = 4; goalField[t] = prepF[t];
402
+ }
403
+ }
404
+ }
405
+ // 吃星:星近就顺路吃
406
+ if (anyStar) {
407
+ for (int t = 0; t < 3; ++t) {
408
+ int i = tank_id(ME, t);
409
+ if (!s.tank[i].alive || goalOf[t] != 0 || s.tank[i].level >= 2) continue;
410
+ int sd = starField[s.tank[i].pos];
411
+ int lim = s.tank[i].level == 0 ? 5 : 3;
412
+ if (sd <= lim && goalField[t][s.tank[i].pos] > 0) { goalOf[t] = 4; goalField[t] = starField; }
413
+ }
414
+ }
415
+
416
+ const int seq[3] = {FIRE, ARMOR, FAST};
417
+ int passes = 0;
418
+ for (int pass = 0; pass < 2; ++pass) {
419
+ if (pass > 0) {
420
+ if (FIXED_PASSES ? pass >= FIXED_PASSES : budget_left() < 25) break;
421
+ }
422
+ ++passes;
423
+ bool changed = false;
424
+ for (int si = 0; si < 3; ++si) {
425
+ int t = seq[si], i = tank_id(ME, t);
426
+ if (!s.tank[i].alive) continue;
427
+ Cmd list[N_CMDS];
428
+ int n = legal_commands(s, i, list);
429
+ bool cf = can_fire(s, i);
430
+ Orders eStay, eThr = threatOrders(s, OP);
431
+ double bestV = -1e18; Cmd bestC = STAY;
432
+ for (int k = 0; k < n; ++k) {
433
+ Cmd c = list[k];
434
+ if (is_fire(c) && !cf) continue;
435
+ Orders mo = orders; mo.cmd[t] = c;
436
+ double v = cmdBonus(s, i, c, goalField[t]);
437
+ Orders eAnt = anticipateOrders(s, OP, i, destOf(s, i, c));
438
+ const Orders* models[3] = {&eStay, &eThr, &eAnt};
439
+ const double wts[3] = {M_STAY, M_THREAT, M_ANT};
440
+ for (int m = 0; m < 3; ++m) {
441
+ State s1 = s;
442
+ step_as(s1, ME, mo, *models[m]);
443
+ double ev = evalState(s, s1);
444
+ if (wts[m] <= 0) continue;
445
+ if (!s1.done && s1.tank[i].alive && L2 > 0) {
446
+ Cmd l2[N_CMDS];
447
+ int n2 = legal_commands(s1, i, l2);
448
+ bool cf2 = can_fire(s1, i);
449
+ Orders e2; if (m >= 1) e2 = threatOrders(s1, OP);
450
+ double b2 = -1e18;
451
+ for (int k2 = 0; k2 < n2; ++k2) {
452
+ if (is_fire(l2[k2]) && !cf2) continue;
453
+ State s2 = s1;
454
+ Orders m2; m2.cmd[t] = l2[k2];
455
+ step_as(s2, ME, m2, e2);
456
+ double e = evalState(s, s2) + cmdBonus(s1, i, l2[k2], goalField[t]);
457
+ if (e > b2) b2 = e;
458
+ }
459
+ ev = (1 - L2) * ev + L2 * b2;
460
+ }
461
+ if (!s1.done && s1.tank[i].alive && !safeNext(s1, i)) ev -= V_UNSAFE;
462
+ v += wts[m] * ev;
463
+ }
464
+ if (v > bestV) { bestV = v; bestC = c; }
465
+ }
466
+ if (bestC != orders.cmd[t]) changed = true;
467
+ orders.cmd[t] = bestC;
468
+ }
469
+ if (!changed) break;
470
+ }
471
+ #ifdef DBG
472
+ for (int t = 0; t < 3; ++t) { int i = tank_id(ME, t); fprintf(stderr, "t%d goal%d side%d f%d w%d | ", t, goalOf[t], sideOf[t], s.tank[i].alive ? goalField[t][s.tank[i].pos] : -1, wStep[t]); }
473
+ fputc(10, stderr);
474
+ #endif
475
+ return orders;
476
+ }