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.
- package/README.md +91 -13
- package/assets/commit0/instance.json +30 -0
- package/assets/commit0/workspace.zip +0 -0
- package/assets/featurebench/hidden/test_regression.py +690 -0
- package/assets/featurebench/instance.json +16 -0
- package/assets/featurebench/workspace.zip +0 -0
- package/assets/swe-bench-pro/hidden/test.patch +36 -0
- package/assets/swe-bench-pro/instance.json +10 -0
- package/assets/swe-bench-pro/workspace.zip +0 -0
- package/eval/README.md +18 -0
- package/eval/arena/arena/README.md +21 -0
- package/eval/arena/arena/bt.py +104 -0
- package/eval/arena/arena/clock.py +15 -0
- package/eval/arena/arena/core.py +265 -0
- package/eval/arena/arena/run.cmd +24 -0
- package/eval/arena/arena/run.sh +11 -0
- package/eval/arena/arena/schedule.py +79 -0
- package/eval/arena/arena/tournament.py +183 -0
- package/eval/arena/bomberman/bots/README.md +16 -0
- package/eval/arena/bomberman/bots/deepseek-v4.1-flash.cpp +447 -0
- package/eval/arena/bomberman/bots/doubao-seed-2.1.cpp +542 -0
- package/eval/arena/bomberman/bots/glm-5.3.cpp +406 -0
- package/eval/arena/bomberman/bots/gpt-6-astra.cpp +768 -0
- package/eval/arena/bomberman/bots/gpt-6-sol.cpp +444 -0
- package/eval/arena/bomberman/bots/kimi-k3.cpp +432 -0
- package/eval/arena/bomberman/bots/mimo-v2.6-pro.cpp +529 -0
- package/eval/arena/bomberman/bots/opus-5.5.cpp +548 -0
- package/eval/arena/bomberman/bots/qwen-3.8-max.cpp +836 -0
- package/eval/arena/bomberman/tournament/README.md +11 -0
- package/eval/arena/bomberman/tournament/bt.py +56 -0
- package/eval/arena/bomberman/tournament/evaluate.py +199 -0
- package/eval/arena/bomberman/tournament/run.py +79 -0
- package/eval/arena/bomberman/tournament/schedule-2to10.json.gz +0 -0
- package/eval/arena/bomberman/workspace/MANIFEST.json +13 -0
- package/eval/arena/bomberman/workspace/README.md +29 -0
- package/eval/arena/bomberman/workspace/RULES.md +100 -0
- package/eval/arena/bomberman/workspace/baseline.cpp +240 -0
- package/eval/arena/bomberman/workspace/clock.py +25 -0
- package/eval/arena/bomberman/workspace/engine/arena.py +198 -0
- package/eval/arena/bomberman/workspace/engine/build.py +35 -0
- package/eval/arena/bomberman/workspace/engine/engine.py +256 -0
- package/eval/arena/bomberman/workspace/engine/verify_replay.py +24 -0
- package/eval/arena/bomberman/workspace/run.cmd +28 -0
- package/eval/arena/bomberman/workspace/run.sh +16 -0
- package/eval/arena/bomberman/workspace/viewer/index.html +42 -0
- package/eval/arena/ctf/bots/README.md +18 -0
- package/eval/arena/ctf/bots/deepseek-v4.1-flash.cpp +601 -0
- package/eval/arena/ctf/bots/doubao-seed-2.1-turbo.cpp +320 -0
- package/eval/arena/ctf/bots/fable-5.1.cpp +634 -0
- package/eval/arena/ctf/bots/glm-5.3.cpp +689 -0
- package/eval/arena/ctf/bots/gpt-6-astra.cpp +657 -0
- package/eval/arena/ctf/bots/gpt-6.1-sol.cpp +725 -0
- package/eval/arena/ctf/bots/kimi-k3.cpp +667 -0
- package/eval/arena/ctf/bots/mimo-v2.6-pro.cpp +533 -0
- package/eval/arena/ctf/bots/opus-5.5.cpp +645 -0
- package/eval/arena/ctf/bots/qwen-3.8-max.cpp +726 -0
- package/eval/arena/ctf/bots/sonnet-5.5.cpp +569 -0
- package/eval/arena/ctf/tournament/README.md +15 -0
- package/eval/arena/ctf/tournament/formal-2to8.json +31 -0
- package/eval/arena/ctf/tournament/formal-9to15.json +31 -0
- package/eval/arena/ctf/workspace/README.md +41 -0
- package/eval/arena/ctf/workspace/RULES.md +119 -0
- package/eval/arena/ctf/workspace/arena.py +186 -0
- package/eval/arena/ctf/workspace/baseline.cpp +257 -0
- package/eval/arena/ctf/workspace/clock.py +15 -0
- package/eval/arena/ctf/workspace/core.py +265 -0
- package/eval/arena/ctf/workspace/engine.py +389 -0
- package/eval/arena/ctf/workspace/run.cmd +24 -0
- package/eval/arena/ctf/workspace/run.sh +11 -0
- package/eval/arena/ctf/workspace/viewer.html +71 -0
- package/eval/arena/land/bots/README.md +17 -0
- package/eval/arena/land/bots/deepseek-4.1f@workbuddy#1/bot.cpp +608 -0
- package/eval/arena/land/bots/glm-5.3@workbuddy#1/bot.cpp +521 -0
- package/eval/arena/land/bots/gpt-6-astra@codex#1/bot.cpp +373 -0
- package/eval/arena/land/bots/gpt-6-sol@codex#1/bot.cpp +341 -0
- package/eval/arena/land/bots/kimi-k3@workbuddy#1/bot.cpp +492 -0
- package/eval/arena/land/bots/mimo-2.6-pro@zcode#1/bot.cpp +652 -0
- package/eval/arena/land/bots/mystery-model@zcode#1/bot.cpp +466 -0
- package/eval/arena/land/bots/opus-5.5@claude-code#1/bot.cpp +630 -0
- package/eval/arena/land/bots/sonnet-5@claude-code#1/bot.cpp +619 -0
- package/eval/arena/land/tournament/make_replay_page.py +17 -0
- package/eval/arena/land/tournament/rate.py +290 -0
- package/eval/arena/land/tournament/round_robin.py +172 -0
- package/eval/arena/land/tournament/sparring.cpp +356 -0
- package/eval/arena/land/workspace/RULES.md +114 -0
- package/eval/arena/land/workspace/TASK.md +46 -0
- package/eval/arena/land/workspace/anchors/.build/greedy_f2e6d2dea5ca +0 -0
- package/eval/arena/land/workspace/anchors/greedy.cpp +194 -0
- package/eval/arena/land/workspace/anchors/random.cpp +80 -0
- package/eval/arena/land/workspace/clock.py +64 -0
- package/eval/arena/land/workspace/engine/build.py +33 -0
- package/eval/arena/land/workspace/engine/game.py +273 -0
- package/eval/arena/land/workspace/engine/referee.py +308 -0
- package/eval/arena/land/workspace/starter/.build/bot_be32b45b4699 +0 -0
- package/eval/arena/land/workspace/starter/bot.cpp +82 -0
- package/eval/arena/land/workspace/viewer/viewer.html +210 -0
- package/eval/arena/tank/bots/README.md +24 -0
- package/eval/arena/tank/bots/half1/deepseek-v4.1-flash.cpp +367 -0
- package/eval/arena/tank/bots/half1/doubao-seed-evolving.cpp +402 -0
- package/eval/arena/tank/bots/half1/gemini-4-argon.cpp +1132 -0
- package/eval/arena/tank/bots/half1/glm-5.3.cpp +551 -0
- package/eval/arena/tank/bots/half1/gpt-6-astra.cpp +233 -0
- package/eval/arena/tank/bots/half1/gpt-6.1-sol-ultrafast.cpp +350 -0
- package/eval/arena/tank/bots/half1/gpt-6.1-sol.cpp +78 -0
- package/eval/arena/tank/bots/half1/grok-4.7.cpp +1099 -0
- package/eval/arena/tank/bots/half1/haiku-5.5.cpp +210 -0
- package/eval/arena/tank/bots/half1/kimi-k3.cpp +353 -0
- package/eval/arena/tank/bots/half1/mimo-v2.6-pro.cpp +336 -0
- package/eval/arena/tank/bots/half1/muse-wonder-full.cpp +560 -0
- package/eval/arena/tank/bots/half1/opus-5.5.cpp +435 -0
- package/eval/arena/tank/bots/half1/qwen-3.8-max.cpp +537 -0
- package/eval/arena/tank/bots/half2/deepseek-v4.1-flash.cpp +425 -0
- package/eval/arena/tank/bots/half2/doubao-seed-evolving.cpp +308 -0
- package/eval/arena/tank/bots/half2/gemini-4-argon.cpp +1390 -0
- package/eval/arena/tank/bots/half2/glm-5.3.cpp +651 -0
- package/eval/arena/tank/bots/half2/gpt-6-astra.cpp +241 -0
- package/eval/arena/tank/bots/half2/gpt-6.1-sol-ultrafast.cpp +356 -0
- package/eval/arena/tank/bots/half2/gpt-6.1-sol.cpp +87 -0
- package/eval/arena/tank/bots/half2/grok-4.7.cpp +1205 -0
- package/eval/arena/tank/bots/half2/haiku-5.5.cpp +210 -0
- package/eval/arena/tank/bots/half2/kimi-k3.cpp +487 -0
- package/eval/arena/tank/bots/half2/mimo-v2.6-pro.cpp +336 -0
- package/eval/arena/tank/bots/half2/muse-wonder-full.cpp +560 -0
- package/eval/arena/tank/bots/half2/opus-5.5.cpp +476 -0
- package/eval/arena/tank/bots/half2/qwen-3.8-max.cpp +392 -0
- package/eval/arena/tank/tournament/README.md +24 -0
- package/eval/arena/tank/tournament/bt.py +218 -0
- package/eval/arena/tank/tournament/final.json +202 -0
- package/eval/arena/tank/tournament/halftime.json +146 -0
- package/eval/arena/tank/tournament/progress.json +911 -0
- package/eval/arena/tank/tournament/schedule.py +100 -0
- package/eval/arena/tank/tournament/sparring.cpp +928 -0
- package/eval/arena/tank/tournament/tournament.py +1426 -0
- package/eval/arena/tank/workspace/README.md +169 -0
- package/eval/arena/tank/workspace/RULES.md +443 -0
- package/eval/arena/tank/workspace/arena.py +862 -0
- package/eval/arena/tank/workspace/baseline.cpp +259 -0
- package/eval/arena/tank/workspace/clock.py +103 -0
- package/eval/arena/tank/workspace/core.py +712 -0
- package/eval/arena/tank/workspace/engine.py +573 -0
- package/eval/arena/tank/workspace/history/.lock +0 -0
- package/eval/arena/tank/workspace/history/20261017-193610-08715287f91f-bot.cpp +109 -0
- package/eval/arena/tank/workspace/history/20261017-193619-f00b2d5635fa-gpt-6-astra.cpp +241 -0
- package/eval/arena/tank/workspace/history/20261017-193643-63319e0a2b1a-gpt-6_1-sol-ultrafast.cpp +356 -0
- package/eval/arena/tank/workspace/history/20261017-193647-41c1341d0a8a-gemini-4-argon.cpp +1390 -0
- package/eval/arena/tank/workspace/history/20261017-193652-e34c0f45539a-gpt-6_1-sol.cpp +87 -0
- package/eval/arena/tank/workspace/history/20261017-193654-c026348ce96e-opus-5_5.cpp +476 -0
- package/eval/arena/tank/workspace/history/20261017-193655-4a21366e94d1-muse-wonder-full.cpp +560 -0
- package/eval/arena/tank/workspace/history/20261017-193657-dbc4e9969c52-mimo-v2_6-pro.cpp +336 -0
- package/eval/arena/tank/workspace/history/20261017-193703-723cd036eb31-grok-4_7.cpp +1205 -0
- package/eval/arena/tank/workspace/history/20261017-193707-1ebbb227be68-deepseek-v4_1-flash.cpp +425 -0
- package/eval/arena/tank/workspace/history/20261017-193709-aeb0e7202ad5-doubao-seed-evolving.cpp +308 -0
- package/eval/arena/tank/workspace/history/20261017-193710-b21ebeb9bc76-kimi-k3.cpp +487 -0
- package/eval/arena/tank/workspace/history/20261017-193712-54cd86531905-haiku-5_5.cpp +210 -0
- package/eval/arena/tank/workspace/history/20261017-193714-efdb8e35c0bf-qwen-3_8-max.cpp +392 -0
- package/eval/arena/tank/workspace/history/20261017-193715-681423c23fdb-glm-5_3.cpp +651 -0
- package/eval/arena/tank/workspace/history/20261017-193717-1a8b3c75181d-sparring.cpp +928 -0
- package/eval/arena/tank/workspace/history/20261017-193720-1e0803a541e8-baseline.cpp +259 -0
- package/eval/arena/tank/workspace/history/log.jsonl +103 -0
- package/eval/arena/tank/workspace/judge/cycles.hpp +53 -0
- package/eval/arena/tank/workspace/judge/fast.cpp +337 -0
- package/eval/arena/tank/workspace/judge/referee.cpp +125 -0
- package/eval/arena/tank/workspace/judge/speed.cpp +99 -0
- package/eval/arena/tank/workspace/run.cmd +31 -0
- package/eval/arena/tank/workspace/run.sh +23 -0
- package/eval/arena/tank/workspace/tank.hpp +1762 -0
- package/eval/arena/tank/workspace/tank_main.cpp +215 -0
- package/eval/arena/tank/workspace/viewer.html +1150 -0
- package/eval/arena/warehouse/workspace/SPEC.md +130 -0
- package/eval/arena/warehouse/workspace/TASK.md +71 -0
- package/eval/arena/warehouse/workspace/baseline/common.h +88 -0
- package/eval/arena/warehouse/workspace/baseline/main.cpp +27 -0
- package/eval/arena/warehouse/workspace/baseline/old_stuff.cpp +87 -0
- package/eval/arena/warehouse/workspace/baseline/sim.cpp +427 -0
- package/eval/arena/warehouse/workspace/baseline/util.cpp +220 -0
- package/eval/arena/warehouse/workspace/clock.py +42 -0
- package/eval/arena/warehouse/workspace/src/common.h +88 -0
- package/eval/arena/warehouse/workspace/src/main.cpp +27 -0
- package/eval/arena/warehouse/workspace/src/old_stuff.cpp +87 -0
- package/eval/arena/warehouse/workspace/src/sim.cpp +427 -0
- package/eval/arena/warehouse/workspace/src/util.cpp +220 -0
- package/eval/arena/warehouse/workspace/tools/bench.py +119 -0
- package/eval/arena/warehouse/workspace/tools/check_input.py +161 -0
- package/eval/arena/warehouse/workspace/tools/gen.py +132 -0
- package/eval/arena/warehouse/workspace/tools/measure.py +107 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_crowded.in +1420 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_crowded.out +9162 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_dynamic.in +1465 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_dynamic.out +3439 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_flood.in +2757 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_flood.out +3126 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_small.in +731 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_small.out +2578 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_wide.in +528 -0
- package/eval/arena/warehouse/workspace/workloads/public/pub_wide.out +2026 -0
- package/eval/bomberman.py +20 -0
- package/eval/commit0.py +21 -0
- package/eval/common.py +61 -0
- package/eval/ctf.py +20 -0
- package/eval/featurebench.py +25 -0
- package/eval/land.py +20 -0
- package/eval/legacy/bomberman-prices.json +4 -0
- package/eval/legacy/bomberman.py +161 -0
- package/eval/legacy/commit0.py +91 -0
- package/eval/legacy/ctf-prices.json +4 -0
- package/eval/legacy/ctf.py +178 -0
- package/eval/legacy/featurebench.py +103 -0
- package/eval/legacy/land-prices.json +4 -0
- package/eval/legacy/land.py +177 -0
- package/eval/legacy/swe_bench_pro.py +116 -0
- package/eval/legacy/tank-prices.json +3 -0
- package/eval/legacy/tank.py +408 -0
- package/eval/legacy/warehouse-prices.json +4 -0
- package/eval/legacy/warehouse.py +365 -0
- package/eval/swe-bench-pro.py +25 -0
- package/eval/tank.py +20 -0
- package/eval/warehouse.py +20 -0
- package/lib/client/index.js +376 -63
- package/lib/client/index.js.map +1 -1
- package/lib/index.js +684 -27
- package/package.json +2 -1
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// util.cpp 工具函数
|
|
2
|
+
#include "common.h"
|
|
3
|
+
|
|
4
|
+
int DX4[4] = {0, 1, 0, -1};
|
|
5
|
+
int DY4[4] = {-1, 0, 1, 0}; // 上 右 下 左 !!!顺序不能改!!!
|
|
6
|
+
|
|
7
|
+
vector<string> SPLIT(const string& s) {
|
|
8
|
+
vector<string> res;
|
|
9
|
+
string cur = "";
|
|
10
|
+
for (int i = 0; i < (int)s.size(); i++) {
|
|
11
|
+
char c = s[i];
|
|
12
|
+
if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f') {
|
|
13
|
+
if (cur != "") {
|
|
14
|
+
res.push_back(cur);
|
|
15
|
+
cur = "";
|
|
16
|
+
}
|
|
17
|
+
} else {
|
|
18
|
+
cur = cur + c;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (cur != "") res.push_back(cur);
|
|
22
|
+
return res;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
int S2I(const string& s) {
|
|
26
|
+
return atoi(s.c_str());
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
string I2S(long long v) {
|
|
30
|
+
stringstream ss;
|
|
31
|
+
ss << v;
|
|
32
|
+
return ss.str();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
string RNAME(int i) {
|
|
36
|
+
return "R" + I2S(i);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
bool INMAP(int x, int y) {
|
|
40
|
+
if (x < 0) return false;
|
|
41
|
+
if (y < 0) return false;
|
|
42
|
+
if (x >= W) return false;
|
|
43
|
+
if (y >= H) return false;
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
bool ISWALL(int x, int y) {
|
|
48
|
+
return MAP[y][x] == '#';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 能不能走(在图内 不是墙 没封)
|
|
52
|
+
bool OK(int x, int y) {
|
|
53
|
+
if (!INMAP(x, y)) return false;
|
|
54
|
+
if (ISWALL(x, y)) return false;
|
|
55
|
+
if (BLK.find(make_pair(x, y)) != BLK.end()) return false;
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 格子上有没有别的车(死了的不算)
|
|
60
|
+
bool OCC(int x, int y, int except) {
|
|
61
|
+
for (int i = 0; i < NR; i++) {
|
|
62
|
+
if (i == except) continue;
|
|
63
|
+
if (RST[i] == "DEAD") continue;
|
|
64
|
+
if (RX[i] == x && RY[i] == y) return true;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// BFS 距离表 d[y][x] 起点不管能不能走都是 0
|
|
70
|
+
vector<vector<int> > BFS(int sx, int sy) {
|
|
71
|
+
#if FLAG_FAST_BFS
|
|
72
|
+
return bfs_fast_cached(sx, sy);
|
|
73
|
+
#endif
|
|
74
|
+
vector<vector<int> > d;
|
|
75
|
+
for (int y = 0; y < H; y++) {
|
|
76
|
+
vector<int> row;
|
|
77
|
+
for (int x = 0; x < W; x++) row.push_back(BIGNUM);
|
|
78
|
+
d.push_back(row);
|
|
79
|
+
}
|
|
80
|
+
queue<pair<int, int> > q;
|
|
81
|
+
d[sy][sx] = 0;
|
|
82
|
+
q.push(make_pair(sx, sy));
|
|
83
|
+
while (q.size() > 0) {
|
|
84
|
+
pair<int, int> p = q.front();
|
|
85
|
+
q.pop();
|
|
86
|
+
for (int k = 0; k < 4; k++) {
|
|
87
|
+
int nx = p.first + DX4[k];
|
|
88
|
+
int ny = p.second + DY4[k];
|
|
89
|
+
if (OK(nx, ny) == false) continue;
|
|
90
|
+
if (d[ny][nx] != BIGNUM) continue;
|
|
91
|
+
d[ny][nx] = d[p.second][p.first] + 1;
|
|
92
|
+
q.push(make_pair(nx, ny));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return d;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// BFS2: 给移动用的(从目标往回搜)。张: 和 BFS 一样,但是别合并,上次合并出过事
|
|
99
|
+
vector<vector<int> > BFS2(int sx, int sy) {
|
|
100
|
+
vector<vector<int> > d(H, vector<int>(W, BIGNUM));
|
|
101
|
+
list<pair<int, int> > q;
|
|
102
|
+
d[sy][sx] = 0;
|
|
103
|
+
q.push_back(make_pair(sx, sy));
|
|
104
|
+
while (!q.empty()) {
|
|
105
|
+
int cx = q.front().first, cy = q.front().second;
|
|
106
|
+
q.pop_front();
|
|
107
|
+
for (int k = 0; k < 4; k++) {
|
|
108
|
+
int nx = cx + DX4[k], ny = cy + DY4[k];
|
|
109
|
+
if (!OK(nx, ny)) continue;
|
|
110
|
+
if (d[ny][nx] < BIGNUM) continue;
|
|
111
|
+
d[ny][nx] = d[cy][cx] + 1;
|
|
112
|
+
q.push_back(make_pair(nx, ny));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return d;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// a 到 b 的距离
|
|
119
|
+
int DIST(int ax, int ay, int bx, int by) {
|
|
120
|
+
if (ax == bx && ay == by) return 0;
|
|
121
|
+
if (!OK(bx, by)) return BIGNUM;
|
|
122
|
+
vector<vector<int> > d = BFS(ax, ay);
|
|
123
|
+
return d[by][bx];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
vector<pair<int, int> > ALL_CHG() {
|
|
127
|
+
vector<pair<int, int> > v;
|
|
128
|
+
for (int y = 0; y < H; y++) {
|
|
129
|
+
for (int x = 0; x < W; x++) {
|
|
130
|
+
if (MAP[y][x] == 'C') v.push_back(make_pair(x, y));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return v;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 到最近充电桩的距离(2023-03 张: 从 sim.cpp 挪过来的,BFS 先内联着,回头再改)
|
|
137
|
+
int CHG_DIST(int x, int y) {
|
|
138
|
+
vector<vector<int> > d(H, vector<int>(W, BIGNUM));
|
|
139
|
+
queue<pair<int, int> > q;
|
|
140
|
+
d[y][x] = 0;
|
|
141
|
+
q.push(make_pair(x, y));
|
|
142
|
+
while (!q.empty()) {
|
|
143
|
+
pair<int, int> p = q.front();
|
|
144
|
+
q.pop();
|
|
145
|
+
for (int k = 0; k < 4; k++) {
|
|
146
|
+
int nx = p.first + DX4[k];
|
|
147
|
+
int ny = p.second + DY4[k];
|
|
148
|
+
if (!INMAP(nx, ny)) continue;
|
|
149
|
+
if (MAP[ny][nx] == '#') continue;
|
|
150
|
+
if (BLK.count(make_pair(nx, ny))) continue;
|
|
151
|
+
if (d[ny][nx] != BIGNUM) continue;
|
|
152
|
+
d[ny][nx] = d[p.second][p.first] + 1;
|
|
153
|
+
q.push(p);
|
|
154
|
+
q.back() = make_pair(nx, ny);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
int best = BIGNUM;
|
|
158
|
+
vector<pair<int, int> > cs = ALL_CHG();
|
|
159
|
+
for (int i = 0; i < (int)cs.size(); i++) {
|
|
160
|
+
if (!OK(cs[i].first, cs[i].second)) continue;
|
|
161
|
+
if (d[cs[i].second][cs[i].first] < best) best = d[cs[i].second][cs[i].first];
|
|
162
|
+
}
|
|
163
|
+
return best;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 最近的充电桩,没有就 (-1,-1)
|
|
167
|
+
pair<int, int> CHG_NEAR(int x, int y) {
|
|
168
|
+
vector<vector<int> > d = BFS(x, y);
|
|
169
|
+
int best = BIGNUM;
|
|
170
|
+
pair<int, int> res = make_pair(-1, -1);
|
|
171
|
+
vector<pair<int, int> > cs = ALL_CHG();
|
|
172
|
+
for (int i = 0; i < (int)cs.size(); i++) {
|
|
173
|
+
if (!OK(cs[i].first, cs[i].second)) continue;
|
|
174
|
+
if (d[cs[i].second][cs[i].first] < best) {
|
|
175
|
+
best = d[cs[i].second][cs[i].first];
|
|
176
|
+
res = cs[i];
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return res;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
int findRobot(const string& name) {
|
|
183
|
+
for (int i = 0; i < NR; i++) {
|
|
184
|
+
if (RNAME(i) == name) return i;
|
|
185
|
+
}
|
|
186
|
+
return -1;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// 写日志
|
|
190
|
+
void writeLog(int t, const string& msg) {
|
|
191
|
+
string line = I2S(t) + " " + msg;
|
|
192
|
+
LOGBUF.push_back(line);
|
|
193
|
+
// 顺便统计一下,省得到处加
|
|
194
|
+
vector<string> w = SPLIT(msg);
|
|
195
|
+
if (w.size() > 0) {
|
|
196
|
+
if (w[0] == "DELIVER") {
|
|
197
|
+
long long lat = S2I(w[w.size() - 1]);
|
|
198
|
+
CNT[0]++;
|
|
199
|
+
CNT[4] += lat;
|
|
200
|
+
if (lat > CNT[5]) CNT[5] = lat;
|
|
201
|
+
} else if (w[0] == "LOST") {
|
|
202
|
+
CNT[1]++;
|
|
203
|
+
} else if (w[0] == "REJECT") {
|
|
204
|
+
CNT[2]++;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
#if FLAG_VERBOSE
|
|
208
|
+
cerr << line << endl;
|
|
209
|
+
#endif
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
void writeRaw(const string& msg) {
|
|
213
|
+
LOGBUF.push_back(msg);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
void dumpLog() {
|
|
217
|
+
for (int i = 0; i < (int)LOGBUF.size(); i++) {
|
|
218
|
+
cout << LOGBUF[i] << endl;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""本地测试工具:编译 src/,运行公开负载,逐字节比对输出,报告 CPU 时间。
|
|
2
|
+
|
|
3
|
+
python tools/bench.py # 编译并测试全部公开负载
|
|
4
|
+
python tools/bench.py --baseline # 同时报告加速比。基线耗时第一次实测(约 40 秒),之后用缓存
|
|
5
|
+
python tools/bench.py --baseline --fresh # 忽略缓存,重新测量基线耗时
|
|
6
|
+
python tools/bench.py --only pub_small --repeat 3
|
|
7
|
+
|
|
8
|
+
Build and run all public workloads, compare output byte by byte, report CPU time.
|
|
9
|
+
"""
|
|
10
|
+
import argparse
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
16
|
+
ROOT = os.path.dirname(HERE)
|
|
17
|
+
sys.path.insert(0, HERE)
|
|
18
|
+
import measure # noqa: E402
|
|
19
|
+
if hasattr(sys.stdout, 'reconfigure'):
|
|
20
|
+
sys.stdout.reconfigure(encoding='utf-8') # Windows 控制台默认 GBK,统一输出 UTF-8
|
|
21
|
+
|
|
22
|
+
WORKLOADS = os.path.join(ROOT, 'workloads', 'public')
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
CACHE = os.path.join(ROOT, 'build', 'baseline_times.json')
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def baseline_stamp():
|
|
29
|
+
d = os.path.join(ROOT, 'baseline')
|
|
30
|
+
return ';'.join('%s:%d:%d' % (f, os.path.getsize(os.path.join(d, f)), int(os.path.getmtime(os.path.join(d, f))))
|
|
31
|
+
for f in sorted(os.listdir(d)))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def load_cache():
|
|
35
|
+
"""基线不会变,测过一次的耗时就记下来;基线代码或负载文件有变化时自动重测。"""
|
|
36
|
+
try:
|
|
37
|
+
with open(CACHE, encoding='utf-8') as f:
|
|
38
|
+
data = json.load(f)
|
|
39
|
+
return data['times'] if data.get('stamp') == baseline_stamp() else {}
|
|
40
|
+
except (OSError, ValueError, KeyError):
|
|
41
|
+
return {}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def save_cache(times):
|
|
45
|
+
os.makedirs(os.path.dirname(CACHE), exist_ok=True)
|
|
46
|
+
with open(CACHE, 'w', encoding='utf-8') as f:
|
|
47
|
+
json.dump({'stamp': baseline_stamp(), 'times': times}, f, indent=1)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def first_diff(a, b):
|
|
51
|
+
la, lb = measure.normalized(a).split(b'\n'), measure.normalized(b).split(b'\n')
|
|
52
|
+
for i in range(max(len(la), len(lb))):
|
|
53
|
+
x = la[i] if i < len(la) else b'<EOF>'
|
|
54
|
+
y = lb[i] if i < len(lb) else b'<EOF>'
|
|
55
|
+
if x != y:
|
|
56
|
+
return i + 1, x.decode(errors='replace'), y.decode(errors='replace')
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def main():
|
|
61
|
+
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
62
|
+
ap.add_argument('--baseline', action='store_true', help='同时测量基线耗时并计算加速比 / also time baseline/')
|
|
63
|
+
ap.add_argument('--only', help='只测某个负载 / only this workload')
|
|
64
|
+
ap.add_argument('--repeat', type=int, default=1, help='每个负载运行次数,取中位数 / runs per workload (median)')
|
|
65
|
+
ap.add_argument('--fresh', action='store_true', help='忽略缓存,重新测量基线耗时 / re-time baseline')
|
|
66
|
+
args = ap.parse_args()
|
|
67
|
+
|
|
68
|
+
names = sorted(f[:-3] for f in os.listdir(WORKLOADS) if f.endswith('.in'))
|
|
69
|
+
if args.only:
|
|
70
|
+
names = [n for n in names if n == args.only]
|
|
71
|
+
exe = measure.build(os.path.join(ROOT, 'src'), os.path.join(ROOT, 'build', 'sim' + measure.EXE))
|
|
72
|
+
base = None
|
|
73
|
+
if args.baseline: # 基线代码不会变,编译过就不再重新编译
|
|
74
|
+
bsrc, base = os.path.join(ROOT, 'baseline'), os.path.join(ROOT, 'build', 'baseline' + measure.EXE)
|
|
75
|
+
newest = max(os.path.getmtime(os.path.join(bsrc, f)) for f in os.listdir(bsrc))
|
|
76
|
+
if not os.path.exists(base) or os.path.getmtime(base) < newest:
|
|
77
|
+
measure.build(bsrc, base)
|
|
78
|
+
cache = load_cache() if base else {}
|
|
79
|
+
|
|
80
|
+
all_ok = True
|
|
81
|
+
total, total_base = 0.0, 0.0
|
|
82
|
+
print('%-14s %-6s %10s %10s %9s %s' % ('负载', '结果', 'CPU秒', '基线CPU秒', '加速比', '内存MB'))
|
|
83
|
+
for n in names:
|
|
84
|
+
inp = os.path.join(WORKLOADS, n + '.in')
|
|
85
|
+
with open(os.path.join(WORKLOADS, n + '.out'), 'rb') as f:
|
|
86
|
+
expected = f.read()
|
|
87
|
+
runs = [measure.run(exe, inp) for _ in range(args.repeat)]
|
|
88
|
+
runs.sort(key=lambda r: r['cpu'])
|
|
89
|
+
r = runs[len(runs) // 2]
|
|
90
|
+
ok = all(x['digest'] == measure.digest(expected) and x['code'] == 0 for x in runs)
|
|
91
|
+
all_ok &= ok
|
|
92
|
+
total += r['cpu']
|
|
93
|
+
bcpu = ''
|
|
94
|
+
speed = ''
|
|
95
|
+
if base:
|
|
96
|
+
key = '%s|%d|%d' % (n, os.path.getsize(inp), int(os.path.getmtime(inp)))
|
|
97
|
+
if args.fresh or key not in cache:
|
|
98
|
+
cache[key] = measure.run(base, inp)['cpu']
|
|
99
|
+
save_cache(cache)
|
|
100
|
+
total_base += cache[key]
|
|
101
|
+
bcpu = '%.3f' % cache[key]
|
|
102
|
+
speed = '%.1fx' % (cache[key] / max(r['cpu'], 1e-3))
|
|
103
|
+
print('%-14s %-6s %10.3f %10s %9s %.0f' % (n, '正确' if ok else '错误', r['cpu'], bcpu, speed, r['mem_mb']))
|
|
104
|
+
if not ok:
|
|
105
|
+
bad = next(x for x in runs if x['digest'] != measure.digest(expected) or x['code'] != 0)
|
|
106
|
+
if bad['code'] != 0:
|
|
107
|
+
print(' 退出码 / exit code %d' % bad['code'])
|
|
108
|
+
d = first_diff(expected, bad['output'])
|
|
109
|
+
if d:
|
|
110
|
+
print(' 第 %d 行不同 / first difference at line %d' % (d[0], d[0]))
|
|
111
|
+
print(' 期望 expected: %s' % d[1][:200])
|
|
112
|
+
print(' 实际 actual: %s' % d[2][:200])
|
|
113
|
+
print('合计 CPU 秒 / total CPU s: %.3f' % total + (' 基线 / baseline: %.3f' % total_base if base else ''))
|
|
114
|
+
print('全部正确 / ALL CORRECT' if all_ok else '存在错误 / SOME OUTPUTS ARE WRONG')
|
|
115
|
+
sys.exit(0 if all_ok else 1)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
if __name__ == '__main__':
|
|
119
|
+
main()
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""输入合法性检查器:检查一个输入文件是否符合 SPEC.md 的输入约定,以及击穿用例的规模上限。
|
|
2
|
+
|
|
3
|
+
python tools/check_input.py 用例.in # 按击穿用例的上限检查
|
|
4
|
+
python tools/check_input.py 用例.in --full # 按正式负载的上限检查(W,H ≤ 200 等)
|
|
5
|
+
python tools/check_input.py 用例.in --p2 # 第二阶段格式(PARAMS 末尾多 agingEvery carryCost)
|
|
6
|
+
|
|
7
|
+
合法时输出 OK 并以 0 退出;否则输出第一个问题并以 1 退出。
|
|
8
|
+
|
|
9
|
+
格式要求(比 SPEC 更严格的地方是为了避免歧义):
|
|
10
|
+
* 每一项之间恰好一个空格,行首行尾没有空格,没有空行;换行用 \\n 或 \\r\\n;
|
|
11
|
+
* 整数写成标准十进制(可以有负号,没有前导零、没有加号)。
|
|
12
|
+
"""
|
|
13
|
+
import re
|
|
14
|
+
import sys
|
|
15
|
+
if hasattr(sys.stdout, 'reconfigure'):
|
|
16
|
+
sys.stdout.reconfigure(encoding='utf-8') # Windows 控制台默认 GBK,统一输出 UTF-8
|
|
17
|
+
|
|
18
|
+
INT = re.compile(r'^(0|-?[1-9][0-9]*)$')
|
|
19
|
+
POS = re.compile(r'^[1-9][0-9]*$')
|
|
20
|
+
|
|
21
|
+
LIMITS_HACK = dict(WH=60, T=3000, R=40, E=20000)
|
|
22
|
+
LIMITS_FULL = dict(WH=200, T=20000, R=300, E=200000)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Bad(Exception):
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def check(text, full=False, p2=False):
|
|
30
|
+
lim = LIMITS_FULL if full else LIMITS_HACK
|
|
31
|
+
lines = text.replace('\r\n', '\n').split('\n')
|
|
32
|
+
if lines and lines[-1] == '':
|
|
33
|
+
lines.pop()
|
|
34
|
+
pos = [0]
|
|
35
|
+
|
|
36
|
+
def nxt(what):
|
|
37
|
+
if pos[0] >= len(lines):
|
|
38
|
+
raise Bad('第 %d 行:文件提前结束,缺少%s' % (pos[0] + 1, what))
|
|
39
|
+
s = lines[pos[0]]
|
|
40
|
+
pos[0] += 1
|
|
41
|
+
if s != s.strip(' ') or ' ' in s or '\t' in s:
|
|
42
|
+
raise Bad('第 %d 行:多余的空白' % pos[0])
|
|
43
|
+
return s
|
|
44
|
+
|
|
45
|
+
def ints(tokens, lo, hi, names):
|
|
46
|
+
out = []
|
|
47
|
+
for t, (a, b), n in zip(tokens, zip(lo, hi), names):
|
|
48
|
+
if not INT.match(t):
|
|
49
|
+
raise Bad('第 %d 行:%s 不是标准整数:%r' % (pos[0], n, t))
|
|
50
|
+
v = int(t)
|
|
51
|
+
if not a <= v <= b:
|
|
52
|
+
raise Bad('第 %d 行:%s=%d 超出范围 [%d, %d]' % (pos[0], n, v, a, b))
|
|
53
|
+
out.append(v)
|
|
54
|
+
return out
|
|
55
|
+
|
|
56
|
+
def expect(s, k, what):
|
|
57
|
+
tok = s.split(' ')
|
|
58
|
+
if len(tok) != k:
|
|
59
|
+
raise Bad('第 %d 行:%s 应有 %d 项,实际 %d 项' % (pos[0], what, k, len(tok)))
|
|
60
|
+
return tok
|
|
61
|
+
|
|
62
|
+
W, H, T = ints(expect(nxt('首行'), 3, '首行 W H T'), [3, 3, 1], [lim['WH'], lim['WH'], lim['T']], ['W', 'H', 'T'])
|
|
63
|
+
g = []
|
|
64
|
+
for y in range(H):
|
|
65
|
+
row = nxt('地图')
|
|
66
|
+
if len(row) != W or set(row) - set('.#C'):
|
|
67
|
+
raise Bad('第 %d 行:地图行必须恰好 %d 个字符,只能是 . # C' % (pos[0], W))
|
|
68
|
+
g.append(row)
|
|
69
|
+
for x in range(W):
|
|
70
|
+
if g[0][x] != '#' or g[H - 1][x] != '#':
|
|
71
|
+
raise Bad('地图四周必须是墙')
|
|
72
|
+
for y in range(H):
|
|
73
|
+
if g[y][0] != '#' or g[y][W - 1] != '#':
|
|
74
|
+
raise Bad('地图四周必须是墙')
|
|
75
|
+
|
|
76
|
+
tok = expect(nxt('PARAMS'), 9 if p2 else 7, 'PARAMS 行')
|
|
77
|
+
if tok[0] != 'PARAMS':
|
|
78
|
+
raise Bad('第 %d 行:应以 PARAMS 开头' % pos[0])
|
|
79
|
+
names = ['batteryMax', 'chargeRate', 'lowThreshold', 'safetyMargin', 'reportEvery', 'hotRadius']
|
|
80
|
+
lo, hi = [1, 1, 0, 0, 1, 0], [100000, 100000, 100000, 1000, 1000000, 400]
|
|
81
|
+
if p2:
|
|
82
|
+
names += ['agingEvery', 'carryCost']
|
|
83
|
+
lo += [1, 1]
|
|
84
|
+
hi += [1000000000, 100]
|
|
85
|
+
pv = ints(tok[1:], lo, hi, names)
|
|
86
|
+
bmax = pv[0]
|
|
87
|
+
if pv[1] > bmax or pv[2] > bmax:
|
|
88
|
+
raise Bad('第 %d 行:chargeRate 和 lowThreshold 不能超过 batteryMax' % pos[0])
|
|
89
|
+
|
|
90
|
+
tok = expect(nxt('ROBOTS'), 2, 'ROBOTS 行')
|
|
91
|
+
if tok[0] != 'ROBOTS':
|
|
92
|
+
raise Bad('第 %d 行:应为 ROBOTS R' % pos[0])
|
|
93
|
+
R, = ints(tok[1:], [1], [lim['R']], ['R'])
|
|
94
|
+
seen = set()
|
|
95
|
+
for i in range(R):
|
|
96
|
+
x, y = ints(expect(nxt('机器人位置'), 2, '机器人位置'), [0, 0], [W - 1, H - 1], ['x', 'y'])
|
|
97
|
+
if g[y][x] == '#':
|
|
98
|
+
raise Bad('第 %d 行:机器人初始位置是墙' % pos[0])
|
|
99
|
+
if (x, y) in seen:
|
|
100
|
+
raise Bad('第 %d 行:机器人初始位置重复' % pos[0])
|
|
101
|
+
seen.add((x, y))
|
|
102
|
+
|
|
103
|
+
tok = expect(nxt('EVENTS'), 2, 'EVENTS 行')
|
|
104
|
+
if tok[0] != 'EVENTS':
|
|
105
|
+
raise Bad('第 %d 行:应为 EVENTS E' % pos[0])
|
|
106
|
+
E, = ints(tok[1:], [0], [lim['E']], ['E'])
|
|
107
|
+
last = 0
|
|
108
|
+
ids = set()
|
|
109
|
+
C = 1000 # 事件里的坐标可以在地图外,但限制在 [-1000, 1000]
|
|
110
|
+
for i in range(E):
|
|
111
|
+
s = nxt('事件')
|
|
112
|
+
tok = s.split(' ')
|
|
113
|
+
if len(tok) < 2:
|
|
114
|
+
raise Bad('第 %d 行:事件格式错误' % pos[0])
|
|
115
|
+
t, = ints(tok[:1], [0], [T - 1], ['tick'])
|
|
116
|
+
if t < last:
|
|
117
|
+
raise Bad('第 %d 行:事件的 tick 必须非递减' % pos[0])
|
|
118
|
+
last = t
|
|
119
|
+
typ = tok[1]
|
|
120
|
+
if typ == 'ORDER':
|
|
121
|
+
expect(s, 8, 'ORDER 事件')
|
|
122
|
+
if not POS.match(tok[2]) or int(tok[2]) > 10 ** 9:
|
|
123
|
+
raise Bad('第 %d 行:订单编号必须是 1..10^9 的正整数,没有前导零' % pos[0])
|
|
124
|
+
if tok[2] in ids:
|
|
125
|
+
raise Bad('第 %d 行:订单编号重复' % pos[0])
|
|
126
|
+
ids.add(tok[2])
|
|
127
|
+
ints(tok[3:], [-C] * 4 + [0], [C] * 4 + [1000], ['px', 'py', 'dx', 'dy', 'priority'])
|
|
128
|
+
elif typ == 'CANCEL':
|
|
129
|
+
expect(s, 3, 'CANCEL 事件')
|
|
130
|
+
if not POS.match(tok[2]) or int(tok[2]) > 10 ** 9:
|
|
131
|
+
raise Bad('第 %d 行:CANCEL 的编号必须是 1..10^9 的正整数,没有前导零' % pos[0])
|
|
132
|
+
elif typ in ('BLOCK', 'UNBLOCK'):
|
|
133
|
+
expect(s, 4, typ + ' 事件')
|
|
134
|
+
ints(tok[2:], [-C, -C], [C, C], ['x', 'y'])
|
|
135
|
+
else:
|
|
136
|
+
raise Bad('第 %d 行:未知事件类型 %r' % (pos[0], typ))
|
|
137
|
+
if pos[0] != len(lines):
|
|
138
|
+
raise Bad('第 %d 行:EVENTS 之后还有多余内容' % (pos[0] + 1))
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def main():
|
|
142
|
+
args = [a for a in sys.argv[1:] if not a.startswith('--')]
|
|
143
|
+
if len(args) != 1:
|
|
144
|
+
print(__doc__)
|
|
145
|
+
sys.exit(2)
|
|
146
|
+
with open(args[0], 'rb') as f:
|
|
147
|
+
data = f.read()
|
|
148
|
+
try:
|
|
149
|
+
text = data.decode('ascii')
|
|
150
|
+
check(text, full='--full' in sys.argv, p2='--p2' in sys.argv)
|
|
151
|
+
except UnicodeDecodeError:
|
|
152
|
+
print('不合法:文件里有非 ASCII 字符')
|
|
153
|
+
sys.exit(1)
|
|
154
|
+
except Bad as e:
|
|
155
|
+
print('不合法:%s' % e)
|
|
156
|
+
sys.exit(1)
|
|
157
|
+
print('OK')
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if __name__ == '__main__':
|
|
161
|
+
main()
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""仓储调度负载生成器(公开版,只包含公开负载的配置)。
|
|
2
|
+
|
|
3
|
+
可以用不同的种子生成大量同类输入,用来和基线对拍;也可以自行修改参数。
|
|
4
|
+
|
|
5
|
+
python tools/gen.py 配置名 种子 > 输出.in
|
|
6
|
+
python tools/gen.py --list # 列出所有配置
|
|
7
|
+
|
|
8
|
+
同一个 (配置名, 种子) 总是生成完全相同的文件。
|
|
9
|
+
"""
|
|
10
|
+
import random
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
# 每个配置侧重不同的瓶颈
|
|
14
|
+
PROFILES = {
|
|
15
|
+
# ---- 公开负载(给选手,基线几秒到几十秒)
|
|
16
|
+
'pub_small': dict(W=40, H=31, T=1500, R=24, chargers=4, rate=0.35, cancel=0.05, block=0.02, dur=(30, 150)),
|
|
17
|
+
'pub_wide': dict(W=90, H=61, T=1500, R=40, chargers=6, rate=0.25, cancel=0.03, block=0.01, dur=(50, 200)),
|
|
18
|
+
'pub_crowded': dict(W=50, H=40, T=1500, R=80, chargers=14, rate=0.8, cancel=0.03, block=0.02, dur=(30, 120)),
|
|
19
|
+
'pub_dynamic': dict(W=60, H=46, T=1500, R=40, chargers=5, rate=0.4, cancel=0.05, block=0.25, dur=(10, 80)),
|
|
20
|
+
'pub_flood': dict(W=50, H=37, T=1500, R=30, chargers=4, rate=1.6, cancel=0.10, block=0.02, dur=(30, 150)),
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# 电量参数(满电、每 tick 充电量、低电量阈值、安全余量)默认按地图尺寸缩放,见 battery_for()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def battery_for(W, H):
|
|
27
|
+
full = 8 * (W + H)
|
|
28
|
+
return (full, max(10, full // 20), 2 * (W + H), 30)
|
|
29
|
+
REPORT_EVERY = 50
|
|
30
|
+
HOT_RADIUS = 3
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def make_grid(W, H, n_chargers, rng):
|
|
34
|
+
g = [['.'] * W for _ in range(H)]
|
|
35
|
+
for x in range(W):
|
|
36
|
+
g[0][x] = g[H - 1][x] = '#'
|
|
37
|
+
for y in range(H):
|
|
38
|
+
g[y][0] = g[y][W - 1] = '#'
|
|
39
|
+
# 货架:每 3 行一排(1 行货架 + 2 行通道),左侧 x=1..4 是装卸区,
|
|
40
|
+
# 每隔 7~10 格留一个 2 格宽的横向通道
|
|
41
|
+
for y in range(4, H - 3, 3):
|
|
42
|
+
x = 6
|
|
43
|
+
while x < W - 4:
|
|
44
|
+
seg = rng.randint(6, 9)
|
|
45
|
+
for xx in range(x, min(x + seg, W - 4)):
|
|
46
|
+
g[y][xx] = '#'
|
|
47
|
+
x += seg + 2
|
|
48
|
+
# 充电桩:均匀分布在顶部通道(y=1),避开左侧和底部的装卸区
|
|
49
|
+
step = max(1, (W - 9) // max(1, n_chargers))
|
|
50
|
+
spots = [(min(W - 2, 6 + i * step), 1) for i in range(n_chargers)]
|
|
51
|
+
for x, y in spots:
|
|
52
|
+
g[y][x] = 'C'
|
|
53
|
+
return g
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def floor_cells(g):
|
|
57
|
+
return [(x, y) for y in range(len(g)) for x in range(len(g[0])) if g[y][x] == '.']
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def generate(name, seed):
|
|
61
|
+
return generate_from(name, PROFILES[name], seed)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def generate_from(name, p, seed):
|
|
65
|
+
"""按配置字典 p 生成;p 里有 extra 时把它追加到 PARAMS 行末尾(第二阶段用)。"""
|
|
66
|
+
rng = random.Random('%s/%d' % (name, seed))
|
|
67
|
+
W, H, T, R = p['W'], p['H'], p['T'], p['R']
|
|
68
|
+
battery = p.get('battery') or battery_for(W, H)
|
|
69
|
+
g = make_grid(W, H, p['chargers'], rng)
|
|
70
|
+
floors = floor_cells(g)
|
|
71
|
+
pickups = [(x, y) for x, y in floors
|
|
72
|
+
if (y > 0 and g[y - 1][x] == '#' and y - 1 > 0) or (y < H - 1 and g[y + 1][x] == '#' and y + 1 < H - 1)]
|
|
73
|
+
docks = [(x, y) for x, y in floors if x <= 3 or y >= H - 3]
|
|
74
|
+
walls = [(x, y) for y in range(1, H - 1) for x in range(1, W - 1) if g[y][x] == '#']
|
|
75
|
+
aisles = [(x, y) for x, y in floors if 5 <= x <= W - 5]
|
|
76
|
+
starts = rng.sample(floors, R)
|
|
77
|
+
|
|
78
|
+
events = [] # (tick, 序号, 文本)
|
|
79
|
+
oid = 0
|
|
80
|
+
for t in range(T):
|
|
81
|
+
n = 0
|
|
82
|
+
r = p['rate']
|
|
83
|
+
while r > 0:
|
|
84
|
+
if rng.random() < min(r, 1.0):
|
|
85
|
+
n += 1
|
|
86
|
+
r -= 1.0
|
|
87
|
+
for _ in range(n):
|
|
88
|
+
oid += 1
|
|
89
|
+
if rng.random() < 0.01: # 少量无效订单(取货点是墙)
|
|
90
|
+
px, py = rng.choice(walls)
|
|
91
|
+
else:
|
|
92
|
+
px, py = rng.choice(pickups)
|
|
93
|
+
dx, dy = rng.choice(docks)
|
|
94
|
+
prio = rng.choices([0, 1, 2, 3], [60, 25, 10, 5])[0]
|
|
95
|
+
events.append((t, len(events), 'ORDER %d %d %d %d %d %d' % (oid, px, py, dx, dy, prio)))
|
|
96
|
+
if rng.random() < p['cancel']:
|
|
97
|
+
ct = t + rng.randint(1, 200)
|
|
98
|
+
if ct < T:
|
|
99
|
+
events.append((ct, len(events), 'CANCEL %d' % oid))
|
|
100
|
+
if rng.random() < 0.003: # 取消一个不存在的订单
|
|
101
|
+
events.append((t, len(events), 'CANCEL %d' % (oid + 100000)))
|
|
102
|
+
b = p['block']
|
|
103
|
+
while b > 0:
|
|
104
|
+
if rng.random() < min(b, 1.0):
|
|
105
|
+
x, y = rng.choice(aisles)
|
|
106
|
+
d = rng.randint(*p['dur'])
|
|
107
|
+
events.append((t, len(events), 'BLOCK %d %d' % (x, y)))
|
|
108
|
+
if t + d < T:
|
|
109
|
+
events.append((t + d, len(events), 'UNBLOCK %d %d' % (x, y)))
|
|
110
|
+
b -= 1.0
|
|
111
|
+
events.sort()
|
|
112
|
+
|
|
113
|
+
out = ['%d %d %d' % (W, H, T)]
|
|
114
|
+
out += [''.join(row) for row in g]
|
|
115
|
+
out.append('PARAMS %d %d %d %d %d %d' % (battery + (REPORT_EVERY, HOT_RADIUS)) +
|
|
116
|
+
''.join(' %d' % v for v in p.get('extra', ())))
|
|
117
|
+
out.append('ROBOTS %d' % R)
|
|
118
|
+
out += ['%d %d' % s for s in starts]
|
|
119
|
+
out.append('EVENTS %d' % len(events))
|
|
120
|
+
out += ['%d %s' % (t, text) for t, _, text in events]
|
|
121
|
+
return '\n'.join(out) + '\n'
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
if __name__ == '__main__':
|
|
125
|
+
if len(sys.argv) == 2 and sys.argv[1] == '--list':
|
|
126
|
+
for k, v in PROFILES.items():
|
|
127
|
+
print(k, v)
|
|
128
|
+
sys.exit(0)
|
|
129
|
+
if len(sys.argv) != 3:
|
|
130
|
+
print(__doc__)
|
|
131
|
+
sys.exit(1)
|
|
132
|
+
sys.stdout.write(generate(sys.argv[1], int(sys.argv[2])))
|