dsh-eval-guard 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -13
- package/assets/bomberman/workspace.zip +0 -0
- package/assets/commit0/instance.json +30 -0
- package/assets/commit0/workspace.zip +0 -0
- package/assets/ctf/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/assets/tank/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,53 @@
|
|
|
1
|
+
// cycles.hpp —— 裁判这一侧测定"1 CPU 毫秒是多少个读数单位"。
|
|
2
|
+
//
|
|
3
|
+
// Windows 上 CPU 用量的读数是周期数(TSC 的计数),每台机器每毫秒多少周期不一样,
|
|
4
|
+
// 所以裁判测定一次,在开局时告诉 bot,两边用同一个数。测定的办法,按先后:
|
|
5
|
+
// 1. 环境变量 TANK_CYCLES_PER_MS(正式赛想钉死一个数时用);
|
|
6
|
+
// 2. CPUID 的 0x15 号功能给出的 TSC 标称频率(较新的 Intel 处理器有,是精确值,各进程读到的完全相同);
|
|
7
|
+
// 3. 用 rdtsc 对着系统的高精度时钟数 250 毫秒,结果四舍五入到 0.1 MHz(各进程测出来的基本落在同一个数上)。
|
|
8
|
+
// 其他系统上 CPU 用量的读数直接是纳秒,固定为 1000000。
|
|
9
|
+
#pragma once
|
|
10
|
+
|
|
11
|
+
#include <chrono>
|
|
12
|
+
#include <cstdlib>
|
|
13
|
+
|
|
14
|
+
#if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__))
|
|
15
|
+
#include <cpuid.h>
|
|
16
|
+
#include <x86intrin.h>
|
|
17
|
+
#endif
|
|
18
|
+
|
|
19
|
+
namespace cycles {
|
|
20
|
+
|
|
21
|
+
struct Scale {
|
|
22
|
+
long long per_ms = 1000000; // 每 CPU 毫秒多少个读数单位
|
|
23
|
+
const char* source = "ns"; // 怎么得到的:env / cpuid / measured / ns
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
inline Scale measure() {
|
|
27
|
+
Scale out;
|
|
28
|
+
#if defined(_WIN32)
|
|
29
|
+
if (const char* text = std::getenv("TANK_CYCLES_PER_MS")) {
|
|
30
|
+
const long long v = std::atoll(text);
|
|
31
|
+
if (v > 0) return Scale{v, "env"};
|
|
32
|
+
}
|
|
33
|
+
#if defined(__x86_64__) || defined(__i386__)
|
|
34
|
+
unsigned a = 0, b = 0, c = 0, d = 0;
|
|
35
|
+
if (__get_cpuid_max(0, nullptr) >= 0x15 && __get_cpuid(0x15, &a, &b, &c, &d) && a && b && c) {
|
|
36
|
+
const unsigned long long hz = (unsigned long long)c * b / a;
|
|
37
|
+
if (hz > 100000000ULL) return Scale{(long long)(hz / 1000), "cpuid"};
|
|
38
|
+
}
|
|
39
|
+
using clock = std::chrono::steady_clock;
|
|
40
|
+
const auto t0 = clock::now();
|
|
41
|
+
const unsigned long long c0 = __rdtsc();
|
|
42
|
+
auto t1 = t0;
|
|
43
|
+
while (std::chrono::duration<double>(t1 - t0).count() < 0.25) t1 = clock::now();
|
|
44
|
+
const unsigned long long c1 = __rdtsc();
|
|
45
|
+
const double per_ms = double(c1 - c0) / std::chrono::duration<double, std::milli>(t1 - t0).count();
|
|
46
|
+
out.per_ms = (long long)(per_ms / 100.0 + 0.5) * 100; // 0.1 MHz = 每毫秒 100 个周期
|
|
47
|
+
out.source = "measured";
|
|
48
|
+
#endif
|
|
49
|
+
#endif
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
} // namespace cycles
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
// fast.cpp —— 快速模式的对局程序:把两个 bot(各编成一个动态库)装进同一个进程直接互相调用。
|
|
2
|
+
//
|
|
3
|
+
// 用法:fast <A 的动态库> <B 的动态库> [--log] [--hang 秒数] [--wall]
|
|
4
|
+
// 标准输入每行一局:"<编号> <种子> <风格> <换边>",换边为 0 时 A 坐 0 号位,为 1 时 A 坐 1 号位。
|
|
5
|
+
// 标准输出每局:
|
|
6
|
+
// B <编号> 开始这一局(先打出来,进程中途死掉时知道死在哪一局)
|
|
7
|
+
// O <回合> <0 号方命令>|<1 号方命令> 只有 --log 时:这一回合实际执行的命令
|
|
8
|
+
// F <回合> <座位> <原因> 这一回合该座位的输出不合法(三辆都不动,记一次失败)
|
|
9
|
+
// FINAL + 回合段文本 只有 --log 时:终局局面
|
|
10
|
+
// G <编号> <种子> <风格> <换边> <回合数> <胜方座位> <原因> <签名> <A 的用量> <B 的用量>
|
|
11
|
+
// 用量 = 首回合毫秒:其余回合平均:其余回合最慢:超预算的回合数:不合法的回合数
|
|
12
|
+
// 出事时打一行 "X <编号> <哪一方:A/B/?> <说明>" 然后退出(退出码 3)。
|
|
13
|
+
//
|
|
14
|
+
// 和正式模式的对应关系(这几条保证不依赖时间的 bot 两种模式打出同样的对局):
|
|
15
|
+
// - 地图:同一个 tank::make_game(种子, 风格)。
|
|
16
|
+
// - 调用顺序:每个 bot 先 init 一次,之后每回合 decide 一次;传进去的是同一个局面。
|
|
17
|
+
// - bot 返回的命令先写成文本再按协议解析(和正式模式走同一个 parse_orders),不合法就三辆都不动、
|
|
18
|
+
// 记一次失败;连续 3 次判负,对局在那一回合结束、不再结算。
|
|
19
|
+
// - 每局开始前把两个动态库卸掉重新加载,全局变量、静态变量都回到初始。
|
|
20
|
+
// - 预算只统计、不判罚;崩溃不隔离。
|
|
21
|
+
// - 预算的统计:每次调用 bot 前后读本线程的 CPU 用量(Windows 上是 QueryThreadCycleTime),
|
|
22
|
+
// 所以只算到调用 bot 的这个线程;--wall 时改用墙上时钟。
|
|
23
|
+
|
|
24
|
+
#include "../tank.hpp"
|
|
25
|
+
#include "cycles.hpp"
|
|
26
|
+
|
|
27
|
+
#include <atomic>
|
|
28
|
+
#include <chrono>
|
|
29
|
+
#include <csignal>
|
|
30
|
+
#include <cstdio>
|
|
31
|
+
#include <cstdlib>
|
|
32
|
+
#include <cstring>
|
|
33
|
+
#include <string>
|
|
34
|
+
#include <thread>
|
|
35
|
+
|
|
36
|
+
#ifdef _WIN32
|
|
37
|
+
#include <fcntl.h>
|
|
38
|
+
#include <io.h>
|
|
39
|
+
#include <windows.h>
|
|
40
|
+
#else
|
|
41
|
+
#include <dlfcn.h>
|
|
42
|
+
#include <unistd.h>
|
|
43
|
+
#endif
|
|
44
|
+
|
|
45
|
+
namespace {
|
|
46
|
+
|
|
47
|
+
const int MAX_STRIKES = 3; // 与 core.py 的 MAX_STRIKES 相同
|
|
48
|
+
|
|
49
|
+
using AbiFn = int (*)(int*, const char**);
|
|
50
|
+
using SetupFn = int (*)(int, double);
|
|
51
|
+
using InitFn = int (*)(const tank::State*, int, double*, char*, int);
|
|
52
|
+
using DecideFn = int (*)(const tank::State*, int, tank::Orders*, double*, char*, int);
|
|
53
|
+
|
|
54
|
+
struct Lib {
|
|
55
|
+
const char* path = nullptr;
|
|
56
|
+
void* handle = nullptr;
|
|
57
|
+
InitFn init = nullptr;
|
|
58
|
+
DecideFn decide = nullptr;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
int g_budget_kind = tank::BUDGET_CPU; // 让 bot 用哪把尺
|
|
62
|
+
double g_per_ms = 1; // 每 CPU 毫秒多少个读数单位
|
|
63
|
+
std::FILE* g_out = nullptr; // 结果输出(原来的标准输出)
|
|
64
|
+
long long g_game = -1; // 正在打的那一局的编号
|
|
65
|
+
std::atomic<int> g_inside{-1}; // 正在哪个 bot 的代码里:0 = A,1 = B,-1 = 都不在
|
|
66
|
+
std::atomic<unsigned long long> g_calls{0}; // 调用计数,给看门狗判断有没有进展
|
|
67
|
+
const char WHO[] = "AB";
|
|
68
|
+
|
|
69
|
+
// 出事时的最后一行。只用最朴素的写法,因为可能是在崩溃处理里。
|
|
70
|
+
[[noreturn]] void die(const char* what) {
|
|
71
|
+
const int who = g_inside.load();
|
|
72
|
+
char line[600];
|
|
73
|
+
const int n = std::snprintf(line, sizeof line, "X %lld %c %s\n", g_game, who < 0 ? '?' : WHO[who], what);
|
|
74
|
+
if (g_out) {
|
|
75
|
+
std::fflush(g_out);
|
|
76
|
+
#ifdef _WIN32
|
|
77
|
+
_write(_fileno(g_out), line, unsigned(n));
|
|
78
|
+
#else
|
|
79
|
+
if (write(fileno(g_out), line, size_t(n)) < 0) {}
|
|
80
|
+
#endif
|
|
81
|
+
}
|
|
82
|
+
std::_Exit(3);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
void on_signal(int sig) {
|
|
86
|
+
char what[64];
|
|
87
|
+
std::snprintf(what, sizeof what, "%s", sig == SIGABRT ? "程序中止(abort / 未接住的异常 / 断言失败)"
|
|
88
|
+
: sig == SIGFPE ? "算术错误(比如除以零)"
|
|
89
|
+
: sig == SIGSEGV ? "非法内存访问" : "非法指令");
|
|
90
|
+
die(what);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
#ifdef _WIN32
|
|
94
|
+
LONG WINAPI on_seh(EXCEPTION_POINTERS* info) {
|
|
95
|
+
const DWORD code = info->ExceptionRecord->ExceptionCode;
|
|
96
|
+
char what[96];
|
|
97
|
+
const char* name = code == EXCEPTION_ACCESS_VIOLATION ? "非法内存访问"
|
|
98
|
+
: code == EXCEPTION_STACK_OVERFLOW ? "栈溢出(递归太深)"
|
|
99
|
+
: code == EXCEPTION_INT_DIVIDE_BY_ZERO ? "除以零"
|
|
100
|
+
: code == EXCEPTION_ILLEGAL_INSTRUCTION ? "非法指令" : "崩溃";
|
|
101
|
+
std::snprintf(what, sizeof what, "%s(异常代码 0x%08lX)", name, static_cast<unsigned long>(code));
|
|
102
|
+
die(what);
|
|
103
|
+
}
|
|
104
|
+
#endif
|
|
105
|
+
|
|
106
|
+
void on_exit_inside() {
|
|
107
|
+
// bot 自己调了 exit():正常跑完时 g_inside 是 -1,不会走到这里面。
|
|
108
|
+
if (g_inside.load() >= 0) die("bot 调用了 exit()");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
void* lib_open(const char* path) {
|
|
112
|
+
#ifdef _WIN32
|
|
113
|
+
return static_cast<void*>(LoadLibraryA(path));
|
|
114
|
+
#else
|
|
115
|
+
return dlopen(path, RTLD_NOW | RTLD_LOCAL);
|
|
116
|
+
#endif
|
|
117
|
+
}
|
|
118
|
+
void lib_close(void* h) {
|
|
119
|
+
#ifdef _WIN32
|
|
120
|
+
FreeLibrary(static_cast<HMODULE>(h));
|
|
121
|
+
#else
|
|
122
|
+
dlclose(h);
|
|
123
|
+
#endif
|
|
124
|
+
}
|
|
125
|
+
void* lib_symbol(void* h, const char* name) {
|
|
126
|
+
#ifdef _WIN32
|
|
127
|
+
return reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE>(h), name));
|
|
128
|
+
#else
|
|
129
|
+
return dlsym(h, name);
|
|
130
|
+
#endif
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 重新加载:卸掉再装,库里的全局变量、静态变量全部重新初始化。
|
|
134
|
+
void reload(Lib& lib, int who) {
|
|
135
|
+
g_inside = who; // 全局对象的构造和析构也是 bot 的代码
|
|
136
|
+
if (lib.handle) {
|
|
137
|
+
lib_close(lib.handle);
|
|
138
|
+
#ifdef _WIN32
|
|
139
|
+
if (GetModuleHandleA(lib.path)) die("动态库卸不掉(bot 可能留着后台线程),全局状态没法重置");
|
|
140
|
+
#endif
|
|
141
|
+
}
|
|
142
|
+
lib.handle = lib_open(lib.path);
|
|
143
|
+
if (!lib.handle) die("动态库加载失败");
|
|
144
|
+
const AbiFn abi = reinterpret_cast<AbiFn>(lib_symbol(lib.handle, "tankbot_abi"));
|
|
145
|
+
lib.init = reinterpret_cast<InitFn>(lib_symbol(lib.handle, "tankbot_init"));
|
|
146
|
+
lib.decide = reinterpret_cast<DecideFn>(lib_symbol(lib.handle, "tankbot_decide"));
|
|
147
|
+
int size = 0;
|
|
148
|
+
const char* version = "";
|
|
149
|
+
if (!abi || !lib.init || !lib.decide || abi(&size, &version) != 1 || size != int(sizeof(tank::State)) ||
|
|
150
|
+
std::strcmp(version, tank::ENGINE_VERSION) != 0)
|
|
151
|
+
die("动态库和对局程序不是同一份 tank.hpp 编出来的");
|
|
152
|
+
const SetupFn setup = reinterpret_cast<SetupFn>(lib_symbol(lib.handle, "tankbot_setup"));
|
|
153
|
+
if (!setup) die("动态库和对局程序不是同一份主循环编出来的");
|
|
154
|
+
setup(g_budget_kind, g_per_ms);
|
|
155
|
+
g_inside = -1;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// CRC-32(和 Python 的 zlib.crc32 相同),用来给对局算签名。
|
|
159
|
+
uint32_t crc32_of(const std::string& text) {
|
|
160
|
+
static uint32_t table[256];
|
|
161
|
+
static bool ready = false;
|
|
162
|
+
if (!ready) {
|
|
163
|
+
for (uint32_t i = 0; i < 256; ++i) {
|
|
164
|
+
uint32_t c = i;
|
|
165
|
+
for (int k = 0; k < 8; ++k) c = (c & 1) ? 0xEDB88320u ^ (c >> 1) : c >> 1;
|
|
166
|
+
table[i] = c;
|
|
167
|
+
}
|
|
168
|
+
ready = true;
|
|
169
|
+
}
|
|
170
|
+
uint32_t c = 0xFFFFFFFFu;
|
|
171
|
+
for (const unsigned char ch : text) c = table[(c ^ ch) & 0xFF] ^ (c >> 8);
|
|
172
|
+
return c ^ 0xFFFFFFFFu;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
struct Usage {
|
|
176
|
+
double first = 0, total = 0, slowest = 0;
|
|
177
|
+
int turns = 0, over = 0, illegal = 0;
|
|
178
|
+
void add(double ms, bool first_turn) {
|
|
179
|
+
if (first_turn) {
|
|
180
|
+
first = ms;
|
|
181
|
+
over += ms > tank::BUDGET_FIRST_MS;
|
|
182
|
+
} else {
|
|
183
|
+
total += ms;
|
|
184
|
+
turns += 1;
|
|
185
|
+
if (ms > slowest) slowest = ms;
|
|
186
|
+
over += ms > tank::BUDGET_TURN_MS;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
std::string three(const tank::Orders& o) {
|
|
192
|
+
return tank::cmd_text(o.cmd[0]) + " " + tank::cmd_text(o.cmd[1]) + " " + tank::cmd_text(o.cmd[2]);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
void play(Lib* libs, long long id, unsigned long long seed, int style, int swap, bool log) {
|
|
196
|
+
g_game = id;
|
|
197
|
+
std::fprintf(g_out, "B %lld\n", id);
|
|
198
|
+
std::fflush(g_out);
|
|
199
|
+
reload(libs[0], 0);
|
|
200
|
+
reload(libs[1], 1);
|
|
201
|
+
|
|
202
|
+
tank::State s = tank::make_game(seed, style);
|
|
203
|
+
const int bot_at[2] = {swap ? 1 : 0, swap ? 0 : 1}; // bot_at[座位] = 0(A)或 1(B)
|
|
204
|
+
Usage usage[2]; // 按 bot(A、B)记
|
|
205
|
+
int strikes[2] = {0, 0}; // 按座位记
|
|
206
|
+
bool dead[2] = {false, false};
|
|
207
|
+
double used = 0;
|
|
208
|
+
char err[400];
|
|
209
|
+
std::string orders_log;
|
|
210
|
+
|
|
211
|
+
for (int seat = 0; seat < 2; ++seat) {
|
|
212
|
+
const int who = bot_at[seat];
|
|
213
|
+
g_inside = who;
|
|
214
|
+
g_calls++;
|
|
215
|
+
if (libs[who].init(&s, seat, &used, err, int(sizeof err))) die((std::string("init 抛出异常:") + err).c_str());
|
|
216
|
+
g_inside = -1;
|
|
217
|
+
}
|
|
218
|
+
while (!s.done) {
|
|
219
|
+
tank::Orders applied[2];
|
|
220
|
+
for (int seat = 0; seat < 2; ++seat) {
|
|
221
|
+
const int who = bot_at[seat];
|
|
222
|
+
tank::Orders raw;
|
|
223
|
+
g_inside = who;
|
|
224
|
+
g_calls++;
|
|
225
|
+
if (libs[who].decide(&s, seat, &raw, &used, err, int(sizeof err)))
|
|
226
|
+
die((std::string("decide 抛出异常:") + err).c_str());
|
|
227
|
+
g_inside = -1;
|
|
228
|
+
usage[who].add(used, s.turn == 0); // 首回合的用量含 init
|
|
229
|
+
// 和正式模式一样:写成一行文本,再按协议解析。
|
|
230
|
+
std::string why;
|
|
231
|
+
if (tank::parse_orders(tank::orders_text(s, raw), s, applied[seat], &why)) {
|
|
232
|
+
strikes[seat] = 0;
|
|
233
|
+
} else {
|
|
234
|
+
applied[seat] = tank::Orders{};
|
|
235
|
+
usage[who].illegal += 1;
|
|
236
|
+
std::fprintf(g_out, "F %d %d %s\n", s.turn + 1, seat, why.c_str());
|
|
237
|
+
if (++strikes[seat] >= MAX_STRIKES) dead[seat] = true;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (dead[0] || dead[1]) break; // 判负的那一回合不结算
|
|
241
|
+
const std::string line = three(applied[0]) + "|" + three(applied[1]) + "\n";
|
|
242
|
+
orders_log += line;
|
|
243
|
+
if (log) std::fprintf(g_out, "O %d %s", s.turn + 1, line.c_str());
|
|
244
|
+
tank::step(s, applied[0], applied[1]);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
int win = s.winner;
|
|
248
|
+
std::string reason = tank::reason_text(s);
|
|
249
|
+
std::string tail = tank::turn_text(s);
|
|
250
|
+
if (dead[0] || dead[1]) {
|
|
251
|
+
win = dead[0] && dead[1] ? -1 : (dead[0] ? 1 : 0);
|
|
252
|
+
reason = "fault";
|
|
253
|
+
tail += std::string("FAULT ") + (dead[0] ? "1" : "0") + (dead[1] ? "1" : "0") + "\n";
|
|
254
|
+
}
|
|
255
|
+
if (log) std::fprintf(g_out, "FINAL\n%s", tank::turn_text(s).c_str());
|
|
256
|
+
std::fprintf(g_out, "G %lld %llu %d %d %d %d %s %08x%08x", id, seed, style, swap, int(s.turn), win, reason.c_str(),
|
|
257
|
+
crc32_of(orders_log), crc32_of(tail));
|
|
258
|
+
for (const Usage& u : usage)
|
|
259
|
+
std::fprintf(g_out, " %.3f:%.4f:%.3f:%d:%d", u.first, u.turns ? u.total / u.turns : 0.0, u.slowest, u.over, u.illegal);
|
|
260
|
+
std::fprintf(g_out, "\n");
|
|
261
|
+
std::fflush(g_out);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
} // namespace
|
|
265
|
+
|
|
266
|
+
int main(int argc, char** argv) {
|
|
267
|
+
bool log = false;
|
|
268
|
+
double hang_seconds = 20;
|
|
269
|
+
const char* paths[2] = {nullptr, nullptr};
|
|
270
|
+
int n_paths = 0;
|
|
271
|
+
for (int k = 1; k < argc; ++k) {
|
|
272
|
+
if (!std::strcmp(argv[k], "--log")) log = true;
|
|
273
|
+
else if (!std::strcmp(argv[k], "--wall")) g_budget_kind = tank::BUDGET_WALL;
|
|
274
|
+
else if (!std::strcmp(argv[k], "--hang") && k + 1 < argc) hang_seconds = std::atof(argv[++k]);
|
|
275
|
+
else if (n_paths < 2) paths[n_paths++] = argv[k];
|
|
276
|
+
}
|
|
277
|
+
if (n_paths != 2) {
|
|
278
|
+
std::fprintf(stderr, "用法:fast <A 的动态库> <B 的动态库> [--log] [--hang 秒数] [--wall]\n");
|
|
279
|
+
return 2;
|
|
280
|
+
}
|
|
281
|
+
g_per_ms = double(cycles::measure().per_ms);
|
|
282
|
+
|
|
283
|
+
// 结果单独留一个句柄,再把标准输出并到标准错误上:bot 往标准输出打的东西不会混进结果里。
|
|
284
|
+
std::fflush(stdout);
|
|
285
|
+
#ifdef _WIN32
|
|
286
|
+
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
|
|
287
|
+
const int fd = _dup(1);
|
|
288
|
+
_dup2(2, 1);
|
|
289
|
+
g_out = _fdopen(fd, "wb");
|
|
290
|
+
ULONG guarantee = 64 * 1024;
|
|
291
|
+
SetThreadStackGuarantee(&guarantee); // 栈溢出时给崩溃处理留一点栈
|
|
292
|
+
SetUnhandledExceptionFilter(on_seh);
|
|
293
|
+
#else
|
|
294
|
+
const int fd = dup(1);
|
|
295
|
+
dup2(2, 1);
|
|
296
|
+
g_out = fdopen(fd, "w");
|
|
297
|
+
#endif
|
|
298
|
+
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
|
299
|
+
if (!g_out) return 2;
|
|
300
|
+
for (const int sig : {SIGABRT, SIGFPE, SIGSEGV, SIGILL}) std::signal(sig, on_signal);
|
|
301
|
+
std::atexit(on_exit_inside);
|
|
302
|
+
|
|
303
|
+
// 看门狗:同一次 bot 调用超过 hang_seconds 秒没有返回,就当作死循环,报出来并退出。
|
|
304
|
+
std::thread([hang_seconds] {
|
|
305
|
+
unsigned long long last = ~0ULL;
|
|
306
|
+
auto since = std::chrono::steady_clock::now();
|
|
307
|
+
for (;;) {
|
|
308
|
+
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
|
309
|
+
const unsigned long long now = g_calls.load();
|
|
310
|
+
const auto t = std::chrono::steady_clock::now();
|
|
311
|
+
if (now != last || g_inside.load() < 0) {
|
|
312
|
+
last = now;
|
|
313
|
+
since = t;
|
|
314
|
+
} else if (std::chrono::duration<double>(t - since).count() > hang_seconds) {
|
|
315
|
+
char what[96];
|
|
316
|
+
std::snprintf(what, sizeof what, "一次调用超过 %.0f 秒没有返回(多半是死循环)", hang_seconds);
|
|
317
|
+
die(what);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}).detach();
|
|
321
|
+
|
|
322
|
+
Lib libs[2];
|
|
323
|
+
libs[0].path = paths[0];
|
|
324
|
+
libs[1].path = paths[1];
|
|
325
|
+
char line[256];
|
|
326
|
+
while (std::fgets(line, sizeof line, stdin)) {
|
|
327
|
+
long long id;
|
|
328
|
+
unsigned long long seed;
|
|
329
|
+
int style, swap;
|
|
330
|
+
if (std::sscanf(line, "%lld %llu %d %d", &id, &seed, &style, &swap) != 4) continue;
|
|
331
|
+
play(libs, id, seed, style, swap, log);
|
|
332
|
+
}
|
|
333
|
+
g_game = -1;
|
|
334
|
+
std::fprintf(g_out, "DONE\n");
|
|
335
|
+
std::fflush(g_out);
|
|
336
|
+
std::_Exit(0); // 不卸载动态库、不跑 bot 的全局析构,直接结束
|
|
337
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// referee.cpp —— 裁判核心:一个常驻的小程序,替 Python 裁判(engine.py)执行 tank.hpp 里的规则。
|
|
2
|
+
//
|
|
3
|
+
// Python 那边不含任何规则:开局、检查命令是否合法、结算、列出事件,全部在这里通过 tank.hpp 完成。
|
|
4
|
+
// 标准输入一行一条请求,标准输出给回复:
|
|
5
|
+
//
|
|
6
|
+
// HELLO → OK <引擎版本> <首回合预算> <每回合预算> <首回合墙上上限> <每回合墙上上限>
|
|
7
|
+
// <每 CPU 毫秒多少个读数单位> <这个数怎么来的> <风格名 × N>
|
|
8
|
+
// NEW <种子> <风格> → OK,然后是完整局面文本(以 END 行结束)
|
|
9
|
+
// LOAD → 后面跟一份完整局面文本(到 END 行);回 OK 或 ERR <原因>
|
|
10
|
+
// STATE → 完整局面文本
|
|
11
|
+
// CHECK <座位> <一行回复> → OK <快速> <火力> <装甲>(并记下,等 STEP 时执行)或 ERR <原因>
|
|
12
|
+
// STEP → 用记下的命令结算一回合(没记的座位三辆都不动),回:OK,回合段文本(到 END 行)
|
|
13
|
+
// STEPX → 同上,但回:OK,若干行 "EV <事件>",回合段文本,6 行 "X ...",最后一行 DONE
|
|
14
|
+
// INFO → 6 行 "X ...",最后一行 DONE
|
|
15
|
+
//
|
|
16
|
+
// X <坦克> <还要几回合复活> <还要装填几回合> <场上子弹数> <子弹上限> <下回合能否开炮>
|
|
17
|
+
//
|
|
18
|
+
// 请求格式不对回一行 ERR,不退出。
|
|
19
|
+
|
|
20
|
+
#include "../tank.hpp"
|
|
21
|
+
#include "cycles.hpp"
|
|
22
|
+
|
|
23
|
+
#include <cstdio>
|
|
24
|
+
#include <cstdlib>
|
|
25
|
+
#include <cstring>
|
|
26
|
+
#include <string>
|
|
27
|
+
|
|
28
|
+
namespace {
|
|
29
|
+
|
|
30
|
+
void put(const std::string& text) { std::fwrite(text.data(), 1, text.size(), stdout); }
|
|
31
|
+
|
|
32
|
+
void put_info(const tank::State& s) {
|
|
33
|
+
for (int i = 0; i < 6; ++i)
|
|
34
|
+
std::printf("X %d %d %d %d %d %d\n", i, tank::respawn_in(s, i), tank::reload_left(s, i), tank::bullets_out(s, i),
|
|
35
|
+
tank::bullet_cap(s, i), tank::can_fire(s, i) ? 1 : 0);
|
|
36
|
+
std::printf("DONE\n");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
bool read_line(std::string& line) {
|
|
40
|
+
line.clear();
|
|
41
|
+
char buf[1024];
|
|
42
|
+
while (std::fgets(buf, sizeof buf, stdin)) {
|
|
43
|
+
line += buf;
|
|
44
|
+
if (!line.empty() && line.back() == '\n') {
|
|
45
|
+
while (!line.empty() && (line.back() == '\n' || line.back() == '\r')) line.pop_back();
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return !line.empty();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
} // namespace
|
|
53
|
+
|
|
54
|
+
int main() {
|
|
55
|
+
tank::State s = tank::make_game(1);
|
|
56
|
+
tank::Orders pending[2];
|
|
57
|
+
tank::EventLog log;
|
|
58
|
+
std::string line;
|
|
59
|
+
while (read_line(line)) {
|
|
60
|
+
if (line == "HELLO") {
|
|
61
|
+
const cycles::Scale scale = cycles::measure();
|
|
62
|
+
std::printf("OK %s %d %d %d %d %lld %s", tank::ENGINE_VERSION, tank::BUDGET_FIRST_MS, tank::BUDGET_TURN_MS,
|
|
63
|
+
tank::BUDGET_WALL_FIRST_MS, tank::BUDGET_WALL_TURN_MS, scale.per_ms, scale.source);
|
|
64
|
+
for (const char* name : tank::STYLE_NAMES) std::printf(" %s", name);
|
|
65
|
+
std::printf("\n");
|
|
66
|
+
} else if (line.rfind("NEW ", 0) == 0) {
|
|
67
|
+
unsigned long long seed = 0;
|
|
68
|
+
int style = -1;
|
|
69
|
+
if (std::sscanf(line.c_str() + 4, "%llu %d", &seed, &style) < 1) {
|
|
70
|
+
std::printf("ERR NEW 后面应为 种子 和 风格\n");
|
|
71
|
+
} else {
|
|
72
|
+
s = tank::make_game(seed, style);
|
|
73
|
+
pending[0] = pending[1] = tank::Orders{};
|
|
74
|
+
std::printf("OK\n");
|
|
75
|
+
put(tank::to_text(s));
|
|
76
|
+
}
|
|
77
|
+
} else if (line == "LOAD") {
|
|
78
|
+
std::string text, row;
|
|
79
|
+
while (read_line(row)) {
|
|
80
|
+
text += row;
|
|
81
|
+
text += '\n';
|
|
82
|
+
if (row == "END") break;
|
|
83
|
+
}
|
|
84
|
+
std::string err;
|
|
85
|
+
tank::State loaded;
|
|
86
|
+
if (tank::from_text(text, loaded, &err)) {
|
|
87
|
+
s = loaded;
|
|
88
|
+
pending[0] = pending[1] = tank::Orders{};
|
|
89
|
+
std::printf("OK\n");
|
|
90
|
+
} else {
|
|
91
|
+
std::printf("ERR %s\n", err.c_str());
|
|
92
|
+
}
|
|
93
|
+
} else if (line == "STATE") {
|
|
94
|
+
put(tank::to_text(s));
|
|
95
|
+
} else if (line.rfind("CHECK ", 0) == 0) {
|
|
96
|
+
const char seat = line.size() > 6 ? line[6] : '?';
|
|
97
|
+
std::string err;
|
|
98
|
+
tank::Orders o;
|
|
99
|
+
if ((seat != '0' && seat != '1') || line.size() < 8 || line[7] != ' ') {
|
|
100
|
+
std::printf("ERR CHECK 后面应为 座位 和 一行回复\n");
|
|
101
|
+
} else if (tank::parse_orders(std::string_view(line).substr(8), s, o, &err)) {
|
|
102
|
+
pending[seat - '0'] = o;
|
|
103
|
+
std::printf("OK %s %s %s\n", tank::cmd_text(o.cmd[0]).c_str(), tank::cmd_text(o.cmd[1]).c_str(),
|
|
104
|
+
tank::cmd_text(o.cmd[2]).c_str());
|
|
105
|
+
} else {
|
|
106
|
+
std::printf("ERR %s\n", err.c_str());
|
|
107
|
+
}
|
|
108
|
+
} else if (line == "STEP" || line == "STEPX") {
|
|
109
|
+
const bool verbose = line == "STEPX";
|
|
110
|
+
tank::step(s, pending[0], pending[1], verbose ? &log : nullptr);
|
|
111
|
+
pending[0] = pending[1] = tank::Orders{};
|
|
112
|
+
std::printf("OK\n");
|
|
113
|
+
if (verbose)
|
|
114
|
+
for (const tank::Event& e : log.list) std::printf("EV %s\n", tank::event_text(e).c_str());
|
|
115
|
+
put(tank::turn_text(s));
|
|
116
|
+
if (verbose) put_info(s);
|
|
117
|
+
} else if (line == "INFO") {
|
|
118
|
+
put_info(s);
|
|
119
|
+
} else {
|
|
120
|
+
std::printf("ERR 不认识的请求\n");
|
|
121
|
+
}
|
|
122
|
+
std::fflush(stdout);
|
|
123
|
+
}
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// speed.cpp —— 测速:跑两段固定的工作量,报告用了多久。用来确认机器的状态(电源模式、主频、别的任务)有没有变。
|
|
2
|
+
//
|
|
3
|
+
// 用法:speed [--cpu 逻辑处理器编号] [--repeat 次数]
|
|
4
|
+
// 输出一行:
|
|
5
|
+
// SPEED cpu=<编号或 -1> ghz=<实际主频> loop_ms=<空转循环的墙上毫秒> engine_ms=<引擎工作量的墙上毫秒>
|
|
6
|
+
// engine_cpu_ms=<引擎工作量的 CPU 毫秒> per_ms=<每 CPU 毫秒多少个读数单位> source=<这个数怎么来的>
|
|
7
|
+
//
|
|
8
|
+
// 两段工作量:
|
|
9
|
+
// 空转循环:2 亿次一拍一次的加法。次数除以用时就是这个核此刻的实际主频(不受内存和缓存影响)。
|
|
10
|
+
// 引擎工作量:用固定的伪随机命令打 400 局不记录的对局(约 12 万次 step)。
|
|
11
|
+
// 各重复若干次取最快的一次(最快的一次最不受别的任务打扰)。
|
|
12
|
+
|
|
13
|
+
#include "../tank.hpp"
|
|
14
|
+
#include "cycles.hpp"
|
|
15
|
+
|
|
16
|
+
#include <chrono>
|
|
17
|
+
#include <cstdio>
|
|
18
|
+
#include <cstdlib>
|
|
19
|
+
#include <cstring>
|
|
20
|
+
|
|
21
|
+
#ifdef _WIN32
|
|
22
|
+
#include <windows.h>
|
|
23
|
+
#endif
|
|
24
|
+
|
|
25
|
+
namespace {
|
|
26
|
+
|
|
27
|
+
double wall_ms() {
|
|
28
|
+
return std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
double spin_loop() {
|
|
32
|
+
unsigned long long x = 0;
|
|
33
|
+
const double t0 = wall_ms();
|
|
34
|
+
for (unsigned long long i = 0; i < 200000000ULL; ++i) {
|
|
35
|
+
x += 1;
|
|
36
|
+
#if defined(__GNUC__)
|
|
37
|
+
__asm__ volatile("" : "+r"(x)); // 不让编译器把循环算掉;每次迭代一条一拍的加法
|
|
38
|
+
#endif
|
|
39
|
+
}
|
|
40
|
+
const double t1 = wall_ms();
|
|
41
|
+
return x ? t1 - t0 : 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
unsigned long long g_sink = 0;
|
|
45
|
+
|
|
46
|
+
void engine_work() {
|
|
47
|
+
unsigned long long r = 88172645463325252ULL;
|
|
48
|
+
for (int game = 0; game < 400; ++game) {
|
|
49
|
+
tank::State s = tank::make_game(1000 + game);
|
|
50
|
+
while (!s.done) {
|
|
51
|
+
tank::Orders o[2];
|
|
52
|
+
for (int seat = 0; seat < 2; ++seat)
|
|
53
|
+
for (int type = 0; type < 3; ++type) {
|
|
54
|
+
r ^= r << 13;
|
|
55
|
+
r ^= r >> 7;
|
|
56
|
+
r ^= r << 17;
|
|
57
|
+
tank::Cmd list[tank::N_CMDS];
|
|
58
|
+
const int n = tank::legal_commands(s, tank::tank_id(seat, type), list);
|
|
59
|
+
o[seat].cmd[type] = list[(r >> 24) % unsigned(n)];
|
|
60
|
+
}
|
|
61
|
+
tank::step(s, o[0], o[1]);
|
|
62
|
+
}
|
|
63
|
+
g_sink += s.wall_hash + unsigned(s.turn);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
} // namespace
|
|
68
|
+
|
|
69
|
+
int main(int argc, char** argv) {
|
|
70
|
+
int cpu = -1, repeat = 5;
|
|
71
|
+
for (int k = 1; k + 1 < argc; ++k) {
|
|
72
|
+
if (!std::strcmp(argv[k], "--cpu")) cpu = std::atoi(argv[++k]);
|
|
73
|
+
else if (!std::strcmp(argv[k], "--repeat")) repeat = std::atoi(argv[++k]);
|
|
74
|
+
}
|
|
75
|
+
#ifdef _WIN32
|
|
76
|
+
if (cpu >= 0 && cpu < 64 && !SetThreadAffinityMask(GetCurrentThread(), DWORD_PTR(1) << cpu)) {
|
|
77
|
+
std::fprintf(stderr, "绑不到逻辑处理器 %d\n", cpu);
|
|
78
|
+
return 2;
|
|
79
|
+
}
|
|
80
|
+
#else
|
|
81
|
+
if (cpu >= 0) std::fprintf(stderr, "这个系统上没有实现绑定处理器,忽略 --cpu\n");
|
|
82
|
+
#endif
|
|
83
|
+
const cycles::Scale scale = cycles::measure();
|
|
84
|
+
tank::budget_setup(tank::BUDGET_CPU, double(scale.per_ms));
|
|
85
|
+
double best_loop = 1e18, best_wall = 1e18, best_cpu = 1e18;
|
|
86
|
+
for (int k = 0; k < (repeat < 1 ? 1 : repeat); ++k) {
|
|
87
|
+
const double loop = spin_loop();
|
|
88
|
+
if (loop < best_loop) best_loop = loop;
|
|
89
|
+
tank::budget_begin(0);
|
|
90
|
+
const double t0 = wall_ms();
|
|
91
|
+
engine_work();
|
|
92
|
+
const double wall = wall_ms() - t0, used = tank::budget_used();
|
|
93
|
+
if (wall < best_wall) best_wall = wall;
|
|
94
|
+
if (used < best_cpu) best_cpu = used;
|
|
95
|
+
}
|
|
96
|
+
std::printf("SPEED cpu=%d ghz=%.3f loop_ms=%.2f engine_ms=%.2f engine_cpu_ms=%.2f per_ms=%lld source=%s sink=%llu\n", cpu,
|
|
97
|
+
200000000.0 / best_loop / 1e6, best_loop, best_wall, best_cpu, scale.per_ms, scale.source, g_sink % 1000);
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
setlocal
|
|
3
|
+
chcp 65001 >nul
|
|
4
|
+
rem Find a Python 3.10+ and run the given script in this folder. No arguments: show the contest clock.
|
|
5
|
+
rem Usage: .\run.cmd arena.py check
|
|
6
|
+
rem (Keep this file ASCII-only except the last line: cmd.exe loses its place in a batch file
|
|
7
|
+
rem that has multi-byte characters after the code page is switched.)
|
|
8
|
+
rem "call" is needed: python3 may be a .cmd shim, and without call control never comes back.
|
|
9
|
+
set "CHECK=import sys; v = sys.version_info; sys.exit(v[0] != 3 or v[1] not in range(10, 100))"
|
|
10
|
+
set "PY="
|
|
11
|
+
for %%P in (python python3) do if not defined PY (
|
|
12
|
+
call %%P -c "%CHECK%" >nul 2>nul && set "PY=%%P"
|
|
13
|
+
)
|
|
14
|
+
if not defined PY (
|
|
15
|
+
call py -3 -c "%CHECK%" >nul 2>nul && set "PY=py -3"
|
|
16
|
+
)
|
|
17
|
+
rem Not on PATH: look in the usual install locations.
|
|
18
|
+
if not defined PY for /d %%D in ("%LOCALAPPDATA%\Programs\Python\Python3*" "%ProgramFiles%\Python3*") do if not defined PY (
|
|
19
|
+
if exist "%%~D\python.exe" call "%%~D\python.exe" -c "%CHECK%" >nul 2>nul && set "PY="%%~D\python.exe""
|
|
20
|
+
)
|
|
21
|
+
if not defined PY if exist "%USERPROFILE%\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\python.exe" (
|
|
22
|
+
set "PY="%USERPROFILE%\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\python.exe""
|
|
23
|
+
)
|
|
24
|
+
if not defined PY goto nopython
|
|
25
|
+
pushd "%~dp0"
|
|
26
|
+
if "%~1"=="" (call %PY% -X utf8 clock.py) else (call %PY% -X utf8 %*)
|
|
27
|
+
set "CODE=%errorlevel%"
|
|
28
|
+
popd
|
|
29
|
+
exit /b %CODE%
|
|
30
|
+
:nopython
|
|
31
|
+
echo 没有找到 Python 3.10 以上的版本(试过 python、python3、py -3 和常见安装位置)。请把这个情况报告出来。 1>&2 & exit /b 1
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# 找一个 Python 3.10 以上,在本目录运行给定的脚本。不带参数时显示比赛用时。
|
|
3
|
+
# 用法:sh run.sh arena.py check
|
|
4
|
+
cd "$(dirname -- "$0")" || exit 1
|
|
5
|
+
[ "$#" -eq 0 ] && set -- clock.py
|
|
6
|
+
ok='import sys; sys.exit(sys.version_info < (3, 10))'
|
|
7
|
+
for py in python3 python; do
|
|
8
|
+
if command -v "$py" >/dev/null 2>&1 && "$py" -c "$ok" >/dev/null 2>&1; then
|
|
9
|
+
exec "$py" -X utf8 "$@"
|
|
10
|
+
fi
|
|
11
|
+
done
|
|
12
|
+
# Windows 上只装了 py 启动器的情况。
|
|
13
|
+
if command -v py >/dev/null 2>&1 && py -3 -c "$ok" >/dev/null 2>&1; then
|
|
14
|
+
exec py -3 -X utf8 "$@"
|
|
15
|
+
fi
|
|
16
|
+
# PATH 里没有:再看几个常见的安装位置。
|
|
17
|
+
for py in "$LOCALAPPDATA"/Programs/Python/Python3*/python.exe "$HOME"/.cache/codex-runtimes/codex-primary-runtime/dependencies/python/python.exe /usr/local/bin/python3 /usr/bin/python3; do
|
|
18
|
+
if [ -x "$py" ] && "$py" -c "$ok" >/dev/null 2>&1; then
|
|
19
|
+
exec "$py" -X utf8 "$@"
|
|
20
|
+
fi
|
|
21
|
+
done
|
|
22
|
+
echo '没有找到 Python 3.10 以上的版本(试过 python3、python、py -3 和常见安装位置)。请把这个情况报告出来。' >&2
|
|
23
|
+
exit 1
|