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,194 @@
1
+ // Anchor bot "greedy": fixed-pattern rectangle loops, ignores the opponents.
2
+ #include <bits/stdc++.h>
3
+ using namespace std;
4
+
5
+ const int DX[4] = {0, 0, -1, 1};
6
+ const int DY[4] = {-1, 1, 0, 0};
7
+ const char DC[4] = {'U', 'D', 'L', 'R'};
8
+
9
+ int dirIndex(char c) { return string("UDLR").find(c); }
10
+
11
+ struct Player {
12
+ int x, y, dir, trailLen, area, deaths;
13
+ };
14
+
15
+ struct State {
16
+ int turn;
17
+ vector<Player> p;
18
+ vector<string> owner; // owner[y][x]: '.' neutral, '0'..'3' territory of that player
19
+ vector<string> trail; // trail[y][x]: '.' none, '0'..'3' trail of that player
20
+ };
21
+
22
+ int W, H, MAX_TURNS, N, ME, MOVE_MS, INIT_MS;
23
+ vector<pair<int, int>> spawn;
24
+
25
+ bool readState(State& s) {
26
+ string tok;
27
+ if (!(cin >> tok)) return false; // "TURN"
28
+ cin >> s.turn;
29
+ s.p.assign(N, {});
30
+ for (int i = 0; i < N; i++) {
31
+ int id;
32
+ char d;
33
+ Player q;
34
+ cin >> tok >> id >> q.x >> q.y >> d >> q.trailLen >> q.area >> q.deaths;
35
+ q.dir = dirIndex(d);
36
+ s.p[id] = q;
37
+ }
38
+ cin >> tok; // OWNER
39
+ s.owner.assign(H, "");
40
+ for (auto& row : s.owner) cin >> row;
41
+ cin >> tok; // TRAIL
42
+ s.trail.assign(H, "");
43
+ for (auto& row : s.trail) cin >> row;
44
+ cin >> tok; // END
45
+ return true;
46
+ }
47
+
48
+ // Greedy loop maker: leave the territory, draw the largest rectangle it can find,
49
+ // return home by the shortest safe path. Ignores the opponents, except that it
50
+ // always takes an immediate chance to cut an opponent's trail.
51
+ mt19937 rng(20260925);
52
+ deque<int> plan;
53
+ bool wasOutside = false;
54
+
55
+ bool inb(int x, int y) { return x >= 0 && x < W && y >= 0 && y < H; }
56
+
57
+ // BFS from the head (no reversing) through cells that are not our own trail.
58
+ // Returns the direction path to the nearest cell satisfying goal(x, y); empty if none.
59
+ template <class F>
60
+ vector<int> bfsPath(const State& s, F goal) {
61
+ const Player& me = s.p[ME];
62
+ char myTrail = '0' + ME;
63
+ vector<int> from(W * H, -1), fdir(W * H, -1);
64
+ deque<int> q;
65
+ int start = me.y * W + me.x;
66
+ from[start] = start;
67
+ for (int d = 0; d < 4; d++) {
68
+ if (d == (me.dir ^ 1)) continue;
69
+ int nx = me.x + DX[d], ny = me.y + DY[d];
70
+ if (!inb(nx, ny) || s.trail[ny][nx] == myTrail) continue;
71
+ int k = ny * W + nx;
72
+ if (from[k] != -1) continue;
73
+ from[k] = start;
74
+ fdir[k] = d;
75
+ q.push_back(k);
76
+ }
77
+ while (!q.empty()) {
78
+ int k = q.front();
79
+ q.pop_front();
80
+ int x = k % W, y = k / W;
81
+ if (goal(x, y)) {
82
+ vector<int> path;
83
+ for (int c = k; c != start; c = from[c]) path.push_back(fdir[c]);
84
+ reverse(path.begin(), path.end());
85
+ return path;
86
+ }
87
+ for (int d = 0; d < 4; d++) {
88
+ int nx = x + DX[d], ny = y + DY[d];
89
+ if (!inb(nx, ny) || s.trail[ny][nx] == myTrail) continue;
90
+ int nk = ny * W + nx;
91
+ if (from[nk] != -1) continue;
92
+ from[nk] = k;
93
+ fdir[nk] = d;
94
+ q.push_back(nk);
95
+ }
96
+ }
97
+ return {};
98
+ }
99
+
100
+ void makePlan(const State& s) {
101
+ char mine = '0' + ME;
102
+ vector<int> path = bfsPath(s, [&](int x, int y) { return s.owner[y][x] != mine; });
103
+ if (path.empty()) return;
104
+ int ex = s.p[ME].x, ey = s.p[ME].y;
105
+ for (int d : path) ex += DX[d], ey += DY[d];
106
+ int d = path.back();
107
+ int best = -1, ba = 0, bb = 0, bs = 0;
108
+ for (int it = 0; it < 24; it++) {
109
+ int a = 3 + rng() % 6, b = 3 + rng() % 6;
110
+ int side = (d < 2) ? 2 + rng() % 2 : rng() % 2; // perpendicular to d
111
+ int fx = ex + DX[d] * (a - 1), fy = ey + DY[d] * (a - 1);
112
+ int cx = fx + DX[side] * b, cy = fy + DY[side] * b;
113
+ if (!inb(fx, fy) || !inb(cx, cy)) continue;
114
+ int gain = 0;
115
+ for (int y = min(ey, cy); y <= max(ey, cy); y++)
116
+ for (int x = min(ex, cx); x <= max(ex, cx); x++)
117
+ if (s.owner[y][x] != mine) gain++;
118
+ if (gain > best) best = gain, ba = a, bb = b, bs = side;
119
+ }
120
+ plan.assign(path.begin(), path.end());
121
+ if (best < 0) return;
122
+ for (int i = 0; i < ba - 1; i++) plan.push_back(d);
123
+ for (int i = 0; i < bb; i++) plan.push_back(bs);
124
+ for (int i = 0; i < ba - 1; i++) plan.push_back(d ^ 1);
125
+ }
126
+
127
+ int anySafe(const State& s) {
128
+ const Player& me = s.p[ME];
129
+ char myTrail = '0' + ME;
130
+ for (int d : {me.dir, 0, 1, 2, 3}) {
131
+ if (d == (me.dir ^ 1)) continue;
132
+ int nx = me.x + DX[d], ny = me.y + DY[d];
133
+ if (inb(nx, ny) && s.trail[ny][nx] != myTrail) return d;
134
+ }
135
+ return me.dir;
136
+ }
137
+
138
+ int decide(const State& s) {
139
+ const Player& me = s.p[ME];
140
+ char mine = '0' + ME;
141
+ bool inside = s.owner[me.y][me.x] == mine;
142
+ if (inside && wasOutside) plan.clear(); // just captured or respawned
143
+ wasOutside = !inside;
144
+
145
+ for (int d = 0; d < 4; d++) {
146
+ if (d == (me.dir ^ 1)) continue;
147
+ int nx = me.x + DX[d], ny = me.y + DY[d];
148
+ if (inb(nx, ny) && s.trail[ny][nx] != '.' && s.trail[ny][nx] != mine) { // any opponent's trail
149
+ plan.clear();
150
+ return d;
151
+ }
152
+ }
153
+ if (plan.empty()) {
154
+ if (inside) makePlan(s);
155
+ else {
156
+ vector<int> home = bfsPath(s, [&](int x, int y) { return s.owner[y][x] == mine; });
157
+ if (!home.empty()) return home[0];
158
+ return anySafe(s);
159
+ }
160
+ }
161
+ if (!plan.empty()) {
162
+ int d = plan.front();
163
+ int nx = me.x + DX[d], ny = me.y + DY[d];
164
+ if (d != (me.dir ^ 1) && inb(nx, ny) && s.trail[ny][nx] != mine) {
165
+ plan.pop_front();
166
+ return d;
167
+ }
168
+ plan.clear();
169
+ if (!inside) {
170
+ vector<int> home = bfsPath(s, [&](int x, int y) { return s.owner[y][x] == mine; });
171
+ if (!home.empty()) return home[0];
172
+ }
173
+ }
174
+ return anySafe(s);
175
+ }
176
+
177
+ int main() {
178
+ ios::sync_with_stdio(false);
179
+ cin.tie(nullptr);
180
+ string tok;
181
+ cin >> tok; // INIT
182
+ cin >> W >> H >> MAX_TURNS >> N >> ME >> MOVE_MS >> INIT_MS;
183
+ spawn.resize(N);
184
+ for (int i = 0; i < N; i++) {
185
+ int id, x, y;
186
+ cin >> tok >> id >> x >> y;
187
+ spawn[id] = {x, y};
188
+ }
189
+ State s;
190
+ while (readState(s)) {
191
+ int d = decide(s);
192
+ cout << s.turn << ' ' << DC[d] << endl; // endl flushes: required every turn
193
+ }
194
+ }
@@ -0,0 +1,80 @@
1
+ // Anchor bot "random": a uniformly random move among those that do not hit a wall or its own trail.
2
+ #include <bits/stdc++.h>
3
+ using namespace std;
4
+
5
+ const int DX[4] = {0, 0, -1, 1};
6
+ const int DY[4] = {-1, 1, 0, 0};
7
+ const char DC[4] = {'U', 'D', 'L', 'R'};
8
+
9
+ int dirIndex(char c) { return string("UDLR").find(c); }
10
+
11
+ struct Player {
12
+ int x, y, dir, trailLen, area, deaths;
13
+ };
14
+
15
+ struct State {
16
+ int turn;
17
+ vector<Player> p;
18
+ vector<string> owner; // owner[y][x]: '.' neutral, '0'..'3' territory of that player
19
+ vector<string> trail; // trail[y][x]: '.' none, '0'..'3' trail of that player
20
+ };
21
+
22
+ int W, H, MAX_TURNS, N, ME, MOVE_MS, INIT_MS;
23
+ vector<pair<int, int>> spawn;
24
+
25
+ bool readState(State& s) {
26
+ string tok;
27
+ if (!(cin >> tok)) return false; // "TURN"
28
+ cin >> s.turn;
29
+ s.p.assign(N, {});
30
+ for (int i = 0; i < N; i++) {
31
+ int id;
32
+ char d;
33
+ Player q;
34
+ cin >> tok >> id >> q.x >> q.y >> d >> q.trailLen >> q.area >> q.deaths;
35
+ q.dir = dirIndex(d);
36
+ s.p[id] = q;
37
+ }
38
+ cin >> tok; // OWNER
39
+ s.owner.assign(H, "");
40
+ for (auto& row : s.owner) cin >> row;
41
+ cin >> tok; // TRAIL
42
+ s.trail.assign(H, "");
43
+ for (auto& row : s.trail) cin >> row;
44
+ cin >> tok; // END
45
+ return true;
46
+ }
47
+
48
+ mt19937 rng(12345);
49
+
50
+ int decide(const State& s) {
51
+ const Player& me = s.p[ME];
52
+ char myTrail = '0' + ME;
53
+ vector<int> ok;
54
+ for (int d = 0; d < 4; d++) {
55
+ if (d == (me.dir ^ 1)) continue;
56
+ int nx = me.x + DX[d], ny = me.y + DY[d];
57
+ if (nx >= 0 && nx < W && ny >= 0 && ny < H && s.trail[ny][nx] != myTrail) ok.push_back(d);
58
+ }
59
+ if (ok.empty()) return me.dir;
60
+ return ok[rng() % ok.size()];
61
+ }
62
+
63
+ int main() {
64
+ ios::sync_with_stdio(false);
65
+ cin.tie(nullptr);
66
+ string tok;
67
+ cin >> tok; // INIT
68
+ cin >> W >> H >> MAX_TURNS >> N >> ME >> MOVE_MS >> INIT_MS;
69
+ spawn.resize(N);
70
+ for (int i = 0; i < N; i++) {
71
+ int id, x, y;
72
+ cin >> tok >> id >> x >> y;
73
+ spawn[id] = {x, y};
74
+ }
75
+ State s;
76
+ while (readState(s)) {
77
+ int d = decide(s);
78
+ cout << s.turn << ' ' << DC[d] << endl; // endl flushes: required every turn
79
+ }
80
+ }
@@ -0,0 +1,64 @@
1
+ """Development timer; never infer the unpack time from file mtime.
2
+ Explicit organizer start is preferred. Legacy birthtime/ctime is frozen once.
3
+ """
4
+ import argparse
5
+ import datetime
6
+ import json
7
+ import os
8
+ import platform
9
+ import time
10
+ from pathlib import Path
11
+
12
+ MARKER = '.development-clock.json'
13
+
14
+ def boot_id():
15
+ try:return Path('/proc/sys/kernel/random/boot_id').read_text().strip()
16
+ except OSError:return None
17
+
18
+ def initialize(root, explicit=False, wall=None, monotonic=None):
19
+ root=Path(root);marker=root/MARKER
20
+ if marker.exists():return json.loads(marker.read_text(encoding='utf-8'))
21
+ now=time.time();mono=time.monotonic()
22
+ if explicit:
23
+ start=now if wall is None else wall
24
+ start_mono=mono if monotonic is None else monotonic
25
+ source='explicit_start'
26
+ else:
27
+ st=root.stat();start=getattr(st,'st_birthtime',None) or st.st_ctime
28
+ source='directory_birthtime' if getattr(st,'st_birthtime',None) or os.name=='nt' else 'directory_ctime_estimate'
29
+ start_mono=mono-max(0,now-start)
30
+ state=dict(version=1,start_unix=start,start_monotonic=start_mono,source=source,
31
+ host=platform.node(),system=platform.system(),boot_id=boot_id(),boot_epoch=now-mono,
32
+ suggested_seconds=3600,hard_stop_seconds=5400)
33
+ try:
34
+ with marker.open('x',encoding='utf-8') as f:json.dump(state,f,indent=2)
35
+ except FileExistsError:return json.loads(marker.read_text(encoding='utf-8'))
36
+ return state
37
+
38
+ def read_clock(root, explicit=False):
39
+ state=initialize(root,explicit);now=time.time();mono=time.monotonic();current_boot=boot_id()
40
+ same_host=state['host']==platform.node() and state['system']==platform.system()
41
+ same_boot=(state['boot_id']==current_boot if state['boot_id'] and current_boot
42
+ else abs((now-mono)-state['boot_epoch'])<120)
43
+ use_mono=same_host and same_boot and mono>=state['start_monotonic']
44
+ elapsed=max(0,mono-state['start_monotonic'] if use_mono else now-state['start_unix'])
45
+ return dict(now_utc=datetime.datetime.fromtimestamp(now,datetime.timezone.utc).isoformat(),
46
+ start_utc=datetime.datetime.fromtimestamp(state['start_unix'],datetime.timezone.utc).isoformat(),
47
+ elapsed_seconds=round(elapsed,3),remaining_seconds=round(max(0,5400-elapsed),3),
48
+ hard_stop_reached=elapsed>=5400,source=state['source'],
49
+ measurement='monotonic' if use_mono else 'persisted_UTC_fallback',
50
+ approximate_start=state['source']=='directory_ctime_estimate')
51
+
52
+ def main():
53
+ ap=argparse.ArgumentParser();ap.add_argument('--start',action='store_true',help='start once; never resets an existing timer')
54
+ ap.add_argument('--json',action='store_true');args=ap.parse_args()
55
+ data=read_clock(Path(__file__).resolve().parent,args.start)
56
+ if args.json:print(json.dumps(data,ensure_ascii=True,indent=2));return
57
+ print('Current UTC: '+data['now_utc'])
58
+ print('Elapsed: %.1f min | Remaining until 90-minute deadline: %.1f min | Suggested: 60 min.' %
59
+ (data['elapsed_seconds']/60,data['remaining_seconds']/60))
60
+ print('Start: %s | Clock: %s | Source: %s' % (data['start_utc'],data['measurement'],data['source']))
61
+ if data['approximate_start']:print('NOTE: legacy Linux ctime start is approximate; prefer the organizer unpack launcher.')
62
+ if data['hard_stop_reached']:print('HARD STOP REACHED: submit the current version now.')
63
+
64
+ if __name__=='__main__':main()
@@ -0,0 +1,33 @@
1
+ """Compile a bot with the official flags. The compiler is taken from $CXX (default g++)."""
2
+ import hashlib
3
+ import os
4
+ import subprocess
5
+ import sys
6
+
7
+ FLAGS = ['-O2', '-std=c++20']
8
+ if sys.platform == 'win32':
9
+ FLAGS.append('-static')
10
+
11
+
12
+ def compile_bot(src, cache_dir=None):
13
+ """Compile src (.cpp) and return the executable path. Rebuilds only when the source changes."""
14
+ src = os.path.abspath(src)
15
+ cache_dir = cache_dir or os.path.join(os.path.dirname(src), '.build')
16
+ os.makedirs(cache_dir, exist_ok=True)
17
+ with open(src, 'rb') as f:
18
+ digest = hashlib.sha1(f.read()).hexdigest()[:12]
19
+ name = os.path.splitext(os.path.basename(src))[0] + '_' + digest
20
+ exe = os.path.join(cache_dir, name + ('.exe' if sys.platform == 'win32' else ''))
21
+ if not os.path.exists(exe):
22
+ cmd = [os.environ.get('CXX', 'g++')] + FLAGS + ['-o', exe, src]
23
+ r = subprocess.run(cmd, capture_output=True, text=True)
24
+ if r.returncode != 0:
25
+ raise RuntimeError('compile failed: %s\n%s' % (' '.join(cmd), r.stderr[-4000:]))
26
+ return exe
27
+
28
+
29
+ def resolve(path, cache_dir=None):
30
+ """Accept a .cpp (compiled on demand) or an executable."""
31
+ if path.endswith('.cpp'):
32
+ return compile_bot(path, cache_dir)
33
+ return os.path.abspath(path)
@@ -0,0 +1,273 @@
1
+ """Territory game (圈地) rules engine.
2
+
3
+ Pure Python, no dependencies, fully deterministic. This file is the single
4
+ source of truth for the rules; RULES.md describes the same behaviour in prose.
5
+ """
6
+ import random
7
+
8
+ DIRS = {'U': (0, -1), 'D': (0, 1), 'L': (-1, 0), 'R': (1, 0)}
9
+ OPPOSITE = {'U': 'D', 'D': 'U', 'L': 'R', 'R': 'L'}
10
+
11
+ DEFAULT_CONFIG = {
12
+ 'width': 40,
13
+ 'height': 40,
14
+ 'max_turns': 600,
15
+ 'start_radius': 2, # initial territory: (2r+1)x(2r+1) square around spawn
16
+ 'protect_radius': 1, # protected zone: (2r+1)x(2r+1) square, can never be captured
17
+ 'spawn_margin': 4, # min distance from spawn to the border
18
+ 'min_spawn_dist': 16, # min Manhattan distance between any two spawns
19
+ 'players': 2,
20
+ }
21
+
22
+ # 4 人混战:正方形棋盘,四个出生点互为 90° 旋转
23
+ MELEE_CONFIG = dict(DEFAULT_CONFIG, width=48, height=48, players=4, spawn_margin=5)
24
+
25
+ ROT90 = {'U': 'R', 'R': 'D', 'D': 'L', 'L': 'U'} # clockwise
26
+
27
+
28
+ def config_for(players):
29
+ return MELEE_CONFIG if players == 4 else DEFAULT_CONFIG
30
+
31
+
32
+ def make_start(seed, cfg=DEFAULT_CONFIG):
33
+ """Spawns and initial directions for a seed.
34
+ 2 players: player 1 is player 0 rotated 180 degrees.
35
+ 4 players: player k is player 0 rotated k x 90 degrees clockwise (square board)."""
36
+ rng = random.Random(seed)
37
+ W, H, m = cfg['width'], cfg['height'], cfg['spawn_margin']
38
+ if cfg.get('players', 2) == 4:
39
+ assert W == H
40
+ while True:
41
+ x, y = rng.randint(m, W - 1 - m), rng.randint(m, H - 1 - m)
42
+ pts = [(x, y)]
43
+ for _ in range(3):
44
+ px, py = pts[-1]
45
+ pts.append((W - 1 - py, px))
46
+ if all(abs(a[0] - b[0]) + abs(a[1] - b[1]) >= cfg['min_spawn_dist']
47
+ for i, a in enumerate(pts) for b in pts[i + 1:]):
48
+ break
49
+ d = rng.choice('UDLR')
50
+ dirs = [d]
51
+ for _ in range(3):
52
+ dirs.append(ROT90[dirs[-1]])
53
+ return pts, dirs
54
+ while True:
55
+ x, y = rng.randint(m, W - 1 - m), rng.randint(m, H - 1 - m)
56
+ x2, y2 = W - 1 - x, H - 1 - y
57
+ if abs(x - x2) + abs(y - y2) >= cfg['min_spawn_dist']:
58
+ break
59
+ d = rng.choice('UDLR')
60
+ return [(x, y), (x2, y2)], [d, OPPOSITE[d]]
61
+
62
+
63
+ class Game:
64
+ def __init__(self, cfg, spawns, dirs):
65
+ self.cfg = cfg
66
+ self.W, self.H = cfg['width'], cfg['height']
67
+ self.N = len(spawns)
68
+ n = self.W * self.H
69
+ self.owner = [-1] * n # territory owner per cell
70
+ self.trail = [-1] * n # trail owner per cell
71
+ self.protected = [-1] * n # protected-zone owner per cell
72
+ self.spawns = list(spawns)
73
+ self.init_dirs = list(dirs)
74
+ self.pos = list(spawns)
75
+ self.dir = list(dirs)
76
+ self.trails = [[] for _ in range(self.N)] # ordered trail cell indices
77
+ self.deaths = [0] * self.N
78
+ self.kills = [0] * self.N
79
+ self.out = [False] * self.N # 被淘汰(程序崩溃或错误过多)的玩家:停在出生点,不再行动
80
+ self.turn = 0
81
+ for i, (sx, sy) in enumerate(spawns):
82
+ r = cfg['start_radius']
83
+ for y in range(sy - r, sy + r + 1):
84
+ for x in range(sx - r, sx + r + 1):
85
+ self.owner[y * self.W + x] = i
86
+ r = cfg['protect_radius']
87
+ for y in range(sy - r, sy + r + 1):
88
+ for x in range(sx - r, sx + r + 1):
89
+ self.protected[y * self.W + x] = i
90
+
91
+ def over(self):
92
+ return self.turn >= self.cfg['max_turns']
93
+
94
+ def areas(self):
95
+ a = [0] * self.N
96
+ for o in self.owner:
97
+ if o >= 0:
98
+ a[o] += 1
99
+ return a
100
+
101
+ def eliminate(self, i):
102
+ """Remove a forfeited player from play (melee only): its trail is cleared, it stays
103
+ on its spawn and never moves again. Its territory stays on the board."""
104
+ for k in self.trails[i]:
105
+ self.trail[k] = -1
106
+ self.trails[i] = []
107
+ self.pos[i] = self.spawns[i]
108
+ self.out[i] = True
109
+
110
+ def _enclosed(self, i):
111
+ """Cells not owned by i and not on i's trail that cannot reach the border
112
+ without crossing i's territory or trail."""
113
+ W, H = self.W, self.H
114
+ owner, trail = self.owner, self.trail
115
+ wall = bytearray(W * H)
116
+ for k in range(W * H):
117
+ if owner[k] == i or trail[k] == i:
118
+ wall[k] = 1
119
+ seen = bytearray(W * H)
120
+ stack = []
121
+ for x in range(W):
122
+ for k in (x, (H - 1) * W + x):
123
+ if not wall[k] and not seen[k]:
124
+ seen[k] = 1
125
+ stack.append(k)
126
+ for y in range(H):
127
+ for k in (y * W, y * W + W - 1):
128
+ if not wall[k] and not seen[k]:
129
+ seen[k] = 1
130
+ stack.append(k)
131
+ while stack:
132
+ k = stack.pop()
133
+ x = k % W
134
+ for nk, ok in ((k - W, k >= W), (k + W, k < W * (H - 1)),
135
+ (k - 1, x > 0), (k + 1, x < W - 1)):
136
+ if ok and not wall[nk] and not seen[nk]:
137
+ seen[nk] = 1
138
+ stack.append(nk)
139
+ return [k for k in range(W * H) if not wall[k] and not seen[k]]
140
+
141
+ def step(self, moves):
142
+ """Advance one turn. moves[i] is 'U'/'D'/'L'/'R' or None (keep direction).
143
+ Returns a frame dict describing what changed (used for replays)."""
144
+ W, H, N = self.W, self.H, self.N
145
+ dirs, new = [], []
146
+ dead = [None] * N # (reason, other_player)
147
+
148
+ # 1. intended moves; reversing or invalid input keeps the current direction
149
+ for i in range(N):
150
+ d = self.dir[i]
151
+ m = moves[i]
152
+ if self.out[i]:
153
+ dirs.append(d)
154
+ new.append(None)
155
+ continue
156
+ if m in DIRS and m != OPPOSITE[d]:
157
+ d = m
158
+ dirs.append(d)
159
+ x, y = self.pos[i]
160
+ dx, dy = DIRS[d]
161
+ nx, ny = x + dx, y + dy
162
+ if 0 <= nx < W and 0 <= ny < H:
163
+ new.append(ny * W + nx)
164
+ else:
165
+ new.append(None)
166
+ dead[i] = ('wall', None)
167
+
168
+ # 2. head-on collisions (same target cell, or swapping cells)
169
+ cur = [py * W + px for px, py in self.pos]
170
+ for i in range(N):
171
+ for j in range(i + 1, N):
172
+ if new[i] is None or new[j] is None:
173
+ continue
174
+ if new[i] == new[j] or (new[i] == cur[j] and new[j] == cur[i]):
175
+ dead[i] = dead[i] or ('head', j)
176
+ dead[j] = dead[j] or ('head', i)
177
+
178
+ # 3. trail cuts: entering a trail cell kills the trail's owner
179
+ for i in range(N):
180
+ if new[i] is None:
181
+ continue
182
+ c = self.trail[new[i]]
183
+ if c != -1 and dead[c] is None:
184
+ dead[c] = ('self', i) if c == i else ('cut', i)
185
+
186
+ frame = {'events': [], 'owner': [], 'trail_clear': [], 'trail_add': []}
187
+
188
+ # 4. deaths: drop trail, teleport to spawn, keep territory
189
+ for i in range(N):
190
+ if dead[i] is None:
191
+ continue
192
+ for k in self.trails[i]:
193
+ self.trail[k] = -1
194
+ self.trails[i] = []
195
+ self.pos[i] = self.spawns[i]
196
+ self.dir[i] = self.init_dirs[i]
197
+ self.deaths[i] += 1
198
+ reason, by = dead[i]
199
+ if reason == 'cut':
200
+ self.kills[by] += 1
201
+ frame['events'].append({'type': 'death', 'player': i, 'reason': reason, 'by': by})
202
+ frame['trail_clear'].append(i)
203
+
204
+ # 5. survivors move
205
+ capturers = []
206
+ for i in range(N):
207
+ if dead[i] is not None or self.out[i]:
208
+ continue
209
+ k = new[i]
210
+ self.dir[i] = dirs[i]
211
+ self.pos[i] = (k % W, k // W)
212
+ if self.owner[k] == i:
213
+ if self.trails[i]:
214
+ capturers.append(i)
215
+ else:
216
+ self.trails[i].append(k)
217
+ self.trail[k] = i
218
+ frame['trail_add'].append([i, k])
219
+
220
+ # 6. captures, computed on the pre-capture board and applied together
221
+ if capturers:
222
+ enclosed = {i: self._enclosed(i) for i in capturers}
223
+ trail_cells = {}
224
+ for i in capturers:
225
+ for k in self.trails[i]:
226
+ trail_cells[k] = i
227
+ claims = {}
228
+ for i in capturers:
229
+ for k in enclosed[i]:
230
+ claims.setdefault(k, []).append(i)
231
+ gained = {i: 0 for i in capturers}
232
+
233
+ def take(k, i):
234
+ if self.protected[k] not in (-1, i) or self.owner[k] == i:
235
+ return
236
+ self.owner[k] = i
237
+ gained[i] += 1
238
+ frame['owner'].append([k, i])
239
+
240
+ for k, i in trail_cells.items():
241
+ take(k, i)
242
+ for k, cl in claims.items():
243
+ if len(cl) == 1 and k not in trail_cells:
244
+ take(k, cl[0])
245
+ for i in capturers:
246
+ for k in self.trails[i]:
247
+ self.trail[k] = -1
248
+ self.trails[i] = []
249
+ frame['trail_clear'].append(i)
250
+ frame['events'].append({'type': 'capture', 'player': i, 'cells': gained[i]})
251
+
252
+ self.turn += 1
253
+ frame['pos'] = [[x, y, d] for (x, y), d in zip(self.pos, self.dir)]
254
+ return frame
255
+
256
+ def state_text(self, turn):
257
+ """Per-turn message sent to bots (see RULES.md)."""
258
+ areas = self.areas()
259
+ lines = ['TURN %d' % turn]
260
+ for i in range(self.N):
261
+ x, y = self.pos[i]
262
+ lines.append('P %d %d %d %s %d %d %d' % (
263
+ i, x, y, self.dir[i], len(self.trails[i]), areas[i], self.deaths[i]))
264
+ W = self.W
265
+ ch = '.0123456789'
266
+ lines.append('OWNER')
267
+ row = ''.join(ch[o + 1] for o in self.owner)
268
+ lines.extend(row[y * W:(y + 1) * W] for y in range(self.H))
269
+ lines.append('TRAIL')
270
+ row = ''.join(ch[o + 1] for o in self.trail)
271
+ lines.extend(row[y * W:(y + 1) * W] for y in range(self.H))
272
+ lines.append('END')
273
+ return '\n'.join(lines) + '\n'