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,634 @@
1
+ // 多阵营夺旗 bot
2
+ // 思路:全图预计算最短路;每回合预测敌人下一步分布;用带危险权重的 Dijkstra 规划路线;
3
+ // 枚举 3 个角色的任务组合(取旗/回家/截杀携旗者/护送/猎杀/巡游)取总价值最大;
4
+ // 最后按预测命中概率选攻击目标并集火。
5
+ #include <bits/stdc++.h>
6
+ using namespace std;
7
+
8
+ struct Unit { int id, team, x, y, hp, flag, respawn_at; };
9
+ struct Flag { int id; string status; int x, y, holder, return_at; };
10
+ struct Event { string type; vector<int> args; };
11
+
12
+ struct Game {
13
+ int teams = 0, me = 0, size = 0, turns = 0, hp = 0, damage = 0, respawn = 0, flag_return = 0, flag_cooldown = 0;
14
+ vector<string> map;
15
+ vector<pair<int, int>> bases;
16
+ vector<pair<int, int>> spots;
17
+ int turn = 0;
18
+ vector<int> score;
19
+ vector<Unit> units;
20
+ vector<Flag> flags;
21
+ vector<Event> events;
22
+ };
23
+
24
+ bool read_turn(Game& g) {
25
+ string w;
26
+ while (cin >> w) {
27
+ if (w == "INIT") {
28
+ cin >> g.teams >> g.me >> g.size >> g.turns >> g.hp >> g.damage >> g.respawn >> g.flag_return >> g.flag_cooldown;
29
+ } else if (w == "MAP") {
30
+ g.map.assign(g.size, "");
31
+ for (auto& row : g.map) cin >> row;
32
+ } else if (w == "BASES") {
33
+ int k; cin >> k; g.bases.resize(k);
34
+ for (auto& b : g.bases) cin >> b.first >> b.second;
35
+ } else if (w == "SPOTS") {
36
+ int k; cin >> k; g.spots.resize(k);
37
+ for (auto& s : g.spots) cin >> s.first >> s.second;
38
+ } else if (w == "TURN") {
39
+ cin >> g.turn;
40
+ } else if (w == "SCORE") {
41
+ g.score.resize(g.teams);
42
+ for (auto& s : g.score) cin >> s;
43
+ } else if (w == "UNITS") {
44
+ int k; cin >> k; g.units.resize(k);
45
+ for (auto& u : g.units) { string tag; cin >> tag >> u.id >> u.team >> u.x >> u.y >> u.hp >> u.flag >> u.respawn_at; }
46
+ } else if (w == "FLAGS") {
47
+ int k; cin >> k; g.flags.resize(k);
48
+ for (auto& f : g.flags) { string tag; cin >> tag >> f.id >> f.status >> f.x >> f.y >> f.holder >> f.return_at; }
49
+ } else if (w == "EVENTS") {
50
+ int k; cin >> k; g.events.assign(k, {});
51
+ string line; getline(cin, line);
52
+ for (auto& e : g.events) {
53
+ getline(cin, line);
54
+ istringstream in(line);
55
+ string tag; in >> tag >> e.type;
56
+ for (int v; in >> v;) e.args.push_back(v);
57
+ }
58
+ } else if (w == "END") {
59
+ return true;
60
+ }
61
+ }
62
+ return false;
63
+ }
64
+
65
+ // ---------------- 预计算 ----------------
66
+ static int N = 0, NN = 0;
67
+ static vector<char> W; // 墙
68
+ static vector<int16_t> Dall; // 全图两两最短路
69
+ static bool pre = false;
70
+ static const int INF = 9999;
71
+ static const int DX[5] = {0, 0, 0, -1, 1}, DY[5] = {0, -1, 1, 0, 0};
72
+ static const char MVC[5] = {'S', 'U', 'D', 'L', 'R'};
73
+
74
+ inline int cid(int x, int y) { return y * N + x; }
75
+ inline int cx(int c) { return c % N; }
76
+ inline int cy(int c) { return c / N; }
77
+ inline int D(int a, int b) { if (a < 0 || b < 0) return INF; return Dall[(size_t)a * NN + b]; }
78
+ inline int manh(int a, int b) { return abs(cx(a) - cx(b)) + abs(cy(a) - cy(b)); }
79
+
80
+ void precompute(const Game& g) {
81
+ N = g.size; NN = N * N; W.assign(NN, 0);
82
+ for (int y = 0; y < N; y++) for (int x = 0; x < N; x++) W[cid(x, y)] = (g.map[y][x] == '#');
83
+ Dall.assign((size_t)NN * NN, INF);
84
+ vector<int> q(NN);
85
+ for (int s = 0; s < NN; s++) {
86
+ if (W[s]) continue;
87
+ int16_t* d = &Dall[(size_t)s * NN];
88
+ int h = 0, t = 0; q[t++] = s; d[s] = 0;
89
+ while (h < t) {
90
+ int c = q[h++]; int x = cx(c), y = cy(c);
91
+ for (int k = 1; k < 5; k++) {
92
+ int nx = x + DX[k], ny = y + DY[k];
93
+ if (nx < 0 || ny < 0 || nx >= N || ny >= N) continue;
94
+ int nc = cid(nx, ny);
95
+ if (W[nc] || d[nc] != INF) continue;
96
+ d[nc] = d[c] + 1; q[t++] = nc;
97
+ }
98
+ }
99
+ }
100
+ pre = true;
101
+ }
102
+
103
+ bool canAtk(int a, int b) {
104
+ if (a < 0 || b < 0) return false;
105
+ int ax = cx(a), ay = cy(a), bx = cx(b), by = cy(b);
106
+ int dx = bx - ax, dy = by - ay;
107
+ int md = abs(dx) + abs(dy);
108
+ if (md > 2) return false;
109
+ if (abs(dx) == 2 && dy == 0) return !W[cid(ax + dx / 2, ay)];
110
+ if (abs(dy) == 2 && dx == 0) return !W[cid(ax, ay + dy / 2)];
111
+ if (abs(dx) == 1 && abs(dy) == 1) return !W[cid(ax + dx, ay)] || !W[cid(ax, ay + dy)];
112
+ return true;
113
+ }
114
+
115
+ // ---------------- 每回合状态 ----------------
116
+ struct EInfo {
117
+ int id, team, cell, hp, flag; bool immune;
118
+ vector<pair<int, double>> pred; double pStay;
119
+ };
120
+ struct MU { int id, cell, hp, flag; bool alive, spawning, canAtk; };
121
+ struct Task { int type; int key; int target; double val; int eta; int hits; };
122
+ // type: 0 RETURN, 1 GET, 2 INTERCEPT, 3 ESCORT, 4 HUNT, 5 ROAM, -1 NONE
123
+
124
+ static double LAMBDA = 2.5;
125
+ inline int lives(int hp, int dmg) { return hp <= 0 ? 0 : (hp + dmg - 1) / dmg; }
126
+
127
+ string decide(const Game& g) {
128
+ if (!pre) precompute(g);
129
+ const int me = g.me, T = g.turn, nTeams = g.teams;
130
+ const int remaining = g.turns - T + 1;
131
+ const int centerCell = cid(N / 2, N / 2);
132
+ LAMBDA = (nTeams == 2) ? 3.0 : (nTeams <= 5) ? 1.5 : 0.8;
133
+
134
+ auto baseCells = [&](int team) {
135
+ vector<int> v; auto [bx, by] = g.bases[team];
136
+ for (int dy = -1; dy <= 1; dy++) for (int dx = -1; dx <= 1; dx++) v.push_back(cid(bx + dx, by + dy));
137
+ return v;
138
+ };
139
+ vector<vector<int>> bcells(nTeams);
140
+ for (int t = 0; t < nTeams; t++) bcells[t] = baseCells(t);
141
+ auto distToBase = [&](int c, int team) {
142
+ int best = INF; for (int b : bcells[team]) best = min(best, D(c, b)); return best;
143
+ };
144
+ auto nearestBaseCell = [&](int c, int team) {
145
+ int best = INF, bc = bcells[team][4];
146
+ for (int b : bcells[team]) if (D(c, b) < best) { best = D(c, b); bc = b; }
147
+ return bc;
148
+ };
149
+
150
+ // 占用
151
+ vector<int> occ(NN, -1);
152
+ for (auto& u : g.units) if (u.x >= 0) occ[cid(u.x, u.y)] = u.id;
153
+
154
+ // 我方重生顺序
155
+ vector<int> spawnOrder = bcells[me];
156
+ {
157
+ auto [bx, by] = g.bases[me];
158
+ sort(spawnOrder.begin(), spawnOrder.end(), [&](int a, int b) {
159
+ int da = manh(a, centerCell), db = manh(b, centerCell);
160
+ if (da != db) return da < db;
161
+ int ka = abs(cx(a) - bx) + 2 * abs(cy(a) - by), kb = abs(cx(b) - bx) + 2 * abs(cy(b) - by);
162
+ return ka < kb;
163
+ });
164
+ }
165
+
166
+ // 我方角色
167
+ vector<MU> M(3);
168
+ for (int k = 0; k < 3; k++) {
169
+ int uid = 3 * me + k; const Unit* u = nullptr;
170
+ for (auto& x : g.units) if (x.id == uid) u = &x;
171
+ MU m; m.id = uid; m.cell = -1; m.hp = 0; m.flag = -1; m.alive = false; m.spawning = false; m.canAtk = false;
172
+ if (u) {
173
+ if (u->x >= 0) { m.cell = cid(u->x, u->y); m.hp = u->hp; m.flag = u->flag; m.alive = true; m.canAtk = (u->flag < 0); }
174
+ else if (u->respawn_at == T) {
175
+ for (int c : spawnOrder) if (occ[c] < 0) { m.cell = c; occ[c] = uid; break; }
176
+ if (m.cell >= 0) { m.alive = true; m.spawning = true; m.hp = g.hp; m.canAtk = false; }
177
+ }
178
+ }
179
+ M[k] = m;
180
+ }
181
+ int myCarrierIdx = -1;
182
+ for (int k = 0; k < 3; k++) if (M[k].alive && M[k].flag >= 0) myCarrierIdx = k;
183
+
184
+ // 敌人
185
+ vector<EInfo> E;
186
+ for (auto& u : g.units) {
187
+ if (u.team == me) continue;
188
+ EInfo e; e.id = u.id; e.team = u.team; e.hp = u.hp; e.flag = u.flag; e.immune = false; e.pStay = 1;
189
+ if (u.x >= 0) e.cell = cid(u.x, u.y);
190
+ else if (u.respawn_at == T) { e.cell = bcells[u.team][4]; e.immune = true; e.hp = g.hp; }
191
+ else continue;
192
+ E.push_back(e);
193
+ }
194
+ // 可用旗子
195
+ vector<int> flagCell(g.flags.size(), -1);
196
+ vector<char> flagAvail(g.flags.size(), 0);
197
+ for (size_t i = 0; i < g.flags.size(); i++) {
198
+ auto& f = g.flags[i];
199
+ if ((f.status == "home" || f.status == "dropped") && f.x >= 0) { flagCell[i] = cid(f.x, f.y); flagAvail[i] = 1; }
200
+ }
201
+ vector<int> myCells;
202
+ for (auto& m : M) if (m.alive) myCells.push_back(m.cell);
203
+
204
+ // 威胁图
205
+ vector<int> thr(NN, 0);
206
+ for (auto& e : E) {
207
+ if (e.immune || e.flag >= 0) continue;
208
+ int ex = cx(e.cell), ey = cy(e.cell);
209
+ for (int dy = -3; dy <= 3; dy++) for (int dx = -3; dx <= 3; dx++) {
210
+ if (abs(dx) + abs(dy) > 3) continue;
211
+ int x = ex + dx, y = ey + dy;
212
+ if (x < 0 || y < 0 || x >= N || y >= N) continue;
213
+ thr[cid(x, y)]++;
214
+ }
215
+ }
216
+
217
+ // 敌人下一步预测
218
+ for (auto& e : E) {
219
+ e.pred.clear();
220
+ if (e.immune) { e.pred.push_back({e.cell, 1.0}); e.pStay = 1.0; continue; }
221
+ vector<pair<int, double>> cand;
222
+ int x = cx(e.cell), y = cy(e.cell);
223
+ int goal = -1, curG = INF;
224
+ int myNear = -1, curM = INF;
225
+ if (e.flag >= 0) {
226
+ goal = nearestBaseCell(e.cell, e.team); curG = D(e.cell, goal);
227
+ } else {
228
+ for (size_t i = 0; i < g.flags.size(); i++) if (flagAvail[i]) { int d = D(e.cell, flagCell[i]); if (d < curG) { curG = d; goal = flagCell[i]; } }
229
+ for (int c : myCells) { int d = D(e.cell, c); if (d < curM) { curM = d; myNear = c; } }
230
+ }
231
+ double tot = 0;
232
+ for (int k = 0; k < 5; k++) {
233
+ int nx = x + DX[k], ny = y + DY[k];
234
+ if (nx < 0 || ny < 0 || nx >= N || ny >= N) continue;
235
+ int nc = cid(nx, ny);
236
+ if (W[nc]) continue;
237
+ double w;
238
+ if (e.flag >= 0) {
239
+ w = (k == 0) ? 0.5 : ((D(nc, goal) < curG) ? 5.0 : 0.4);
240
+ } else {
241
+ w = (k == 0) ? 1.2 : 1.0;
242
+ if (goal >= 0 && k != 0 && D(nc, goal) < curG) w += 2.0;
243
+ if (myNear >= 0 && curM <= 4 && k != 0 && D(nc, myNear) < curM) w += 1.5;
244
+ if (k != 0 && occ[nc] >= 0) w *= 0.3; // 有人占着,进不去
245
+ }
246
+ cand.push_back({nc, w}); tot += w;
247
+ }
248
+ e.pStay = 0;
249
+ for (auto& c : cand) { c.second /= tot; if (c.first == e.cell) e.pStay = c.second; }
250
+ e.pred = cand;
251
+ }
252
+ auto pRange = [&](int c, const EInfo& e) {
253
+ double p = 0; for (auto& q : e.pred) if (canAtk(c, q.first)) p += q.second; return p;
254
+ };
255
+
256
+ // 带危险权重的 Dijkstra(多源,返回到目标集合的代价,单位 10 = 一步)
257
+ auto ctgTo = [&](const vector<int>& targets, int K10) {
258
+ vector<int> cost(NN, INT_MAX);
259
+ priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
260
+ for (int t : targets) if (t >= 0 && !W[t]) { cost[t] = 0; pq.push({0, t}); }
261
+ while (!pq.empty()) {
262
+ auto [d, c] = pq.top(); pq.pop();
263
+ if (d > cost[c]) continue;
264
+ int x = cx(c), y = cy(c);
265
+ for (int k = 1; k < 5; k++) {
266
+ int nx = x + DX[k], ny = y + DY[k];
267
+ if (nx < 0 || ny < 0 || nx >= N || ny >= N) continue;
268
+ int nc = cid(nx, ny);
269
+ if (W[nc]) continue;
270
+ int step = 10 + K10 * thr[nc];
271
+ if (occ[nc] >= 0 && occ[nc] / 3 != me) step += 25;
272
+ int nd = d + step;
273
+ if (nd < cost[nc]) { cost[nc] = nd; pq.push({nd, nc}); }
274
+ }
275
+ }
276
+ return cost;
277
+ };
278
+
279
+ // ---------------- 任务生成 ----------------
280
+ vector<vector<Task>> tasks(3);
281
+ // 敌方携旗者回家路径
282
+ map<int, vector<int>> epath; // enemy id -> path cells (excluding current)
283
+ for (auto& e : E) {
284
+ if (e.flag < 0 || e.immune) continue;
285
+ int tb = nearestBaseCell(e.cell, e.team);
286
+ vector<int> path; int c = e.cell; int guard = 0;
287
+ while (c != tb && guard++ < 200) {
288
+ int x = cx(c), y = cy(c); int nxt = -1;
289
+ for (int k = 1; k < 5; k++) {
290
+ int nx = x + DX[k], ny = y + DY[k];
291
+ if (nx < 0 || ny < 0 || nx >= N || ny >= N) continue;
292
+ int nc = cid(nx, ny);
293
+ if (W[nc]) continue;
294
+ if (D(nc, tb) == D(c, tb) - 1) { nxt = nc; break; }
295
+ }
296
+ if (nxt < 0) break;
297
+ c = nxt; path.push_back(c);
298
+ }
299
+ epath[e.id] = path;
300
+ }
301
+
302
+ for (int k = 0; k < 3; k++) {
303
+ auto& m = M[k];
304
+ if (!m.alive) { tasks[k].push_back({-1, 0, -1, 0, 0, 0}); continue; }
305
+ if (m.flag >= 0) { tasks[k].push_back({0, 0, -1, 1000, 0, 0}); continue; }
306
+ // GET
307
+ for (size_t i = 0; i < g.flags.size(); i++) {
308
+ if (!flagAvail[i]) continue;
309
+ int fc = flagCell[i];
310
+ int eta = D(m.cell, fc); if (eta >= INF) continue;
311
+ if (g.flags[i].status == "dropped" && g.flags[i].return_at >= 0 && eta > g.flags[i].return_at - T) continue;
312
+ int ret = distToBase(fc, me); if (ret >= INF) continue;
313
+ if (eta + ret > remaining) continue;
314
+ int eEta = INF, nearEnemies = 0;
315
+ for (auto& e : E) {
316
+ if (e.flag >= 0) continue;
317
+ int d = D(e.cell, fc) + (e.immune ? 1 : 0);
318
+ eEta = min(eEta, d);
319
+ if (!e.immune && d <= 5) nearEnemies++;
320
+ }
321
+ double pget;
322
+ if (eta + 2 < eEta) pget = 0.97; else if (eta < eEta) pget = 0.85; else if (eta == eEta) pget = 0.45;
323
+ else if (eta == eEta + 1) pget = 0.25; else pget = 0.1;
324
+ if (nTeams >= 3) pget = max(pget, eta < eEta ? 0.9 : eta == eEta ? 0.55 : eta <= eEta + 2 ? 0.35 : 0.2);
325
+ int corridor = 0;
326
+ for (auto& e : E) if (!e.immune && e.flag < 0 && D(e.cell, fc) + distToBase(e.cell, me) <= ret + 4) corridor++;
327
+ double pret = 0.95 - 0.12 * min(3, nearEnemies) - 0.08 * min(4, corridor);
328
+ double val = 100.0 * pget * pret - LAMBDA * (eta + ret);
329
+ val += 6.0 * (lives(m.hp, g.damage) - 1);
330
+ if (lives(m.hp, g.damage) == 1) {
331
+ bool healthyNear = false;
332
+ for (int j = 0; j < 3; j++) if (j != k && M[j].alive && M[j].flag < 0 && lives(M[j].hp, g.damage) >= 2 && D(M[j].cell, fc) <= eta + 2) healthyNear = true;
333
+ if (healthyNear) val -= 60;
334
+ }
335
+ bool contested = (eEta <= eta + 3);
336
+ tasks[k].push_back({1, (int)i, fc, val, eta, contested ? 1 : 0});
337
+ }
338
+ // INTERCEPT
339
+ for (auto& e : E) {
340
+ if (e.flag < 0 || e.immune) continue;
341
+ auto& path = epath[e.id];
342
+ int L = (int)path.size();
343
+ if (L == 0) continue;
344
+ int tfirst = -1;
345
+ for (int t = 1; t <= L; t++) {
346
+ int q = path[t - 1]; int qx = cx(q), qy = cy(q); bool ok = false;
347
+ for (int dy = -2; dy <= 2 && !ok; dy++) for (int dx = -2; dx <= 2 && !ok; dx++) {
348
+ if (abs(dx) + abs(dy) > 2 || (dx == 0 && dy == 0)) continue;
349
+ int x = qx + dx, y = qy + dy;
350
+ if (x < 0 || y < 0 || x >= N || y >= N) continue;
351
+ int qq = cid(x, y);
352
+ if (W[qq]) continue;
353
+ if (!canAtk(qq, q)) continue;
354
+ if (D(m.cell, qq) <= t) ok = true;
355
+ }
356
+ if (ok) { tfirst = t; break; }
357
+ }
358
+ if (tfirst < 0) continue;
359
+ if (nTeams >= 6 && tfirst > 6) continue;
360
+ if (nTeams >= 3 && nTeams < 6 && tfirst > 12) continue;
361
+ int hits = L - tfirst + 1;
362
+ int target = (tfirst <= 1) ? e.cell : path[tfirst - 1];
363
+ tasks[k].push_back({2, e.id, target, 0, tfirst, hits});
364
+ }
365
+ // ESCORT
366
+ if (myCarrierIdx >= 0 && myCarrierIdx != k) {
367
+ auto& c = M[myCarrierIdx];
368
+ int threats = 0;
369
+ for (auto& e : E) if (!e.immune && e.flag < 0 && D(e.cell, c.cell) <= 5) threats++;
370
+ int d = D(m.cell, c.cell);
371
+ double val = 12 + 22 * min(3, threats) - LAMBDA * 0.5 * d;
372
+ if (threats == 0 && d > 6) val = 2;
373
+ if (nTeams >= 3) val = 40 + 22 * min(3, threats) - LAMBDA * 0.5 * d;
374
+ tasks[k].push_back({3, myCarrierIdx, c.cell, val, d, 0});
375
+ }
376
+ // HUNT
377
+ for (auto& e : E) {
378
+ if (e.immune || e.flag >= 0) continue;
379
+ int d = D(m.cell, e.cell);
380
+ if (d > 7) continue;
381
+ int support = 0, mysup = 0;
382
+ for (auto& e2 : E) if (e2.id != e.id && !e2.immune && e2.flag < 0 && D(e2.cell, e.cell) <= 3) support++;
383
+ for (int j = 0; j < 3; j++) if (j != k && M[j].alive && M[j].canAtk && D(M[j].cell, e.cell) <= 4) mysup++;
384
+ double val = (nTeams == 2 ? 30 : 8) - LAMBDA * d + (e.hp <= 68 ? 12 : 0) + (e.hp <= 34 ? 12 : 0) - 18 * support + 12 * mysup;
385
+ if (nTeams > 2 && d > 4) continue;
386
+ if (nTeams >= 6 && !(d <= 2 && lives(e.hp, g.damage) <= 2)) continue;
387
+ if (lives(m.hp, g.damage) == 1 && lives(e.hp, g.damage) >= 2 && mysup == 0) continue;
388
+ if (lives(m.hp, g.damage) < lives(e.hp, g.damage) && mysup == 0) val -= 15;
389
+ if (val <= 0) continue;
390
+ tasks[k].push_back({4, e.id, e.cell, val, d, 0});
391
+ }
392
+ // ROAM:去空旗点等旗(优先自家旗点、中心)
393
+ {
394
+ int bestSpot = -1; double bestV = -1e9;
395
+ vector<pair<double, int>> roams;
396
+ for (size_t i = 0; i < g.spots.size(); i++) {
397
+ int sc = cid(g.spots[i].first, g.spots[i].second);
398
+ bool taken = false;
399
+ for (size_t f = 0; f < g.flags.size(); f++) if (flagAvail[f] && flagCell[f] == sc) taken = true;
400
+ if (taken) continue;
401
+ int d = D(m.cell, sc); if (d >= INF) continue;
402
+ double v = 6 - 0.35 * d;
403
+ for (auto& f : g.flags) if (f.status == "cooldown" && f.return_at >= 0) {
404
+ int wait = max(d, f.return_at - T);
405
+ v += max(0.0, 0.25 * (30.0 - LAMBDA * (wait + D(sc, bcells[me][4]))));
406
+ }
407
+ if ((int)i == me) v += (nTeams >= 6 ? 7 : 3);
408
+ if (sc == centerCell) v += (nTeams >= 3 && nTeams <= 5) ? 5.0 : 2.5;
409
+ v -= 1.5 * thr[sc];
410
+ roams.push_back({v, sc});
411
+ if (v > bestV) { bestV = v; bestSpot = sc; }
412
+ }
413
+ sort(roams.rbegin(), roams.rend());
414
+ for (size_t i = 0; i < roams.size() && i < 3; i++) tasks[k].push_back({5, roams[i].second, roams[i].second, max(1.0, roams[i].first), 0, 0});
415
+ if (roams.empty()) tasks[k].push_back({5, bcells[me][4], bcells[me][4], 1.0, 0, 0});
416
+ }
417
+ }
418
+
419
+ // ---------------- 任务组合枚举 ----------------
420
+ auto evalCombo = [&](const array<int, 3>& idx) {
421
+ double total = 0;
422
+ // GET 同旗
423
+ vector<pair<int, int>> gets; // (eta, k)
424
+ for (int k = 0; k < 3; k++) { auto& t = tasks[k][idx[k]]; if (t.type == 1) gets.push_back({t.eta, k}); }
425
+ sort(gets.begin(), gets.end());
426
+ map<int, int> cnt;
427
+ for (auto& [eta, k] : gets) {
428
+ auto& t = tasks[k][idx[k]];
429
+ int c = cnt[t.key]++;
430
+ double f = 1.0;
431
+ if (c == 1) f = t.hits ? 0.6 : (nTeams >= 3 ? 0.55 : 0.15);
432
+ if (c >= 2) f = t.hits ? 0.35 : (nTeams >= 3 ? 0.4 : 0.03);
433
+ total += t.val * f;
434
+ }
435
+ // INTERCEPT
436
+ map<int, vector<int>> inter;
437
+ for (int k = 0; k < 3; k++) { auto& t = tasks[k][idx[k]]; if (t.type == 2) inter[t.key].push_back(k); }
438
+ for (auto& [eid, ks] : inter) {
439
+ const EInfo* e = nullptr; for (auto& x : E) if (x.id == eid) e = &x;
440
+ int hits = 0, tfirst = INF; double tcost = 0;
441
+ for (int k : ks) { auto& t = tasks[k][idx[k]]; hits += t.hits; tfirst = min(tfirst, t.eta); tcost += LAMBDA * t.eta; }
442
+ int needed = (e->hp + g.damage - 1) / g.damage;
443
+ double deny = (nTeams == 2) ? 0.9 : 0.55;
444
+ if (hits >= needed) total += 100.0 * (deny + 0.45) * 0.85 - tcost;
445
+ else total += 15.0 * hits / needed - tcost;
446
+ }
447
+ // ESCORT
448
+ int esc = 0;
449
+ for (int k = 0; k < 3; k++) { auto& t = tasks[k][idx[k]]; if (t.type == 3) { total += t.val * (esc == 0 ? 1.0 : 0.4); esc++; } }
450
+ // HUNT
451
+ map<int, int> hcnt;
452
+ for (int k = 0; k < 3; k++) { auto& t = tasks[k][idx[k]]; if (t.type == 4) { int c = hcnt[t.key]++; total += t.val * (c == 0 ? 1.0 : c == 1 ? 0.8 : 0.5); } }
453
+ // ROAM
454
+ map<int, int> rcnt;
455
+ for (int k = 0; k < 3; k++) { auto& t = tasks[k][idx[k]]; if (t.type == 5) { int c = rcnt[t.key]++; total += t.val * (c == 0 ? 1.0 : 0.2); } }
456
+ for (int k = 0; k < 3; k++) { auto& t = tasks[k][idx[k]]; if (t.type == 0) total += t.val; }
457
+ return total;
458
+ };
459
+ array<int, 3> best = {0, 0, 0}; double bestV = -1e18;
460
+ for (int a = 0; a < (int)tasks[0].size(); a++)
461
+ for (int b = 0; b < (int)tasks[1].size(); b++)
462
+ for (int c = 0; c < (int)tasks[2].size(); c++) {
463
+ array<int, 3> idx = {a, b, c};
464
+ double v = evalCombo(idx);
465
+ if (v > bestV) { bestV = v; best = idx; }
466
+ }
467
+
468
+ // ---------------- 移动选择 ----------------
469
+ // 一命的角色站在旗上、而血多的队友就在旁边:让位(走开、不拾旗)
470
+ vector<char> yieldK(3, 0);
471
+ for (int k = 0; k < 3; k++) {
472
+ auto& m = M[k];
473
+ if (!m.alive || m.flag >= 0 || lives(m.hp, g.damage) != 1 || remaining <= 30) continue;
474
+ bool onFlag = false;
475
+ for (size_t i = 0; i < g.flags.size(); i++) if (flagAvail[i] && flagCell[i] == m.cell) onFlag = true;
476
+ if (!onFlag) continue;
477
+ for (int j = 0; j < 3; j++) if (j != k && M[j].alive && !M[j].spawning && M[j].flag < 0 && lives(M[j].hp, g.damage) >= 2 && D(M[j].cell, m.cell) <= 2) yieldK[k] = 1;
478
+ }
479
+ vector<int> order; // 携旗者优先,其次让位者
480
+ for (int k = 0; k < 3; k++) if (M[k].alive && M[k].flag >= 0) order.push_back(k);
481
+ for (int k = 0; k < 3; k++) if (M[k].alive && M[k].flag < 0 && yieldK[k]) order.push_back(k);
482
+ for (int k = 0; k < 3; k++) if (M[k].alive && M[k].flag < 0 && !yieldK[k]) order.push_back(k);
483
+ vector<int> chosenCell(3, -1), chosenMove(3, 0);
484
+ vector<char> reserved(NN, 0);
485
+ vector<char> leaving(3, 0);
486
+ vector<int> forced(3, -1); // 被队友要求换位:必须走到该格
487
+ vector<int> carrierNext(3, -1); // 携旗者选定的下一格
488
+ for (int k : order) {
489
+ auto& m = M[k]; auto& t = tasks[k][best[k]];
490
+ if (forced[k] >= 0) {
491
+ int fc = forced[k]; int mv = 0;
492
+ for (int q = 1; q < 5; q++) if (cx(m.cell) + DX[q] == cx(fc) && cy(m.cell) + DY[q] == cy(fc)) mv = q;
493
+ chosenMove[k] = mv; chosenCell[k] = fc; reserved[fc] = 1; leaving[k] = 1;
494
+ continue;
495
+ }
496
+ vector<int> targets;
497
+ if (t.type == 0) targets = bcells[me];
498
+ else if (t.type == 3) {
499
+ // 护送:走到携旗者前方(沿其回家路线再往前一格)
500
+ int ci = t.key; int cc = (chosenCell[ci] >= 0) ? chosenCell[ci] : M[ci].cell;
501
+ auto cg = ctgTo(bcells[me], 5);
502
+ int ahead = cc; int bestc = cg[cc];
503
+ for (int q = 1; q < 5; q++) { int nx = cx(cc) + DX[q], ny = cy(cc) + DY[q]; if (nx < 0 || ny < 0 || nx >= N || ny >= N) continue; int nc = cid(nx, ny); if (W[nc]) continue; if (cg[nc] < bestc) { bestc = cg[nc]; ahead = nc; } }
504
+ targets = {ahead};
505
+ }
506
+ else if (t.type >= 0) targets = {t.target};
507
+ else targets = {m.cell};
508
+ bool chased = false;
509
+ for (auto& e : E) if (!e.immune && e.flag < 0 && manh(e.cell, m.cell) <= 2) chased = true;
510
+ int K10 = (m.flag >= 0) ? (chased ? 3 : (nTeams >= 6 ? 4 : 15)) : 5;
511
+ if (m.hp <= 34 && !chased && m.flag < 0) K10 += 15;
512
+ int curBaseD = (m.flag >= 0) ? distToBase(m.cell, me) : 0;
513
+ auto ctg = ctgTo(targets, K10);
514
+ double hitVal = 2.5 + (m.flag >= 0 ? 1.0 : 0) + (m.hp <= 34 ? 3.0 : m.hp <= 68 ? 1.0 : 0);
515
+ if (nTeams >= 6) hitVal *= 1.3;
516
+ if (m.spawning) hitVal = 0;
517
+ int x = cx(m.cell), y = cy(m.cell);
518
+ double bestS = -1e18; int bestK = 0, bestC = m.cell, bestSwap = -1;
519
+ for (int mv = 0; mv < 5; mv++) {
520
+ int nx = x + DX[mv], ny = y + DY[mv];
521
+ if (nx < 0 || ny < 0 || nx >= N || ny >= N) continue;
522
+ int nc = cid(nx, ny);
523
+ if (W[nc]) continue;
524
+ if (reserved[nc]) continue;
525
+ // 队友当前格:队友已决定离开可进;携旗者可与尚未决定的队友换位
526
+ bool teammateHere = false; int swapWith = -1;
527
+ for (int j = 0; j < 3; j++) if (j != k && M[j].alive && M[j].cell == nc && !(leaving[j])) {
528
+ if (m.flag >= 0 && chosenCell[j] < 0 && forced[j] < 0 && M[j].flag < 0 && !M[j].spawning) swapWith = j; else teammateHere = true;
529
+ }
530
+ if (teammateHere) continue;
531
+ if (m.flag >= 0 && distToBase(nc, me) > curBaseD) continue; // 携旗不后退
532
+ if (yieldK[k] && mv == 0) continue; // 让位:必须走开
533
+ double s = 0;
534
+ if (ctg[nc] >= INT_MAX) s -= 1e6; else s -= ctg[nc] / 10.0;
535
+ // 攻击期望
536
+ double atk = 0;
537
+ if (m.canAtk && !m.spawning) {
538
+ for (auto& e : E) {
539
+ if (e.immune) continue;
540
+ double p = pRange(nc, e); if (p <= 0) continue;
541
+ double v = 2.5 + (e.flag >= 0 ? 2.5 : 0) + (e.hp <= 34 ? 3.0 : e.hp <= 68 ? 1.0 : 0);
542
+ if (myCarrierIdx >= 0 && D(e.cell, M[myCarrierIdx].cell) <= 3) v += 1.0;
543
+ atk = max(atk, p * v);
544
+ }
545
+ }
546
+ s += (nTeams >= 6 ? 0.5 : 1.0) * atk;
547
+ // 风险
548
+ double risk = 0;
549
+ if (!m.spawning) for (auto& e : E) { if (e.immune || e.flag >= 0) continue; risk += pRange(nc, e) * hitVal; }
550
+ s -= risk;
551
+ // 敌人占着的格子
552
+ if (mv != 0 && occ[nc] >= 0 && occ[nc] / 3 != me) {
553
+ const EInfo* e = nullptr; for (auto& x2 : E) if (x2.id == occ[nc]) e = &x2;
554
+ if (e) s -= 1.0 + 6.0 * e->pStay;
555
+ }
556
+ if (getenv("BOTDBG") && m.flag >= 0) cerr << "T" << T << " u" << m.id << " mv" << mv << " nc(" << cx(nc) << "," << cy(nc) << ") ctg=" << ctg[nc] << " atk=" << atk << " risk=" << risk << " s=" << s << " task=" << t.type << endl;
557
+ if (swapWith >= 0) s -= 0.3;
558
+ if (s > bestS) { bestS = s; bestK = mv; bestC = nc; bestSwap = swapWith; }
559
+ }
560
+ if (bestSwap >= 0) { forced[bestSwap] = m.cell; reserved[m.cell] = 1; }
561
+ chosenMove[k] = bestK; chosenCell[k] = bestC; reserved[bestC] = 1;
562
+ if (bestC != m.cell) leaving[k] = 1;
563
+ }
564
+
565
+ // ---------------- 攻击目标 / 拾旗 / 丢旗 ----------------
566
+ vector<string> act(3, "-");
567
+ map<int, int> dmgOn; // 目标 -> 已分配的命中次数
568
+ auto threatAt = [&](int c) {
569
+ double best = 0; int cnt = 0;
570
+ for (auto& e : E) { if (e.immune || e.flag >= 0) continue; double p = pRange(c, e); best = max(best, p); if (p >= 0.45) cnt++; }
571
+ return make_pair(best, cnt);
572
+ };
573
+ for (int k : order) {
574
+ auto& m = M[k]; int c = chosenCell[k];
575
+ // 携旗被追:距离基地还远、命够,就把旗放在脚下(继续站着)并转为反击
576
+ if (m.flag >= 0 && !m.spawning) {
577
+ auto [pt, cnt] = threatAt(c);
578
+ int dist = distToBase(c, me);
579
+ int escorts = 0;
580
+ for (int j = 0; j < 3; j++) if (j != k && M[j].alive && M[j].canAtk && !M[j].spawning && manh(M[j].cell, c) <= 3) escorts++;
581
+ if (nTeams <= 5 && cnt == 1 && dist > lives(m.hp, g.damage) + 1 && lives(m.hp, g.damage) >= 2 && escorts <= 1 && remaining > dist + 6) {
582
+ act[k] = "X";
583
+ }
584
+ continue;
585
+ }
586
+ // 拾旗:脚下有旗。若有敌人在射程内且自己命够,先开火守旗(旗在脚下别人拿不走)
587
+ if (m.flag < 0) {
588
+ int onFlag = -1;
589
+ for (size_t i = 0; i < g.flags.size(); i++) if (flagAvail[i] && flagCell[i] == c) onFlag = (int)i;
590
+ if (onFlag >= 0) {
591
+ bool yield = yieldK[k] && c == m.cell;
592
+ if (yield) { /* 让血多的队友来拿 */ }
593
+ else {
594
+ auto [pt, cnt] = threatAt(c);
595
+ bool timerOk = !(g.flags[onFlag].status == "dropped" && g.flags[onFlag].return_at >= 0 && g.flags[onFlag].return_at - T <= 2);
596
+ bool guard = (pt >= 0.45) && lives(m.hp, g.damage) >= 2 && timerOk && m.canAtk && !m.spawning && remaining > distToBase(c, me) + 4;
597
+ if (!guard) { act[k] = "P"; continue; }
598
+ }
599
+ }
600
+ }
601
+ if (!m.canAtk || m.spawning) continue;
602
+ double bestE = 0; int bestId = -1;
603
+ for (auto& e : E) {
604
+ if (e.immune) continue;
605
+ double p = pRange(c, e); if (p <= 0.001) continue;
606
+ double v = 2.5 + (e.flag >= 0 ? 2.5 : 0) + (e.hp <= 34 ? 3.0 : e.hp <= 68 ? 1.0 : 0);
607
+ if (myCarrierIdx >= 0 && D(e.cell, M[myCarrierIdx].cell) <= 3) v += 1.0;
608
+ int needed = (e.hp + g.damage - 1) / g.damage;
609
+ int already = dmgOn.count(e.id) ? dmgOn[e.id] : 0;
610
+ if (already + 1 >= needed) v += 3.0; // 集火可击杀
611
+ else if (already > 0) v += 0.8;
612
+ if (p * v > bestE) { bestE = p * v; bestId = e.id; }
613
+ }
614
+ if (bestId >= 0) { act[k] = to_string(bestId); dmgOn[bestId]++; }
615
+ else if (m.flag < 0) {
616
+ for (auto& sp : g.spots) if (cid(sp.first, sp.second) == c) act[k] = "P";
617
+ }
618
+ }
619
+
620
+ string out = to_string(T);
621
+ for (int k = 0; k < 3; k++) {
622
+ if (!M[k].alive) { out += " S -"; continue; }
623
+ out += ' '; out += MVC[chosenMove[k]]; out += ' '; out += act[k];
624
+ }
625
+ return out;
626
+ }
627
+
628
+ int main() {
629
+ ios::sync_with_stdio(false);
630
+ Game g;
631
+ while (read_turn(g)) {
632
+ cout << decide(g) << endl;
633
+ }
634
+ }