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,689 @@
1
+ // 多阵营夺旗 bot(支持 2–15 方)
2
+ //
3
+ // 双模式架构(按人数切换,均经本地对战验证):
4
+ // · 2–3 方「战术模式」:每回合为 3 个角色分配任务(携旗回家 / 抢旗 / 清剿基地 /
5
+ // 支援抢旗 / 护送 / 拦截敌方携旗手 / 守旗点),用危险加权的 Dijkstra 规划移动
6
+ // (敌方射程、局部兵力差、敌方基地禁区都计入代价),动作阶段做集火目标选择。
7
+ // · ≥4 方「抱团模式」:实测人多混战里简单纪律胜过复杂门禁——三个单位抱团奔
8
+ // 往"往返时间最短"的旗,拾旗后携旗手沿危险加权路线绕开火线回家,射程内
9
+ // 优先攻击敌方携旗手。死亡很便宜(10 回合满血重生),以量取胜。
10
+ //
11
+ // 关键机制:死亡重生预测(重生当回合即可移动/拾旗)、自家旗点专职看守、
12
+ // 携旗必派护卫(护卫就位于携旗手阵亡点旁可接棒)、残血不拾贴身旗、
13
+ // 队友在场减危、内部移动冲突消解。
14
+ #include <bits/stdc++.h>
15
+ using namespace std;
16
+
17
+ struct Unit { int id, team, x, y, hp, flag, respawn_at; };
18
+ struct FlagT { int id; string status; int x, y, holder, return_at; };
19
+ struct EventT { string type; vector<int> args; };
20
+
21
+ struct Game {
22
+ int teams = 0, me = 0, size = 0, totalTurns = 0, maxhp = 0, dmg = 0, respT = 0, flagRet = 0, flagCd = 0;
23
+ vector<string> grid;
24
+ vector<pair<int,int>> bases, spots;
25
+ int turn = 0;
26
+ vector<int> score;
27
+ vector<Unit> units;
28
+ vector<FlagT> flags;
29
+ vector<EventT> events;
30
+ };
31
+
32
+ bool read_turn(Game& g) {
33
+ string w;
34
+ while (cin >> w) {
35
+ if (w == "INIT") { cin >> g.teams >> g.me >> g.size >> g.totalTurns >> g.maxhp >> g.dmg >> g.respT >> g.flagRet >> g.flagCd; }
36
+ else if (w == "MAP") { g.grid.assign(g.size, string()); for (auto& r : g.grid) cin >> r; }
37
+ else if (w == "BASES") { int k; cin >> k; g.bases.resize(k); for (auto& b : g.bases) cin >> b.first >> b.second; }
38
+ else if (w == "SPOTS") { int k; cin >> k; g.spots.resize(k); for (auto& s : g.spots) cin >> s.first >> s.second; }
39
+ else if (w == "TURN") { cin >> g.turn; }
40
+ else if (w == "SCORE") { g.score.resize(g.teams); for (auto& s : g.score) cin >> s; }
41
+ else if (w == "UNITS") { int k; cin >> k; g.units.resize(k); for (auto& u : g.units) { string tag; cin >> tag >> u.id >> u.team >> u.x >> u.y >> u.hp >> u.flag >> u.respawn_at; } }
42
+ else if (w == "FLAGS") { int k; cin >> k; g.flags.resize(k); for (auto& f : g.flags) { string tag; cin >> tag >> f.id >> f.status >> f.x >> f.y >> f.holder >> f.return_at; } }
43
+ else if (w == "EVENTS") { int k; cin >> k; g.events.assign(k, {}); string line; getline(cin, line); for (auto& e : g.events) { getline(cin, line); istringstream in(line); string tag; in >> tag >> e.type; for (int v; in >> v;) e.args.push_back(v); } }
44
+ else if (w == "END") return true;
45
+ }
46
+ return false;
47
+ }
48
+
49
+ // ---------------- 全局 ----------------
50
+ int S, N, ME, MAXHP, DMG, TOTALTURNS;
51
+ vector<string> G;
52
+ vector<vector<int>> dB; // dB[team][cell] = 到该阵营基地(3x3)的 BFS 距离
53
+ vector<float> baseDanger; // 敌方基地附近静态危险
54
+ vector<pair<int,int>> spotsG, myBase9, mySpawnOrder;
55
+ pair<int,int> C0;
56
+ bool inited = false;
57
+
58
+ inline int ID(int x, int y) { return y * S + x; }
59
+ inline bool openc(int x, int y) { return x >= 0 && x < S && y >= 0 && y < S && G[y][x] == '.'; }
60
+ inline int md(int ax, int ay, int bx, int by) { return abs(ax - bx) + abs(ay - by); }
61
+
62
+ bool canAtk(int ax, int ay, int bx, int by) {
63
+ int dx = bx - ax, dy = by - ay;
64
+ if (abs(dx) + abs(dy) > 2) return false;
65
+ if (abs(dx) == 2 && dy == 0) return openc(ax + dx / 2, ay);
66
+ if (abs(dy) == 2 && dx == 0) return openc(ax, ay + dy / 2);
67
+ if (abs(dx) == 1 && abs(dy) == 1) return openc(ax + dx, ay) || openc(ax, ay + dy);
68
+ return true;
69
+ }
70
+
71
+ vector<int> bfsCells(const vector<pair<int,int>>& src) {
72
+ vector<int> d(S * S, INT_MAX);
73
+ deque<int> q;
74
+ for (auto& p : src) if (openc(p.first, p.second) && d[ID(p.first, p.second)] == INT_MAX) { d[ID(p.first, p.second)] = 0; q.push_back(ID(p.first, p.second)); }
75
+ const int DX[4] = {1,-1,0,0}, DY[4] = {0,0,1,-1};
76
+ while (!q.empty()) {
77
+ int c = q.front(); q.pop_front();
78
+ int x = c % S, y = c / S, dc = d[c] + 1;
79
+ for (int i = 0; i < 4; i++) {
80
+ int nx = x + DX[i], ny = y + DY[i];
81
+ if (openc(nx, ny)) { int nc = ID(nx, ny); if (d[nc] > dc) { d[nc] = dc; q.push_back(nc); } }
82
+ }
83
+ }
84
+ return d;
85
+ }
86
+
87
+ void doInit(Game& g) {
88
+ S = g.size; N = g.teams; ME = g.me; MAXHP = g.maxhp; DMG = g.dmg; TOTALTURNS = g.totalTurns; G = g.grid;
89
+ C0 = {S / 2, S / 2};
90
+ baseDanger.assign(S * S, 0.f);
91
+ for (int k = 0; k < N; k++) {
92
+ int bx = g.bases[k].first, by = g.bases[k].second;
93
+ vector<pair<int,int>> cells;
94
+ for (int dy = -1; dy <= 1; dy++) for (int dx = -1; dx <= 1; dx++) cells.push_back({bx + dx, by + dy});
95
+ dB.push_back(bfsCells(cells));
96
+ if (k == ME) myBase9 = cells;
97
+ else for (int c = 0; c < S * S; c++) {
98
+ int dd = dB[k][c];
99
+ baseDanger[c] += dd == 0 ? 0.8f : dd == 1 ? 0.35f : dd == 2 ? 0.12f : 0.f;
100
+ }
101
+ }
102
+ int bcx = g.bases[ME].first, bcy = g.bases[ME].second;
103
+ mySpawnOrder = myBase9;
104
+ sort(mySpawnOrder.begin(), mySpawnOrder.end(), [&](const pair<int,int>& a, const pair<int,int>& b) {
105
+ int da = md(a.first, a.second, C0.first, C0.second), db = md(b.first, b.second, C0.first, C0.second);
106
+ if (da != db) return da < db;
107
+ return abs(a.first - bcx) + 2 * abs(a.second - bcy) < abs(b.first - bcx) + 2 * abs(b.second - bcy);
108
+ });
109
+ spotsG = g.spots;
110
+ inited = true;
111
+ }
112
+
113
+ // 角色
114
+ enum { R_CARRY = 0, R_FETCH, R_CLEAR, R_SUPPORT, R_ESCORT, R_HUNT, R_CAMP, R_IDLE, R_DEAD };
115
+
116
+
117
+ // 简单模式(对照实验/备用):携旗回家,否则奔最近可抢的旗,射程内选最优目标攻击
118
+ string decideSimple(Game& g) {
119
+ int T = g.turn;
120
+ const int DX[4] = {1,-1,0,0}, DY[4] = {0,0,1,-1};
121
+ Unit raw[3] = {};
122
+ for (auto& u : g.units) if (u.team == ME) raw[u.id - 3 * ME] = u;
123
+ set<int> occ;
124
+ for (auto& u : g.units) if (u.x >= 0) occ.insert(ID(u.x, u.y));
125
+ int ux[3], uy[3], uhp[3], uflag[3]; bool alive[3], respNow[3];
126
+ for (int k = 0; k < 3; k++) {
127
+ ux[k] = raw[k].x; uy[k] = raw[k].y; uhp[k] = raw[k].hp; uflag[k] = raw[k].flag;
128
+ alive[k] = ux[k] >= 0; respNow[k] = false;
129
+ if (!alive[k] && raw[k].respawn_at == T)
130
+ for (auto& p : mySpawnOrder) { int c = ID(p.first, p.second);
131
+ if (!occ.count(c)) { ux[k] = p.first; uy[k] = p.second; uhp[k] = MAXHP; uflag[k] = -1; alive[k] = true; respNow[k] = true; occ.insert(c); break; } }
132
+ }
133
+ vector<Unit> en;
134
+ for (auto& u : g.units) if (u.team != ME && u.x >= 0) en.push_back(u);
135
+ // 危险图(敌方非携旗单位的射程)
136
+ vector<float> danger(S * S, 0.f);
137
+ for (auto& e : en) {
138
+ if (e.flag >= 0) continue;
139
+ for (int dy = -4; dy <= 4; dy++) for (int dx = -4; dx <= 4; dx++) {
140
+ int d = abs(dx) + abs(dy); if (d == 0 || d > 4) continue;
141
+ int x = e.x + dx, y = e.y + dy;
142
+ if (!openc(x, y)) continue;
143
+ float w = 0;
144
+ if (d <= 2) w = canAtk(e.x, e.y, x, y) ? 1.f : 0.3f;
145
+ else if (d == 3) w = 0.45f; else w = 0.12f;
146
+ danger[ID(x, y)] += w;
147
+ }
148
+ }
149
+ for (int k = 0; k < 3; k++) if (alive[k]) {
150
+ for (int dy = -2; dy <= 2; dy++) for (int dx = -2; dx <= 2; dx++) {
151
+ if (abs(dx) + abs(dy) == 0) continue;
152
+ int x = ux[k] + dx, y = uy[k] + dy;
153
+ if (openc(x, y)) danger[ID(x, y)] -= 0.25f;
154
+ }
155
+ }
156
+ for (int c = 0; c < S * S; c++) if (danger[c] < 0.f) danger[c] = 0.f;
157
+ // 可见旗子
158
+ vector<pair<int,int>> afv;
159
+ for (auto& f : g.flags) if ((f.status == "home" || f.status == "dropped") && f.return_at != T) afv.push_back({f.x, f.y});
160
+ string out = to_string(T);
161
+ set<int> of;
162
+ for (int k = 0; k < 3; k++) if (alive[k]) of.insert(ID(ux[k], uy[k]));
163
+ for (int k = 0; k < 3; k++) {
164
+ string mv = "S", act = "-";
165
+ if (alive[k]) {
166
+ vector<pair<int,int>> targs;
167
+ if (uflag[k] >= 0) targs = myBase9;
168
+ else {
169
+ int bd = INT_MAX; pair<int,int> best{-1,-1};
170
+ for (auto& p : afv) {
171
+ int d = md(ux[k], uy[k], p.first, p.second) + dB[ME][ID(p.first, p.second)];
172
+ if (d < bd) { bd = d; best = p; }
173
+ }
174
+ if (best.first >= 0) targs = {best};
175
+ }
176
+ // BFS 找一步
177
+ int nxk = ux[k], nyk = uy[k];
178
+ if (!targs.empty()) {
179
+ float wD = (uflag[k] >= 0) ? 1.5f : 0.6f; // 携旗手绕开火线
180
+ vector<float> D(S * S, 1e18f);
181
+ priority_queue<pair<float,int>, vector<pair<float,int>>, greater<pair<float,int>>> pq;
182
+ for (auto& p : targs) { int c = ID(p.first, p.second); if (openc(p.first, p.second) && D[c] > 0) { D[c] = 0.f; pq.push({0.f, c}); } }
183
+ while (!pq.empty()) {
184
+ pair<float,int> pr = pq.top(); pq.pop();
185
+ float dd = pr.first; int c = pr.second;
186
+ if (dd > D[c] + 1e-6f) continue;
187
+ int x = c % S, y = c / S;
188
+ for (int i = 0; i < 4; i++) {
189
+ int ax = x + DX[i], ay = y + DY[i];
190
+ if (!openc(ax, ay)) continue;
191
+ int ac = ID(ax, ay);
192
+ float cost = 1.f + wD * danger[ac];
193
+ if (dd + cost < D[ac] - 1e-6f) { D[ac] = dd + cost; pq.push({D[ac], ac}); }
194
+ }
195
+ }
196
+ int cur = ID(ux[k], uy[k]);
197
+ if (D[cur] > 0.f && D[cur] < 1e17f) {
198
+ float bc = 1e18f; int bs = -1;
199
+ for (int i = 0; i < 4; i++) {
200
+ int ax = ux[k] + DX[i], ay = uy[k] + DY[i];
201
+ if (!openc(ax, ay)) continue;
202
+ int ac = ID(ax, ay);
203
+ if (of.count(ac)) continue;
204
+ bool enemyOn = false;
205
+ for (auto& e : en) if (e.x == ax && e.y == ay) enemyOn = true;
206
+ if (enemyOn) continue;
207
+ float v = 1.f + wD * danger[ac] + D[ac];
208
+ if (v < bc) { bc = v; bs = ac; }
209
+ }
210
+ if (bs >= 0) { of.erase(cur); of.insert(bs); nxk = bs % S; nyk = bs / S; }
211
+ }
212
+ }
213
+ int ddx = nxk - ux[k], ddy = nyk - uy[k];
214
+ if (ddx == 1) mv = "R"; else if (ddx == -1) mv = "L"; else if (ddy == 1) mv = "D"; else if (ddy == -1) mv = "U";
215
+ if (uflag[k] < 0) {
216
+ bool onF = false;
217
+ for (auto& f : g.flags) if ((f.status == "home" || f.status == "dropped") && f.return_at != T && f.x == nxk && f.y == nyk) onF = true;
218
+ if (onF) act = "P";
219
+ else if (!respNow[k]) {
220
+ int bestT = -1; float bs2 = -1e9;
221
+ for (auto& e : en) {
222
+ if (!canAtk(nxk, nyk, e.x, e.y)) continue;
223
+ float s2 = (e.flag >= 0 ? 100.f : 0.f) + (MAXHP - e.hp) * 0.3f;
224
+ if (s2 > bs2) { bs2 = s2; bestT = e.id; }
225
+ }
226
+ if (bestT >= 0) act = to_string(bestT);
227
+ }
228
+ }
229
+ }
230
+ out += " " + mv + " " + act;
231
+ }
232
+ return out;
233
+ }
234
+
235
+ string decide(Game& g) {
236
+ if (!inited) doInit(g);
237
+ int T = g.turn, total = TOTALTURNS;
238
+ if (N >= 4) return decideSimple(g); // 4 人以上:抱团抢旗的简单模式胜过复杂门禁
239
+ const int DX[4] = {1,-1,0,0}, DY[4] = {0,0,1,-1};
240
+
241
+ // ---- 我的角色(含本回合重生的预测位置)
242
+ Unit raw[3] = {};
243
+ for (auto& u : g.units) if (u.team == ME) raw[u.id - 3 * ME] = u;
244
+ int ux[3], uy[3], uhp[3], uflag[3]; bool alive[3], respNow[3];
245
+ set<int> occ;
246
+ for (auto& u : g.units) if (u.x >= 0) occ.insert(ID(u.x, u.y));
247
+ for (int k = 0; k < 3; k++) {
248
+ ux[k] = raw[k].x; uy[k] = raw[k].y; uhp[k] = raw[k].hp; uflag[k] = raw[k].flag;
249
+ alive[k] = ux[k] >= 0; respNow[k] = false;
250
+ if (!alive[k] && raw[k].respawn_at == T) {
251
+ for (auto& p : mySpawnOrder) {
252
+ int c = ID(p.first, p.second);
253
+ if (occ.find(c) == occ.end()) { ux[k] = p.first; uy[k] = p.second; uhp[k] = MAXHP; uflag[k] = -1; alive[k] = true; respNow[k] = true; occ.insert(c); break; }
254
+ }
255
+ }
256
+ }
257
+ vector<Unit> en;
258
+ for (auto& u : g.units) if (u.team != ME && u.x >= 0) en.push_back(u);
259
+
260
+ // ---- 危险图
261
+ vector<float> danger(S * S, 0.f);
262
+ for (auto& e : en) {
263
+ if (e.flag >= 0) continue; // 携旗者不能攻击
264
+ for (int dy = -4; dy <= 4; dy++) for (int dx = -4; dx <= 4; dx++) {
265
+ int d = abs(dx) + abs(dy); if (d == 0 || d > 4) continue;
266
+ int x = e.x + dx, y = e.y + dy;
267
+ if (!openc(x, y)) continue;
268
+ float w = 0;
269
+ if (d <= 2) w = canAtk(e.x, e.y, x, y) ? 1.f : 0.3f;
270
+ else if (d == 3) w = 0.45f; else w = 0.12f;
271
+ danger[ID(x, y)] += w;
272
+ }
273
+ }
274
+ for (int c = 0; c < S * S; c++) danger[c] += baseDanger[c];
275
+ // 盟友减危:附近有队友时更敢打(护送/结伴作战)
276
+ for (int k = 0; k < 3; k++) if (alive[k]) {
277
+ for (int dy = -2; dy <= 2; dy++) for (int dx = -2; dx <= 2; dx++) {
278
+ int d = abs(dx) + abs(dy); if (d == 0) continue;
279
+ int x = ux[k] + dx, y = uy[k] + dy;
280
+ if (!openc(x, y)) continue;
281
+ danger[ID(x, y)] -= 0.25f;
282
+ }
283
+ }
284
+ for (int c = 0; c < S * S; c++) if (danger[c] < 0.f) danger[c] = 0.f;
285
+
286
+ // 局部兵力:敌人(非携旗)与队友在 3 步内的数量,用于避免落单送死
287
+ vector<int> enN(S * S, 0), allySplash(S * S, 0);
288
+ for (auto& e : en) {
289
+ if (e.flag >= 0) continue;
290
+ for (int dy = -3; dy <= 3; dy++) for (int dx = -3; dx <= 3; dx++) {
291
+ if (abs(dx) + abs(dy) > 3) continue;
292
+ int x = e.x + dx, y = e.y + dy;
293
+ if (openc(x, y)) enN[ID(x, y)]++;
294
+ }
295
+ }
296
+ for (int k = 0; k < 3; k++) if (alive[k]) {
297
+ for (int dy = -3; dy <= 3; dy++) for (int dx = -3; dx <= 3; dx++) {
298
+ if (abs(dx) + abs(dy) > 3) continue;
299
+ int x = ux[k] + dx, y = uy[k] + dy;
300
+ if (openc(x, y)) allySplash[ID(x, y)]++;
301
+ }
302
+ }
303
+
304
+ // ---- 可见旗子
305
+ struct AF { int x, y, id, dh; };
306
+ vector<AF> af; vector<vector<int>> fdist;
307
+ for (auto& f : g.flags) if ((f.status == "home" || f.status == "dropped") && f.return_at != T) {
308
+ af.push_back({f.x, f.y, f.id, dB[ME][ID(f.x, f.y)]});
309
+ fdist.push_back(bfsCells({{f.x, f.y}}));
310
+ }
311
+ int naf = af.size();
312
+
313
+ // ---- 角色分配
314
+ int role[3], parm[3], parm2[3];
315
+ for (int k = 0; k < 3; k++) { role[k] = alive[k] ? R_IDLE : R_DEAD; parm[k] = parm2[k] = -1; }
316
+ for (int k = 0; k < 3; k++) if (alive[k] && uflag[k] >= 0) role[k] = R_CARRY;
317
+
318
+ // 抢旗:代价 = 我的往返时间 - 竞争优势
319
+ struct PR { float cost; int u, f, margin; };
320
+ vector<PR> prs;
321
+ int centerD = dB[ME][ID(C0.first, C0.second)];
322
+ vector<int> enNear(naf, INT_MAX); // 敌方到旗的最短距离
323
+ for (int i = 0; i < naf; i++) for (auto& e : en) {
324
+ int d2 = fdist[i][ID(e.x, e.y)];
325
+ if (d2 < enNear[i]) enNear[i] = d2;
326
+ }
327
+ for (int k = 0; k < 3; k++) if (alive[k] && role[k] == R_IDLE) {
328
+ for (int i = 0; i < naf; i++) {
329
+ int d1 = fdist[i][ID(ux[k], uy[k])]; if (d1 == INT_MAX) continue;
330
+ int myT = d1 + af[i].dh;
331
+ if (T + myT + 2 > total) continue;
332
+ int enT = INT_MAX;
333
+ for (auto& e : en) {
334
+ int d2 = fdist[i][ID(e.x, e.y)]; if (d2 == INT_MAX) continue;
335
+ int v = d2 + dB[e.team][ID(af[i].x, af[i].y)];
336
+ if (v < enT) enT = v;
337
+ }
338
+ if (uhp[k] <= 34 && enNear[i] <= 4) continue; // 残血且敌人贴身:不拾旗
339
+ if (uhp[k] <= 34 && af[i].dh > centerD * 2 / 3 && enNear[i] <= 6) continue; // 残血只跑家门口
340
+ int margin = (enT == INT_MAX) ? 8 : enT - myT;
341
+ // 多人局:比我更近的敌方单位不超过 1 个、或旗离家近、或总时间不吃亏,就去抢
342
+ int closerLim = max(1, N / 3); // 人多时排进最近几名就有机会
343
+ int closer = 0;
344
+ for (auto& e : en) if (e.flag < 0 && fdist[i][ID(e.x, e.y)] < d1) closer++;
345
+ bool homeZone = af[i].dh <= centerD * 2 / 3;
346
+ if (N >= 5 && af[i].dh > centerD * 3 / 4) continue; // 大图:太远的旗运不回来
347
+ if (margin < -8 && !(homeZone || (N >= 3 && closer <= closerLim))) continue;
348
+ prs.push_back({(float)(myT - 0.6 * max(min(margin, 8), -8) - (homeZone ? 3 : 0)) + (MAXHP - uhp[k]) * 0.3f, k, i, margin});
349
+ }
350
+ }
351
+ sort(prs.begin(), prs.end(), [](const PR& a, const PR& b) { return a.cost < b.cost; });
352
+ vector<char> fTaken(naf, 0);
353
+ int fMargin[3]; bool supported[3];
354
+ for (int k = 0; k < 3; k++) { fMargin[k] = 1000; supported[k] = false; }
355
+ for (auto& p : prs) if (role[p.u] == R_IDLE && !fTaken[p.f]) { role[p.u] = R_FETCH; parm[p.u] = p.f; fTaken[p.f] = 1; fMargin[p.u] = p.margin; }
356
+
357
+ vector<int> left;
358
+ for (int k = 0; k < 3; k++) if (alive[k] && role[k] == R_IDLE) left.push_back(k);
359
+
360
+ // 专职旗点看守(多人局):最近的空闲单位站上我的旗点,旗一刷新立刻拾取
361
+ if (!left.empty() && N >= 3) {
362
+ int mySpotIdx = 0, bd0 = INT_MAX;
363
+ for (int j = 0; j < (int)spotsG.size(); j++) {
364
+ int d = dB[ME][ID(spotsG[j].first, spotsG[j].second)];
365
+ if (d < bd0) { bd0 = d; mySpotIdx = j; }
366
+ }
367
+ bool fetcherThere = false;
368
+ for (int i = 0; i < naf; i++)
369
+ if (fTaken[i] && af[i].x == spotsG[mySpotIdx].first && af[i].y == spotsG[mySpotIdx].second) fetcherThere = true;
370
+ int bi = 0, bdu = INT_MAX;
371
+ for (int i = 0; i < (int)left.size(); i++) {
372
+ int d = md(ux[left[i]], uy[left[i]], spotsG[mySpotIdx].first, spotsG[mySpotIdx].second);
373
+ if (d < bdu) { bdu = d; bi = i; }
374
+ }
375
+ // 旗点已有抢旗队友时改守中心(第二近的刷新点)
376
+ int sitIdx = mySpotIdx;
377
+ if (fetcherThere) {
378
+ int bc2 = -1, bdc = INT_MAX;
379
+ for (int j = 0; j < (int)spotsG.size(); j++) {
380
+ int d = dB[ME][ID(spotsG[j].first, spotsG[j].second)];
381
+ if (d < bdc && d > bd0) { bdc = d; bc2 = j; }
382
+ }
383
+ if (bc2 >= 0) sitIdx = bc2;
384
+ }
385
+ role[left[bi]] = R_CAMP; parm[left[bi]] = sitIdx; left.erase(left.begin() + bi);
386
+ }
387
+
388
+ // 清理基地附近的敌人(不清掉会打断交旗)
389
+ {
390
+ vector<int> campers;
391
+ for (auto& e : en) if (dB[ME][ID(e.x, e.y)] <= 2) campers.push_back(e.id);
392
+ vector<char> camTaken(campers.size(), 0);
393
+ while (!left.empty()) {
394
+ int bi = -1, bj = -1, bd = INT_MAX;
395
+ for (int i = 0; i < (int)left.size(); i++) for (int j = 0; j < (int)campers.size(); j++) {
396
+ if (camTaken[j]) continue;
397
+ int d = md(ux[left[i]], uy[left[i]], g.units[campers[j]].x, g.units[campers[j]].y);
398
+ if (d < bd) { bd = d; bi = i; bj = j; }
399
+ }
400
+ if (bi < 0) break;
401
+ role[left[bi]] = R_CLEAR; parm[left[bi]] = campers[bj]; camTaken[bj] = 1;
402
+ left.erase(left.begin() + bi);
403
+ }
404
+ }
405
+
406
+ // 护送优先于低价值抢旗:没空闲单位时,抽调代价最高的抢旗手来护送
407
+ for (int cs = 0; cs < 3 && left.empty() && N >= 3; cs++) if (alive[cs] && uflag[cs] >= 0) {
408
+ int bk = -1; float bcost = -1e9;
409
+ for (int k = 0; k < 3; k++) if (role[k] == R_FETCH) {
410
+ float c = (float)fMargin[k] + af[parm[k]].dh * 0.3f; // 优势越小、旗越远越优先被抽
411
+ if (c > bcost) { bcost = c; bk = k; }
412
+ }
413
+ if (bk >= 0) { left.push_back(bk); role[bk] = R_IDLE; fTaken[parm[bk]] = 0; }
414
+ }
415
+
416
+ // 护送携旗队友:大图一律双护卫(护卫跟在携旗手旁,被打死时就地接棒),
417
+ // 空闲单位不够就从最不划算的抢旗任务里抽调
418
+ auto stealFetcher = [&]() -> bool {
419
+ int bk = -1; float bcost = -1e9;
420
+ for (int k = 0; k < 3; k++) if (role[k] == R_FETCH) {
421
+ float c = (float)fMargin[k] + af[parm[k]].dh * 0.3f;
422
+ if (c > bcost) { bcost = c; bk = k; }
423
+ }
424
+ if (bk >= 0) { left.push_back(bk); role[bk] = R_IDLE; fTaken[parm[bk]] = 0; return true; }
425
+ return false;
426
+ };
427
+ for (int cs = 0; cs < 3; cs++) if (alive[cs] && uflag[cs] >= 0) {
428
+ int nearE = INT_MAX, threat = 0;
429
+ for (auto& e : en) {
430
+ int d = md(e.x, e.y, ux[cs], uy[cs]);
431
+ if (d < nearE) nearE = d;
432
+ if (d <= 4) threat++;
433
+ }
434
+ if (nearE > (N <= 2 ? 8 : 12)) continue;
435
+ int need = (N >= 5) ? 2 : (threat >= 2 ? 2 : 1);
436
+ int got = 0;
437
+ while (got < need && (!left.empty() || (N >= 3 && stealFetcher()))) {
438
+ int bi = 0, bd = INT_MAX;
439
+ for (int i = 0; i < (int)left.size(); i++) { int d = md(ux[left[i]], uy[left[i]], ux[cs], uy[cs]); if (d < bd) { bd = d; bi = i; } }
440
+ role[left[bi]] = R_ESCORT; parm[left[bi]] = cs; left.erase(left.begin() + bi);
441
+ got++;
442
+ }
443
+ }
444
+
445
+ // 支援被争夺的抢旗队友(敌人比队友晚到不足 6 步就算有争夺)
446
+ while (!left.empty()) {
447
+ int bk = -1; int bestc = INT_MAX;
448
+ for (int k = 0; k < 3; k++) if (role[k] == R_FETCH && !supported[k]) {
449
+ int d1 = fdist[parm[k]][ID(ux[k], uy[k])];
450
+ int contest = (enNear[parm[k]] == INT_MAX) ? INT_MAX : enNear[parm[k]] - d1;
451
+ if (contest < bestc && contest < 6) { bestc = contest; bk = k; }
452
+ }
453
+ if (bk < 0) break;
454
+ supported[bk] = 1;
455
+ int bi = 0, bd = INT_MAX;
456
+ for (int i = 0; i < (int)left.size(); i++) { int d = md(ux[left[i]], uy[left[i]], ux[bk], uy[bk]); if (d < bd) { bd = d; bi = i; } }
457
+ // 支援者跟到旗附近待命,帮抢也帮护
458
+ role[left[bi]] = R_SUPPORT; parm[left[bi]] = bk; parm2[left[bi]] = parm[bk]; left.erase(left.begin() + bi);
459
+ }
460
+
461
+ // 拦截敌方携旗手:在其回家路径上找能提前到位的伏击点(仅少人局)
462
+ for (int li = 0; li < (int)left.size(); ) {
463
+ int k = left[li];
464
+ int bestPrey = -1, bestCell = -1; float bestAdv = 1e9f;
465
+ if (N > 6) { li++; continue; }
466
+ for (auto& e : en) {
467
+ if (e.flag < 0) continue;
468
+ int home = dB[e.team][ID(e.x, e.y)]; if (home < 3) continue;
469
+ int allies = 0;
470
+ for (auto& a : en) if (a.id != e.id && a.team == e.team && a.flag < 0 && md(a.x, a.y, e.x, e.y) <= 2) allies++;
471
+ if (allies >= 2) continue;
472
+ vector<int> myD = bfsCells({{ux[k], uy[k]}});
473
+ for (int c = 0; c < S * S; c++) {
474
+ if (myD[c] == INT_MAX) continue;
475
+ int dp = dB[e.team][c]; if (dp < 2 || dp >= home) continue;
476
+ float adv = (float)myD[c] + dp - home;
477
+ if (adv < bestAdv) { bestAdv = adv; bestPrey = e.id; bestCell = c; }
478
+ }
479
+ }
480
+ if (bestPrey >= 0 && bestAdv <= -1.f) { role[k] = R_HUNT; parm[k] = bestPrey; parm2[k] = bestCell; left.erase(left.begin() + li); }
481
+ else li++;
482
+ }
483
+
484
+ // 守旗点(站上去就能第一时间拾取)。之后的空闲单位在大图做机会型抢旗
485
+ if (!left.empty()) {
486
+ vector<char> spotTaken(spotsG.size(), 0);
487
+ int nCamp = 0;
488
+ for (int li = 0; li < (int)left.size(); li++) {
489
+ int k = left[li];
490
+ if (nCamp >= 1 && N >= 5) {
491
+ int bi = -1, bc = INT_MAX;
492
+ for (int i = 0; i < naf; i++) {
493
+ if (fTaken[i] || af[i].dh > centerD * 3 / 4) continue;
494
+ int d1 = fdist[i][ID(ux[k], uy[k])];
495
+ if (d1 == INT_MAX || T + d1 + af[i].dh + 2 > total) continue;
496
+ if (d1 + af[i].dh < bc) { bc = d1 + af[i].dh; bi = i; }
497
+ }
498
+ if (bi >= 0) { role[k] = R_FETCH; parm[k] = bi; parm2[k] = bi; fTaken[bi] = 1; fMargin[k] = 0; supported[k] = true; continue; }
499
+ }
500
+ int bj = -1, bs = INT_MAX;
501
+ for (int j = 0; j < (int)spotsG.size(); j++) {
502
+ if (spotTaken[j]) continue;
503
+ int dhome = dB[ME][ID(spotsG[j].first, spotsG[j].second)];
504
+ if (dhome > centerD + 2) continue; // 不守太远的旗点
505
+ bool fetcherGoing = false;
506
+ for (int i = 0; i < naf; i++) if (fTaken[i] && af[i].x == spotsG[j].first && af[i].y == spotsG[j].second) fetcherGoing = true;
507
+ if (fetcherGoing) continue;
508
+ int sc = 2 * dhome + md(ux[k], uy[k], spotsG[j].first, spotsG[j].second);
509
+ if (sc < bs) { bs = sc; bj = j; }
510
+ }
511
+ if (bj >= 0) { role[k] = R_CAMP; parm[k] = bj; spotTaken[bj] = 1; nCamp++; }
512
+ else if (dB[ME][ID(ux[k], uy[k])] > 3) { role[k] = R_CAMP; parm[k] = -1; }
513
+ }
514
+ }
515
+
516
+ // 近距离追杀敌方携旗手:3 步内的携旗敌人直接追(它跑不掉,保持 2 格距离就能每回合命中)
517
+ for (int k = 0; k < 3; k++) {
518
+ if (!alive[k] || uflag[k] >= 0 || role[k] == R_CARRY || role[k] == R_CLEAR || N > 6) continue;
519
+ int bc = -1, bdd = 4;
520
+ for (auto& e : en) if (e.flag >= 0) {
521
+ int d = md(ux[k], uy[k], e.x, e.y);
522
+ if (d < bdd) { bdd = d; bc = ID(e.x, e.y); }
523
+ }
524
+ if (bc >= 0) {
525
+ bool onFlag = false;
526
+ for (auto& f : g.flags) if ((f.status == "home" || f.status == "dropped") && f.return_at != T && f.x == ux[k] && f.y == uy[k]) onFlag = true;
527
+ if (!onFlag) { role[k] = R_HUNT; parm[k] = -1; parm2[k] = bc; }
528
+ }
529
+ }
530
+
531
+ // ---- 移动规划(按角色优先级)
532
+ int order3[3] = {-1, -1, -1}; int oi = 0;
533
+ for (int r = R_CARRY; r <= R_IDLE; r++) for (int k = 0; k < 3; k++) if (role[k] == r) order3[oi++] = k;
534
+ set<int> of;
535
+ for (int k = 0; k < 3; k++) if (alive[k]) of.insert(ID(ux[k], uy[k]));
536
+ int nx[3], ny[3];
537
+ for (int k = 0; k < 3; k++) { nx[k] = ux[k]; ny[k] = uy[k]; }
538
+
539
+ vector<float> D;
540
+ for (int o = 0; o < 3; o++) {
541
+ int k = order3[o];
542
+ if (k < 0 || !alive[k] || role[k] == R_IDLE || role[k] == R_DEAD) continue;
543
+ vector<pair<int,int>> targs; float wD; int allow = -1;
544
+ switch (role[k]) {
545
+ case R_CARRY: targs = myBase9; wD = 2.2f; break;
546
+ case R_FETCH: {
547
+ int fx = af[parm[k]].x, fy = af[parm[k]].y;
548
+ int enF = 0, alF = 0;
549
+ for (auto& e : en) if (md(e.x, e.y, fx, fy) <= 3) enF++;
550
+ for (int j = 0; j < 3; j++) if (alive[j] && j != k && md(ux[j], uy[j], fx, fy) <= 3) alF++;
551
+ if (enF >= alF + 2) {
552
+ // 敌众我寡明显:不冲上去送死,占 2 格火力位射击靠近者
553
+ for (int dy = -2; dy <= 2; dy++) for (int dx = -2; dx <= 2; dx++) {
554
+ int d = abs(dx) + abs(dy);
555
+ if (d == 0 || d > 2) continue;
556
+ int x = fx + dx, y = fy + dy;
557
+ if (openc(x, y) && canAtk(x, y, fx, fy)) targs.push_back({x, y});
558
+ }
559
+ if (targs.empty()) targs.push_back({fx, fy});
560
+ } else {
561
+ targs = {{fx, fy}};
562
+ allow = ID(fx, fy);
563
+ }
564
+ wD = 1.1f; break;
565
+ }
566
+ case R_CLEAR: {
567
+ Unit& e = g.units[parm[k]];
568
+ for (int dy = -2; dy <= 2; dy++) for (int dx = -2; dx <= 2; dx++) {
569
+ int x = e.x + dx, y = e.y + dy;
570
+ if (openc(x, y) && canAtk(x, y, e.x, e.y)) targs.push_back({x, y});
571
+ }
572
+ wD = 0.8f; break;
573
+ }
574
+ case R_SUPPORT: {
575
+ int p = parm[k];
576
+ if (md(ux[k], uy[k], ux[p], uy[p]) <= 1) targs.push_back({ux[k], uy[k]});
577
+ for (int dy = -1; dy <= 1; dy++) for (int dx = -1; dx <= 1; dx++) {
578
+ if (abs(dx) + abs(dy) != 1) continue;
579
+ int x = ux[p] + dx, y = uy[p] + dy;
580
+ if (openc(x, y)) targs.push_back({x, y});
581
+ }
582
+ wD = 1.0f; break;
583
+ }
584
+ case R_ESCORT: {
585
+ int p = parm[k];
586
+ if (md(ux[k], uy[k], ux[p], uy[p]) <= 1) targs.push_back({ux[k], uy[k]});
587
+ for (int dy = -1; dy <= 1; dy++) for (int dx = -1; dx <= 1; dx++) {
588
+ if (abs(dx) + abs(dy) != 1) continue;
589
+ int x = ux[p] + dx, y = uy[p] + dy;
590
+ if (openc(x, y)) targs.push_back({x, y});
591
+ }
592
+ wD = role[k] == R_SUPPORT ? 1.0f : 0.8f; break;
593
+ }
594
+ case R_HUNT: targs = {{parm2[k] % S, parm2[k] / S}}; wD = 0.7f; break;
595
+ case R_CAMP:
596
+ if (parm[k] >= 0) targs = {spotsG[parm[k]]};
597
+ else targs = myBase9;
598
+ wD = 1.3f; break;
599
+ default: continue;
600
+ }
601
+ if (targs.empty()) continue;
602
+ // 残血也照常作战(死亡很便宜,10 回合满血重生);携旗者才惜命
603
+ float thr = (role[k] == R_CARRY) ? 0.45f * uhp[k] : max(0.85f * uhp[k], 0.6f * MAXHP);
604
+ float imbW = (role[k] == R_CARRY) ? 1.0f : (role[k] == R_FETCH || role[k] == R_CAMP) ? 0.7f : 0.25f;
605
+ auto ldanger = [&](int c) -> float {
606
+ float imb = (float)enN[c] - ((float)allySplash[c] - (md(ux[k], uy[k], c % S, c / S) <= 3 ? 1.f : 0.f));
607
+ return danger[c] + imbW * min(max(imb, 0.f), 2.f);
608
+ };
609
+ D.assign(S * S, 1e18f);
610
+ priority_queue<pair<float,int>, vector<pair<float,int>>, greater<pair<float,int>>> pq;
611
+ for (auto& p : targs) { int c = ID(p.first, p.second); if (openc(p.first, p.second) && D[c] > 0) { D[c] = 0.f; pq.push({0.f, c}); } }
612
+ auto pen = [&](int c) -> bool { return 34.f * ldanger(c) >= thr; };
613
+ while (!pq.empty()) {
614
+ pair<float,int> pr = pq.top(); pq.pop();
615
+ float d = pr.first; int c = pr.second;
616
+ if (d > D[c] + 1e-6f) continue;
617
+ int x = c % S, y = c / S;
618
+ for (int i = 0; i < 4; i++) {
619
+ int ax = x + DX[i], ay = y + DY[i];
620
+ if (!openc(ax, ay)) continue;
621
+ int ac = ID(ax, ay);
622
+ float cost = 1.f + wD * ldanger(ac) + ((pen(ac) && ac != allow) ? 30.f : 0.f);
623
+ if (d + cost < D[ac] - 1e-6f) { D[ac] = d + cost; pq.push({D[ac], ac}); }
624
+ }
625
+ }
626
+ int cur = ID(ux[k], uy[k]);
627
+ if (D[cur] <= 0.f || D[cur] > 1e17f) continue; // 已在目标 / 不可达:原地
628
+ int best = -1; float bestv = 1e18f;
629
+ for (int i = 0; i < 4; i++) {
630
+ int ax = ux[k] + DX[i], ay = uy[k] + DY[i];
631
+ if (!openc(ax, ay)) continue;
632
+ int ac = ID(ax, ay);
633
+ if (of.count(ac)) continue; // 被队友占用/认领
634
+ bool enemyOn = false;
635
+ for (auto& e : en) if (e.x == ax && e.y == ay) { enemyOn = true; break; }
636
+ if (enemyOn) continue;
637
+ float v = 1.f + wD * ldanger(ac) + ((pen(ac) && ac != allow) ? 30.f : 0.f) + D[ac];
638
+ if (v < bestv) { bestv = v; best = ac; }
639
+ }
640
+ if (best < 0) continue;
641
+ if (pen(best) && !pen(cur) && ldanger(cur) < ldanger(best)) continue; // 不踏进致命格,先等一步
642
+ of.erase(cur); of.insert(best);
643
+ nx[k] = best % S; ny[k] = best / S;
644
+ }
645
+
646
+ // ---- 动作
647
+ string out = to_string(T);
648
+ for (int k = 0; k < 3; k++) {
649
+ string mv = "S", act = "-";
650
+ if (alive[k]) {
651
+ int ddx = nx[k] - ux[k], ddy = ny[k] - uy[k];
652
+ if (ddx == 1) mv = "R"; else if (ddx == -1) mv = "L"; else if (ddy == 1) mv = "D"; else if (ddy == -1) mv = "U";
653
+ if (uflag[k] < 0) {
654
+ bool onF = false;
655
+ for (auto& f : g.flags) if ((f.status == "home" || f.status == "dropped") && f.return_at != T && f.x == nx[k] && f.y == ny[k]) onF = true;
656
+ if (onF) act = "P";
657
+ else if (!respNow[k]) {
658
+ int bestT = -1; float bs = -1e9;
659
+ for (auto& e : en) {
660
+ if (!canAtk(nx[k], ny[k], e.x, e.y)) continue;
661
+ float s = 0;
662
+ if (e.flag >= 0) s += 100; // 敌方携旗手
663
+ if (dB[ME][ID(e.x, e.y)] <= 2) s += 80; // 在我基地附近
664
+ for (auto& f : g.flags) if ((f.status == "home" || f.status == "dropped") && f.return_at != T && f.x == e.x && f.y == e.y) s += 90; // 站在旗上
665
+ for (int j = 0; j < 3; j++) if (alive[j] && uflag[j] >= 0 && md(e.x, e.y, nx[j], ny[j]) <= 3) s += 50;
666
+ int hits = 0;
667
+ for (int j = 0; j < 3; j++) if (alive[j] && uflag[j] < 0 && !respNow[j] && canAtk(nx[j], ny[j], e.x, e.y)) hits++;
668
+ if (e.hp <= hits * DMG) s += 55; // 集火可击杀
669
+ s += (MAXHP - e.hp) * 0.25f;
670
+ if (s > bs) { bs = s; bestT = e.id; }
671
+ }
672
+ if (bestT >= 0) act = to_string(bestT);
673
+ }
674
+ }
675
+ }
676
+ out += " " + mv + " " + act;
677
+ }
678
+ static const char* rname[9] = {"cy","fe","cl","su","es","hu","ca","id","xx"};
679
+ string note = "#";
680
+ for (int k = 0; k < 3; k++) { if (k) note += ";"; note += rname[role[k] < 9 ? role[k] : 8]; }
681
+ out += " " + note;
682
+ return out;
683
+ }
684
+
685
+ int main() {
686
+ ios::sync_with_stdio(false);
687
+ Game g;
688
+ while (read_turn(g)) cout << decide(g) << endl;
689
+ }