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,487 @@
|
|
|
1
|
+
// bot.cpp —— 一层 maxmin 精确前推 + 启发式评估。
|
|
2
|
+
#include "tank.hpp"
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
#include <cstdio>
|
|
5
|
+
#include <cstdlib>
|
|
6
|
+
|
|
7
|
+
using namespace tank;
|
|
8
|
+
|
|
9
|
+
static int g_me;
|
|
10
|
+
static int dist_base[2][CELLS]; // dist_base[seat][c]:c 到 seat 的基地要走几步(砖墙当可走)
|
|
11
|
+
static bool danger[CELLS]; // 敌方子弹本回合两个小步内会经过的格子
|
|
12
|
+
static bool g_threat; // 有敌车能直接打到 / 贴近我的基地
|
|
13
|
+
static int dist_supply[CELLS]; // 到最近补给点的步数(砖墙当可走)
|
|
14
|
+
|
|
15
|
+
// 多源 BFS:从全部补给点出发
|
|
16
|
+
static void bfs_supply(const State& s) {
|
|
17
|
+
static int queue[CELLS];
|
|
18
|
+
for (int c = 0; c < CELLS; ++c) dist_supply[c] = 9999;
|
|
19
|
+
int head = 0, tail = 0;
|
|
20
|
+
for (int k = 0; k < s.n_supply; ++k) {
|
|
21
|
+
dist_supply[s.supply[k]] = 0;
|
|
22
|
+
queue[tail++] = s.supply[k];
|
|
23
|
+
}
|
|
24
|
+
while (head < tail) {
|
|
25
|
+
const int c = queue[head++];
|
|
26
|
+
for (int d = 0; d < 4; ++d) {
|
|
27
|
+
const int nx = c + DIR_DELTA[d];
|
|
28
|
+
const uint8_t g = s.grid[nx];
|
|
29
|
+
if ((g == EMPTY || g == BRICK) && dist_supply[nx] > dist_supply[c] + 1) {
|
|
30
|
+
dist_supply[nx] = dist_supply[c] + 1;
|
|
31
|
+
queue[tail++] = nx;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 开局抢星:第一批星第 10 回合末出现。让 fast 直奔补给点蹲守(有子弹在飞时交给搜索)。
|
|
38
|
+
// 返回是否接管了 fast 的命令。
|
|
39
|
+
static Cmd opening_star_cmd(const State& s, int me) {
|
|
40
|
+
if (s.turn >= 9 || s.n_bullets > 0) return STAY;
|
|
41
|
+
const int i = tank_id(me, FAST);
|
|
42
|
+
if (!s.tank[i].alive) return STAY;
|
|
43
|
+
const int p = s.tank[i].pos;
|
|
44
|
+
if (dist_supply[p] == 0) return STAY; // 已经在补给点上:蹲着等星
|
|
45
|
+
// 优先两步、再一步,走到 dist_supply 更小的空格;挡路的是砖就朝它开炮
|
|
46
|
+
Cmd bestc = STAY;
|
|
47
|
+
int bestd = dist_supply[p];
|
|
48
|
+
for (int d1 = 0; d1 < 4; ++d1) {
|
|
49
|
+
const int p1 = p + DIR_DELTA[d1];
|
|
50
|
+
const uint8_t g1 = s.grid[p1];
|
|
51
|
+
if (g1 == BRICK) {
|
|
52
|
+
if (dist_supply[p1] < bestd && can_fire(s, i)) { bestd = dist_supply[p1]; bestc = cmd_fire(d1); }
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (g1 != EMPTY || tank_at(s, p1) >= 0) continue;
|
|
56
|
+
if (dist_supply[p1] < bestd) { bestd = dist_supply[p1]; bestc = cmd_move(d1); }
|
|
57
|
+
for (int d2 = 0; d2 < 4; ++d2) {
|
|
58
|
+
const int p2 = p1 + DIR_DELTA[d2];
|
|
59
|
+
if (s.grid[p2] != EMPTY || tank_at(s, p2) >= 0) continue;
|
|
60
|
+
if (dist_supply[p2] < bestd) { bestd = dist_supply[p2]; bestc = cmd_move2(d1, d2); }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return bestc;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static void bfs(const State& s, int src, int* dist) {
|
|
67
|
+
static int queue[CELLS];
|
|
68
|
+
for (int c = 0; c < CELLS; ++c) dist[c] = 9999;
|
|
69
|
+
int head = 0, tail = 0;
|
|
70
|
+
dist[src] = 0;
|
|
71
|
+
queue[tail++] = src;
|
|
72
|
+
while (head < tail) {
|
|
73
|
+
const int c = queue[head++];
|
|
74
|
+
for (int d = 0; d < 4; ++d) {
|
|
75
|
+
const int nx = c + DIR_DELTA[d];
|
|
76
|
+
const uint8_t g = s.grid[nx];
|
|
77
|
+
if ((g == EMPTY || g == BRICK) && dist[nx] > dist[c] + 1) {
|
|
78
|
+
dist[nx] = dist[c] + 1;
|
|
79
|
+
queue[tail++] = nx;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 从 pos 朝 d 看炮线(对 seat 而言)。bricks 输出途中砖墙数。
|
|
86
|
+
static double fire_line_score(const State& s, int seat, int pos, int d, int level, int* bricks = nullptr) {
|
|
87
|
+
int nb = 0;
|
|
88
|
+
for (int c = pos + DIR_DELTA[d];; c += DIR_DELTA[d]) {
|
|
89
|
+
const uint8_t g = s.grid[c];
|
|
90
|
+
if (g == EMPTY || g == WATER) {
|
|
91
|
+
const int t = tank_at(s, c);
|
|
92
|
+
if (t >= 0) {
|
|
93
|
+
if (bricks) *bricks = nb;
|
|
94
|
+
return seat_of(t) == seat ? -70.0 : 55.0;
|
|
95
|
+
}
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (bricks) *bricks = nb;
|
|
99
|
+
if (g == BRICK) { ++nb; continue; }
|
|
100
|
+
if (g == STEEL) return (level >= 2) ? 6.0 : -2.0;
|
|
101
|
+
if (g == BASE0 + (1 - seat)) return 45.0 - 6.0 * nb; // 隔砖也能打穿,慢一些
|
|
102
|
+
if (g == BASE0 + seat) return -120.0;
|
|
103
|
+
return -2.0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 从 pos 朝某方向看不隔着任何墙就到 target_base(纯空地/水)。
|
|
108
|
+
static bool clear_line_to_base(const State& s, int pos, int base_cell) {
|
|
109
|
+
const int bx = cell_x(base_cell), by = cell_y(base_cell);
|
|
110
|
+
const int px = cell_x(pos), py = cell_y(pos);
|
|
111
|
+
if (px != bx && py != by) return false;
|
|
112
|
+
int d;
|
|
113
|
+
if (px == bx) d = by > py ? DOWN : UP;
|
|
114
|
+
else d = bx > px ? RIGHT : LEFT;
|
|
115
|
+
for (int c = pos + DIR_DELTA[d];; c += DIR_DELTA[d]) {
|
|
116
|
+
if (c == base_cell) return true;
|
|
117
|
+
const uint8_t g = s.grid[c];
|
|
118
|
+
if (g != EMPTY && g != WATER) return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 从 pos 到 foe 基地的四条直线里,最少隔几块砖(到不了返回 99;0 = 无墙直通)。
|
|
123
|
+
static int min_bricks_to_base(const State& s, int seat, int pos) {
|
|
124
|
+
const int base = s.base_pos[1 - seat];
|
|
125
|
+
if (cell_x(pos) != cell_x(base) && cell_y(pos) != cell_y(base)) return 99;
|
|
126
|
+
int d;
|
|
127
|
+
if (cell_x(pos) == cell_x(base)) d = cell_y(base) > cell_y(pos) ? DOWN : UP;
|
|
128
|
+
else d = cell_x(base) > cell_x(pos) ? RIGHT : LEFT;
|
|
129
|
+
int nb = 0;
|
|
130
|
+
for (int c = pos + DIR_DELTA[d];; c += DIR_DELTA[d]) {
|
|
131
|
+
if (c == base) return nb;
|
|
132
|
+
const uint8_t g = s.grid[c];
|
|
133
|
+
if (g == BRICK) { ++nb; continue; }
|
|
134
|
+
if (g == EMPTY || g == WATER) continue;
|
|
135
|
+
return 99;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
static double evaluate(const State& s) {
|
|
140
|
+
const int me = g_me, opp = 1 - me;
|
|
141
|
+
if (s.done) {
|
|
142
|
+
if (s.winner == me) return 1e6 - s.turn;
|
|
143
|
+
if (s.winner == opp) return -1e6 + s.turn;
|
|
144
|
+
return 0.0;
|
|
145
|
+
}
|
|
146
|
+
double v = 0;
|
|
147
|
+
v += 140.0 * (s.base_hp[me] - s.base_hp[opp]);
|
|
148
|
+
v += 40.0 * (s.kills[me] - s.kills[opp]);
|
|
149
|
+
v += 10.0 * (s.stars_eaten[me] - s.stars_eaten[opp]);
|
|
150
|
+
for (int i = 0; i < 6; ++i) {
|
|
151
|
+
const Tank& t = s.tank[i];
|
|
152
|
+
const int seat = seat_of(i), foe = 1 - seat;
|
|
153
|
+
double tv;
|
|
154
|
+
if (t.alive) {
|
|
155
|
+
tv = 20.0 + 5.0 * t.hp + 14.0 * t.level;
|
|
156
|
+
const int dfb = dist_base[foe][t.pos];
|
|
157
|
+
tv -= 4.0 * dfb;
|
|
158
|
+
tv += 4.0 * (12 - dfb < 0 ? 0 : 12 - dfb); // 贴近对方基地额外加分
|
|
159
|
+
tv += 5.0 * (7 - dfb < 0 ? 0 : 7 - dfb); // 非常贴近再加(攻其基地/防我基地都覆盖)
|
|
160
|
+
double best_line = -1e9;
|
|
161
|
+
for (int d = 0; d < 4; ++d) {
|
|
162
|
+
const double ls = fire_line_score(s, seat, t.pos, d, t.level);
|
|
163
|
+
if (ls > best_line) best_line = ls;
|
|
164
|
+
}
|
|
165
|
+
tv += 0.25 * best_line;
|
|
166
|
+
// 能持续轰对方基地的位置极其关键:值一大段未来伤害。
|
|
167
|
+
// 不用装填的车(火力型 / 1 级以上)每回合都能开炮,磨血速度翻倍,位置更值钱。
|
|
168
|
+
const bool no_reload = (type_of(i) == FIRE) || t.level >= 1;
|
|
169
|
+
const int nb2 = min_bricks_to_base(s, seat, t.pos);
|
|
170
|
+
if (nb2 == 0) tv += no_reload ? 320.0 : 150.0;
|
|
171
|
+
else if (nb2 < 99) tv += (60.0 - 8.0 * nb2) * (no_reload ? 1.0 : 0.6);
|
|
172
|
+
// 炮手的拦截/护航梯度:我是炮手时敌车靠近要扣分(对方视角对称,
|
|
173
|
+
// 等价于"我的车靠近对方炮手会加分"——产生跨多回合的拦截拉力)
|
|
174
|
+
if (nb2 == 0)
|
|
175
|
+
for (int j = 3 * foe; j < 3 * foe + 3; ++j)
|
|
176
|
+
if (s.tank[j].alive) {
|
|
177
|
+
const int md = std::abs(cell_x(s.tank[j].pos) - cell_x(t.pos))
|
|
178
|
+
+ std::abs(cell_y(s.tank[j].pos) - cell_y(t.pos));
|
|
179
|
+
tv -= 24.0 / (1.0 + md);
|
|
180
|
+
}
|
|
181
|
+
// 站在对方出生点上、对方下回合末复活:会被炸毁
|
|
182
|
+
for (int j = 3 * foe; j < 3 * foe + 3; ++j)
|
|
183
|
+
if (!s.tank[j].alive && t.pos == s.spawn[j] && respawn_in(s, j) <= 1) tv -= 30.0;
|
|
184
|
+
} else {
|
|
185
|
+
tv = -2.0 * respawn_in(s, i);
|
|
186
|
+
}
|
|
187
|
+
v += (seat == me ? tv : -tv);
|
|
188
|
+
}
|
|
189
|
+
// 星星争夺:有星或快出星时,我的车比对方更靠近补给点要加分(等级=射速+血量,是胜负手)
|
|
190
|
+
{
|
|
191
|
+
const bool soon = s.turn + 1 >= s.P.supply_first &&
|
|
192
|
+
(s.turn + 1 - s.P.supply_first) % s.P.supply_period >= s.P.supply_period - 5;
|
|
193
|
+
if (s.star_mask || soon) {
|
|
194
|
+
for (int q = 0; q < s.n_supply; ++q) {
|
|
195
|
+
const bool has = (s.star_mask >> q) & 1;
|
|
196
|
+
if (!has && !soon) continue;
|
|
197
|
+
const int sp = s.supply[q];
|
|
198
|
+
int dm = 99, dt = 99;
|
|
199
|
+
for (int i = 0; i < 6; ++i) {
|
|
200
|
+
if (!s.tank[i].alive) continue;
|
|
201
|
+
const int d = std::abs(cell_x(sp) - cell_x(s.tank[i].pos)) + std::abs(cell_y(sp) - cell_y(s.tank[i].pos));
|
|
202
|
+
if (seat_of(i) == me) { if (d < dm) dm = d; }
|
|
203
|
+
else if (d < dt) dt = d;
|
|
204
|
+
}
|
|
205
|
+
v += (has ? 5.0 : 2.0) * (dt - dm);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return v;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
struct Ranked { Cmd c; double sc; };
|
|
213
|
+
|
|
214
|
+
// 按类别挑候选命令:保证开炮、推进、躲避/回防都有代表进入模拟,而不是被单一分数剪没。
|
|
215
|
+
static int rank_commands(const State& s, int i, int seat, Ranked* out, int k, bool guard) {
|
|
216
|
+
Cmd list[N_CMDS];
|
|
217
|
+
const int n = legal_commands(s, i, list);
|
|
218
|
+
const Tank& t = s.tank[i];
|
|
219
|
+
const int foe = 1 - seat;
|
|
220
|
+
int m = 0;
|
|
221
|
+
auto add = [&](Cmd c) {
|
|
222
|
+
for (int j = 0; j < m; ++j) if (out[j].c == c) return;
|
|
223
|
+
if (m < k) out[m++] = Ranked{c, 0};
|
|
224
|
+
};
|
|
225
|
+
add(STAY);
|
|
226
|
+
// 守家车:优先挑靠近己方基地、面向中场拦截的命令
|
|
227
|
+
if (guard) {
|
|
228
|
+
for (int pick = 0; pick < 2; ++pick) {
|
|
229
|
+
Cmd bc = 0; double bs = -1e9;
|
|
230
|
+
for (int j = 0; j < n; ++j) {
|
|
231
|
+
if (!is_move(list[j]) && !is_move2(list[j])) continue;
|
|
232
|
+
bool dup = false;
|
|
233
|
+
for (int q = 0; q < m; ++q) if (out[q].c == list[j]) dup = true;
|
|
234
|
+
if (dup) continue;
|
|
235
|
+
int d1, d2 = -1;
|
|
236
|
+
if (is_move(list[j])) d1 = list[j] - 1;
|
|
237
|
+
else { d1 = (list[j] - 9) >> 2; d2 = (list[j] - 9) & 3; }
|
|
238
|
+
int p1 = t.pos + DIR_DELTA[d1];
|
|
239
|
+
if (s.grid[p1] != EMPTY) p1 = t.pos;
|
|
240
|
+
int p2 = p1;
|
|
241
|
+
if (d2 >= 0 && s.grid[p1 + DIR_DELTA[d2]] == EMPTY) p2 = p1 + DIR_DELTA[d2];
|
|
242
|
+
double sc = 3.0 * (dist_base[seat][t.pos] - dist_base[seat][p2]);
|
|
243
|
+
// 有敌车逼近时,改朝最近的敌车去拦截
|
|
244
|
+
if (g_threat)
|
|
245
|
+
for (int e = 3 * foe; e < 3 * foe + 3; ++e)
|
|
246
|
+
if (s.tank[e].alive && dist_base[seat][s.tank[e].pos] <= 6)
|
|
247
|
+
sc += 2.0 * (std::abs(cell_x(s.tank[e].pos) - cell_x(t.pos)) + std::abs(cell_y(s.tank[e].pos) - cell_y(t.pos))
|
|
248
|
+
- std::abs(cell_x(s.tank[e].pos) - cell_x(p2)) - std::abs(cell_y(s.tank[e].pos) - cell_y(p2)));
|
|
249
|
+
if (sc > bs) { bs = sc; bc = list[j]; }
|
|
250
|
+
}
|
|
251
|
+
if (bc) add(bc);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// 移动落点(假定走成)
|
|
255
|
+
auto endpoint = [&](Cmd c) {
|
|
256
|
+
int d1, d2 = -1;
|
|
257
|
+
if (is_move(c)) d1 = c - 1;
|
|
258
|
+
else { d1 = (c - 9) >> 2; d2 = (c - 9) & 3; }
|
|
259
|
+
int p1 = t.pos + DIR_DELTA[d1];
|
|
260
|
+
if (s.grid[p1] != EMPTY) p1 = t.pos;
|
|
261
|
+
int p2 = p1;
|
|
262
|
+
if (d2 >= 0 && s.grid[p1 + DIR_DELTA[d2]] == EMPTY) p2 = p1 + DIR_DELTA[d2];
|
|
263
|
+
return p2;
|
|
264
|
+
};
|
|
265
|
+
// 1) 开炮:炮线得分前 2
|
|
266
|
+
for (int pick = 0; pick < 2; ++pick) {
|
|
267
|
+
Cmd bc = 0; double bs = -1e9;
|
|
268
|
+
for (int j = 0; j < n; ++j) {
|
|
269
|
+
if (!is_fire(list[j])) continue;
|
|
270
|
+
bool dup = false;
|
|
271
|
+
for (int q = 0; q < m; ++q) if (out[q].c == list[j]) dup = true;
|
|
272
|
+
if (dup) continue;
|
|
273
|
+
const double sc = fire_line_score(s, seat, t.pos, list[j] - 5, t.level);
|
|
274
|
+
if (sc > bs) { bs = sc; bc = list[j]; }
|
|
275
|
+
}
|
|
276
|
+
if (bc && bs > -5.0) add(bc);
|
|
277
|
+
}
|
|
278
|
+
// 2) 推进:向对方基地靠近最多的 6 步(危险落点扣一点,但不至于剪光)
|
|
279
|
+
for (int pick = 0; pick < 6; ++pick) {
|
|
280
|
+
Cmd bc = 0; double bs = -1e9;
|
|
281
|
+
for (int j = 0; j < n; ++j) {
|
|
282
|
+
if (!is_move(list[j]) && !is_move2(list[j])) continue;
|
|
283
|
+
bool dup = false;
|
|
284
|
+
for (int q = 0; q < m; ++q) if (out[q].c == list[j]) dup = true;
|
|
285
|
+
if (dup) continue;
|
|
286
|
+
const int p2 = endpoint(list[j]);
|
|
287
|
+
double sc = 3.0 * (dist_base[foe][t.pos] - dist_base[foe][p2]);
|
|
288
|
+
if (is_move2(list[j])) sc += 1.0;
|
|
289
|
+
if (danger[p2]) sc -= 20.0;
|
|
290
|
+
if (sc > bs) { bs = sc; bc = list[j]; }
|
|
291
|
+
}
|
|
292
|
+
if (bc) add(bc);
|
|
293
|
+
}
|
|
294
|
+
// 3) 躲避 / 回防 / 吃星:再补 2 条
|
|
295
|
+
for (int pick = 0; pick < 2; ++pick) {
|
|
296
|
+
Cmd bc = 0; double bs = -1e9;
|
|
297
|
+
for (int j = 0; j < n; ++j) {
|
|
298
|
+
if (!is_move(list[j]) && !is_move2(list[j])) continue;
|
|
299
|
+
bool dup = false;
|
|
300
|
+
for (int q = 0; q < m; ++q) if (out[q].c == list[j]) dup = true;
|
|
301
|
+
if (dup) continue;
|
|
302
|
+
const int p2 = endpoint(list[j]);
|
|
303
|
+
double sc = 0;
|
|
304
|
+
if (danger[t.pos] && !danger[p2]) sc += 30.0;
|
|
305
|
+
if (danger[p2]) sc -= 30.0;
|
|
306
|
+
if (g_threat) sc += 3.0 * (dist_base[seat][t.pos] - dist_base[seat][p2]);
|
|
307
|
+
for (int q = 0; q < s.n_supply; ++q)
|
|
308
|
+
if ((s.star_mask >> q) & 1) {
|
|
309
|
+
const int sp = s.supply[q];
|
|
310
|
+
sc += 6.0 * (std::abs(cell_x(sp) - cell_x(t.pos)) + std::abs(cell_y(sp) - cell_y(t.pos))
|
|
311
|
+
- std::abs(cell_x(sp) - cell_x(p2)) - std::abs(cell_y(sp) - cell_y(p2)));
|
|
312
|
+
}
|
|
313
|
+
if (sc > bs) { bs = sc; bc = list[j]; }
|
|
314
|
+
}
|
|
315
|
+
if (bc) add(bc);
|
|
316
|
+
}
|
|
317
|
+
// 给候选补一个粗分数,供联合命令排序(强对手命令优先,利于剪枝)
|
|
318
|
+
for (int j = 0; j < m; ++j) {
|
|
319
|
+
const Cmd c = out[j].c;
|
|
320
|
+
if (c == STAY) out[j].sc = 0;
|
|
321
|
+
else if (is_fire(c)) out[j].sc = fire_line_score(s, seat, t.pos, c - 5, t.level);
|
|
322
|
+
else {
|
|
323
|
+
const int p2 = endpoint(c);
|
|
324
|
+
out[j].sc = 3.0 * (dist_base[foe][t.pos] - dist_base[foe][p2]) - (danger[p2] ? 20.0 : 0.0);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return m;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
static void compute_danger(const State& s, int me) {
|
|
331
|
+
for (int c = 0; c < CELLS; ++c) danger[c] = false;
|
|
332
|
+
for (int k = 0; k < s.n_bullets; ++k) {
|
|
333
|
+
const Bullet& b = s.bullet[k];
|
|
334
|
+
if (seat_of(b.owner) == me) continue;
|
|
335
|
+
int c = b.pos;
|
|
336
|
+
for (int stepn = 0; stepn < 2; ++stepn) {
|
|
337
|
+
c += DIR_DELTA[b.dir];
|
|
338
|
+
danger[c] = true;
|
|
339
|
+
const uint8_t g = s.grid[c];
|
|
340
|
+
if (g != EMPTY && g != WATER) break;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// rollout 用的联合贪心:在少量联合候选里挑"对方按 prev 行动时"评估最优的。
|
|
346
|
+
static Orders joint_greedy(const State& s, int seat, const Orders& opp_prev, int k) {
|
|
347
|
+
compute_danger(s, seat);
|
|
348
|
+
bool threat = false;
|
|
349
|
+
for (int i = 3 * (1 - seat); i < 3 * (1 - seat) + 3; ++i)
|
|
350
|
+
if (s.tank[i].alive &&
|
|
351
|
+
(dist_base[seat][s.tank[i].pos] <= 4 || clear_line_to_base(s, s.tank[i].pos, s.base_pos[seat])))
|
|
352
|
+
threat = true;
|
|
353
|
+
g_threat = threat;
|
|
354
|
+
Ranked rc[3][11];
|
|
355
|
+
int nn[3] = {1, 1, 1};
|
|
356
|
+
for (int type = 0; type < 3; ++type) {
|
|
357
|
+
const int i = tank_id(seat, type);
|
|
358
|
+
if (s.tank[i].alive) nn[type] = rank_commands(s, i, seat, rc[type], k, false);
|
|
359
|
+
else rc[type][0] = Ranked{STAY, 0};
|
|
360
|
+
}
|
|
361
|
+
Orders best;
|
|
362
|
+
double bv = -1e18;
|
|
363
|
+
for (int a = 0; a < nn[0]; ++a)
|
|
364
|
+
for (int b = 0; b < nn[1]; ++b)
|
|
365
|
+
for (int c = 0; c < nn[2]; ++c) {
|
|
366
|
+
Orders o;
|
|
367
|
+
o.cmd[0] = rc[0][a].c; o.cmd[1] = rc[1][b].c; o.cmd[2] = rc[2][c].c;
|
|
368
|
+
State nx = s;
|
|
369
|
+
step_as(nx, seat, o, opp_prev);
|
|
370
|
+
const double v = seat == g_me ? evaluate(nx) : -evaluate(nx);
|
|
371
|
+
if (v > bv) { bv = v; best = o; }
|
|
372
|
+
}
|
|
373
|
+
return best;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// 从局面 s 起双方交替最佳应手对推 depth 回合,返回终局评估(g_me 视角)。
|
|
377
|
+
static double rollout(State s, int depth) {
|
|
378
|
+
Orders prev[2] = {};
|
|
379
|
+
for (int t = 0; t < depth && !s.done; ++t) {
|
|
380
|
+
const Orders o0 = joint_greedy(s, 0, prev[1], 4);
|
|
381
|
+
const Orders o1 = joint_greedy(s, 1, prev[0], 4);
|
|
382
|
+
prev[0] = o0; prev[1] = o1;
|
|
383
|
+
step(s, o0, o1);
|
|
384
|
+
}
|
|
385
|
+
return evaluate(s);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
void init(const State& s, int me) {
|
|
389
|
+
g_me = me;
|
|
390
|
+
bfs(s, s.base_pos[0], dist_base[0]);
|
|
391
|
+
bfs(s, s.base_pos[1], dist_base[1]);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
Orders decide(const State& s, int me) {
|
|
395
|
+
g_me = me;
|
|
396
|
+
bfs(s, s.base_pos[0], dist_base[0]);
|
|
397
|
+
bfs(s, s.base_pos[1], dist_base[1]);
|
|
398
|
+
bfs_supply(s);
|
|
399
|
+
compute_danger(s, me);
|
|
400
|
+
|
|
401
|
+
g_threat = false;
|
|
402
|
+
for (int i = 3 * (1 - me); i < 3 * (1 - me) + 3; ++i)
|
|
403
|
+
if (s.tank[i].alive &&
|
|
404
|
+
(dist_base[me][s.tank[i].pos] <= 4 || clear_line_to_base(s, s.tank[i].pos, s.base_pos[me])))
|
|
405
|
+
g_threat = true;
|
|
406
|
+
|
|
407
|
+
const int K_MINE = 12, K_OPP = 6;
|
|
408
|
+
Ranked mine[3][14], opps[3][14];
|
|
409
|
+
int nm[3] = {1, 1, 1}, no[3] = {1, 1, 1};
|
|
410
|
+
for (int type = 0; type < 3; ++type) {
|
|
411
|
+
const int im = tank_id(me, type), io = tank_id(1 - me, type);
|
|
412
|
+
if (s.tank[im].alive) nm[type] = rank_commands(s, im, me, mine[type], K_MINE, false);
|
|
413
|
+
else { mine[type][0] = Ranked{STAY, 0}; }
|
|
414
|
+
if (s.tank[io].alive) no[type] = rank_commands(s, io, 1 - me, opps[type], K_OPP, false);
|
|
415
|
+
else { opps[type][0] = Ranked{STAY, 0}; }
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
struct Joint { Orders o; double sc; };
|
|
419
|
+
static Joint opp_joint[1000];
|
|
420
|
+
int njo = 0;
|
|
421
|
+
for (int a = 0; a < no[0]; ++a)
|
|
422
|
+
for (int b = 0; b < no[1]; ++b)
|
|
423
|
+
for (int c = 0; c < no[2]; ++c) {
|
|
424
|
+
Orders o;
|
|
425
|
+
o.cmd[0] = opps[0][a].c; o.cmd[1] = opps[1][b].c; o.cmd[2] = opps[2][c].c;
|
|
426
|
+
opp_joint[njo++] = Joint{o, opps[0][a].sc + opps[1][b].sc + opps[2][c].sc};
|
|
427
|
+
}
|
|
428
|
+
std::sort(opp_joint, opp_joint + njo, [](const Joint& x, const Joint& y) { return x.sc > y.sc; });
|
|
429
|
+
|
|
430
|
+
Orders best;
|
|
431
|
+
double best_val = -1e18;
|
|
432
|
+
int checked = 0;
|
|
433
|
+
// 主循环里记录前 12 个候选,稍后对它们做 rollout 再选
|
|
434
|
+
static Orders top_ord[12];
|
|
435
|
+
static double top_val[12];
|
|
436
|
+
int n_top = 0;
|
|
437
|
+
auto consider_top = [&](const Orders& mo, double v) {
|
|
438
|
+
if (n_top < 12) {
|
|
439
|
+
top_ord[n_top] = mo; top_val[n_top] = v; ++n_top;
|
|
440
|
+
} else {
|
|
441
|
+
int wi = 0;
|
|
442
|
+
for (int q = 1; q < 12; ++q) if (top_val[q] < top_val[wi]) wi = q;
|
|
443
|
+
if (v > top_val[wi]) { top_ord[wi] = mo; top_val[wi] = v; }
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
for (int a = 0; a < nm[0]; ++a)
|
|
447
|
+
for (int b = 0; b < nm[1]; ++b)
|
|
448
|
+
for (int c = 0; c < nm[2]; ++c) {
|
|
449
|
+
Orders mo;
|
|
450
|
+
mo.cmd[0] = mine[0][a].c; mo.cmd[1] = mine[1][b].c; mo.cmd[2] = mine[2][c].c;
|
|
451
|
+
double worst = 1e18, v_top = 0;
|
|
452
|
+
int j = 0;
|
|
453
|
+
for (; j < njo; ++j) {
|
|
454
|
+
State nx = s;
|
|
455
|
+
step_as(nx, me, mo, opp_joint[j].o);
|
|
456
|
+
const double v = evaluate(nx);
|
|
457
|
+
if (j == 0) v_top = v;
|
|
458
|
+
if (v < worst) worst = v;
|
|
459
|
+
if (worst < best_val && v_top < best_val) break;
|
|
460
|
+
if ((j & 15) == 15 && budget_left() < 8.0) break;
|
|
461
|
+
}
|
|
462
|
+
const double combined = 0.4 * worst + 0.6 * v_top;
|
|
463
|
+
if (combined > best_val) {
|
|
464
|
+
best_val = combined;
|
|
465
|
+
best = mo;
|
|
466
|
+
}
|
|
467
|
+
if (j == njo) consider_top(mo, combined); // 只收完整评估过的
|
|
468
|
+
if (++checked >= 12) {
|
|
469
|
+
checked = 0;
|
|
470
|
+
if (budget_left() < 8.0) goto done;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
// rollout 阶段实测有害,停用(保留代码备查)
|
|
474
|
+
if (false && budget_left() > 14.0) {
|
|
475
|
+
double best2 = -1e18;
|
|
476
|
+
for (int q = 0; q < n_top; ++q) {
|
|
477
|
+
State leaf = s;
|
|
478
|
+
step_as(leaf, me, top_ord[q], opp_joint[0].o);
|
|
479
|
+
const double rv = rollout(leaf, 8);
|
|
480
|
+
const double v2 = 0.6 * top_val[q] + 0.4 * rv;
|
|
481
|
+
if (v2 > best2) { best2 = v2; best = top_ord[q]; }
|
|
482
|
+
if (budget_left() < 8.0) break;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
done:;
|
|
486
|
+
return best;
|
|
487
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// bot.cpp —— 两步前瞻(我方连续两回合),对手用两种模型(不动 / 朝我方可打的目标开炮),取平均。
|
|
2
|
+
#include "tank.hpp"
|
|
3
|
+
|
|
4
|
+
using namespace tank;
|
|
5
|
+
|
|
6
|
+
static int dist_enemy[CELLS]; // 每个格子到对方基地的步数(砖墙当作可走)
|
|
7
|
+
|
|
8
|
+
static void compute_distances(const State& s, int me) {
|
|
9
|
+
static int queue[CELLS];
|
|
10
|
+
for (int c = 0; c < CELLS; ++c) dist_enemy[c] = 9999;
|
|
11
|
+
int head = 0, tail = 0;
|
|
12
|
+
const int target = s.base_pos[1 - me];
|
|
13
|
+
dist_enemy[target] = 0;
|
|
14
|
+
queue[tail++] = target;
|
|
15
|
+
while (head < tail) {
|
|
16
|
+
const int c = queue[head++];
|
|
17
|
+
for (int d = 0; d < 4; ++d) {
|
|
18
|
+
const int next = c + DIR_DELTA[d];
|
|
19
|
+
const uint8_t g = s.grid[next];
|
|
20
|
+
if ((g == EMPTY || g == BRICK) && dist_enemy[next] > dist_enemy[c] + 1) {
|
|
21
|
+
dist_enemy[next] = dist_enemy[c] + 1;
|
|
22
|
+
queue[tail++] = next;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 子弹从 pos 沿 d 飞出去,先碰到什么:>=0 坦克编号;-2 基地 seat 0;-3 基地 seat 1;-1 墙或界外。
|
|
29
|
+
static int first_hit(const State& s, int pos, int d) {
|
|
30
|
+
for (int c = pos + DIR_DELTA[d];; c += DIR_DELTA[d]) {
|
|
31
|
+
const uint8_t g = s.grid[c];
|
|
32
|
+
const int t = tank_at(s, c);
|
|
33
|
+
if (t >= 0) return t;
|
|
34
|
+
if (g == BASE0) return -2;
|
|
35
|
+
if (g == BASE1) return -3;
|
|
36
|
+
if (g == BRICK || g == STEEL || g == OUTSIDE) return -1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 对手的"开炮模型":每辆能开炮的车,朝第一个会打到我方坦克或我方基地的方向开炮。
|
|
41
|
+
static Orders enemy_greedy(const State& s, int me) {
|
|
42
|
+
Orders o;
|
|
43
|
+
const int eb = 1 - me;
|
|
44
|
+
for (int type = 0; type < 3; ++type) {
|
|
45
|
+
const int j = tank_id(eb, type);
|
|
46
|
+
if (!s.tank[j].alive || !can_fire(s, j)) continue;
|
|
47
|
+
for (int d = 0; d < 4; ++d) {
|
|
48
|
+
const int h = first_hit(s, s.tank[j].pos, d);
|
|
49
|
+
const bool ours = (h >= 0 && seat_of(h) == me) || (h <= -2 && (-2 - h) == me);
|
|
50
|
+
if (ours) { o.cmd[type] = cmd_fire(d); break; }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return o;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static double evaluate(const State& s, int me) {
|
|
57
|
+
const int eb = 1 - me;
|
|
58
|
+
if (s.done) {
|
|
59
|
+
if (s.winner == me) return 1e7;
|
|
60
|
+
if (s.winner == eb) return -1e7;
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
double v = 0;
|
|
64
|
+
v += 250.0 * s.base_hp[me];
|
|
65
|
+
v -= 250.0 * s.base_hp[eb];
|
|
66
|
+
for (int type = 0; type < 3; ++type) {
|
|
67
|
+
const int i = tank_id(me, type), j = tank_id(eb, type);
|
|
68
|
+
if (s.tank[i].alive) {
|
|
69
|
+
v += 80 + 20 * s.tank[i].hp;
|
|
70
|
+
v -= 6.0 * dist_enemy[s.tank[i].pos];
|
|
71
|
+
if (can_fire(s, i))
|
|
72
|
+
for (int d = 0; d < 4; ++d) {
|
|
73
|
+
const int h = first_hit(s, s.tank[i].pos, d);
|
|
74
|
+
if (h >= 0 && seat_of(h) == eb) { v += 30; break; }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (s.tank[j].alive) v -= 80 + 20 * s.tank[j].hp;
|
|
78
|
+
}
|
|
79
|
+
// 飞行中的子弹:看它最终会打到什么。基地每回合最多扣 1 血,同一基地的第二发及之后价值很低。
|
|
80
|
+
int hits_eb = 0, hits_me = 0;
|
|
81
|
+
for (int k = 0; k < s.n_bullets; ++k) {
|
|
82
|
+
const Bullet& b = s.bullet[k];
|
|
83
|
+
const int owner_seat = seat_of(b.owner);
|
|
84
|
+
const int h = first_hit(s, b.pos, b.dir);
|
|
85
|
+
if (h >= 0) {
|
|
86
|
+
if (seat_of(h) == owner_seat) v -= (owner_seat == me ? 25 : -25);
|
|
87
|
+
else if (owner_seat == me) v += 40;
|
|
88
|
+
else v -= 40 + 10 * s.tank[h].hp;
|
|
89
|
+
} else if (h == -2 - eb) {
|
|
90
|
+
if (owner_seat == me) v += hits_eb++ == 0 ? 90 : 20;
|
|
91
|
+
} else if (h == -2 - me) {
|
|
92
|
+
if (owner_seat == eb) v -= hits_me++ == 0 ? 90 : 20;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// 站位:沿某个方向看过去,能打到某个基地(中间只有空地、水,最多一块砖)
|
|
96
|
+
for (int seat = 0; seat < 2; ++seat) {
|
|
97
|
+
const int target_base = seat == me ? eb : me;
|
|
98
|
+
for (int type = 0; type < 3; ++type) {
|
|
99
|
+
const int i = tank_id(seat, type);
|
|
100
|
+
if (!s.tank[i].alive) continue;
|
|
101
|
+
for (int d = 0; d < 4; ++d) {
|
|
102
|
+
int bricks = 0;
|
|
103
|
+
int c = s.tank[i].pos;
|
|
104
|
+
for (;;) {
|
|
105
|
+
c += DIR_DELTA[d];
|
|
106
|
+
const uint8_t g = s.grid[c];
|
|
107
|
+
if (g == BASE0 || g == BASE1) {
|
|
108
|
+
if (g == BASE0 + target_base) {
|
|
109
|
+
const double bonus = bricks == 0 ? 12.0 : 5.0;
|
|
110
|
+
v += seat == me ? bonus : -bonus;
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
if (g == BRICK) { if (++bricks > 1) break; continue; }
|
|
115
|
+
if (g != EMPTY && g != WATER) break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return v;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 我方候选命令(死车只有 STAY)
|
|
124
|
+
static void candidates(const State& s, int me, Cmd cand[3][N_CMDS], int nc[3]) {
|
|
125
|
+
for (int type = 0; type < 3; ++type) {
|
|
126
|
+
const int i = tank_id(me, type);
|
|
127
|
+
if (!s.tank[i].alive) { cand[type][0] = STAY; nc[type] = 1; }
|
|
128
|
+
else nc[type] = legal_commands(s, i, cand[type]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 用两种对手模型各自前推一回合,返回平均值
|
|
133
|
+
static double value_after(const State& s, int me, const Orders& mine, const Orders& eo) {
|
|
134
|
+
State t = s;
|
|
135
|
+
if (me == 0) step(t, mine, eo);
|
|
136
|
+
else step(t, eo, mine);
|
|
137
|
+
return evaluate(t, me);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 一步搜索:返回所有组合的平均值(对两种对手模型)和最优组合
|
|
141
|
+
struct Node { double v; Orders o; };
|
|
142
|
+
|
|
143
|
+
static const int MAX_TOP = 3;
|
|
144
|
+
|
|
145
|
+
void init(const State& s, int me) {
|
|
146
|
+
(void)s; (void)me;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
Orders decide(const State& s, int me) {
|
|
150
|
+
compute_distances(s, me);
|
|
151
|
+
Cmd cand[3][N_CMDS];
|
|
152
|
+
int nc[3];
|
|
153
|
+
candidates(s, me, cand, nc);
|
|
154
|
+
|
|
155
|
+
const Orders greedy = enemy_greedy(s, me);
|
|
156
|
+
const Orders stay{};
|
|
157
|
+
|
|
158
|
+
// 第一层:所有组合,对两种对手模型取平均
|
|
159
|
+
static Node all[25 * 25 * 25];
|
|
160
|
+
int n = 0;
|
|
161
|
+
for (int a = 0; a < nc[0]; ++a)
|
|
162
|
+
for (int b = 0; b < nc[1]; ++b)
|
|
163
|
+
for (int c = 0; c < nc[2]; ++c) {
|
|
164
|
+
Orders mine;
|
|
165
|
+
mine.cmd[0] = cand[0][a];
|
|
166
|
+
mine.cmd[1] = cand[1][b];
|
|
167
|
+
mine.cmd[2] = cand[2][c];
|
|
168
|
+
const double v = 0.5 * value_after(s, me, mine, stay) + 0.5 * value_after(s, me, mine, greedy);
|
|
169
|
+
all[n++] = Node{v, mine};
|
|
170
|
+
}
|
|
171
|
+
// 取前 K 个
|
|
172
|
+
int top_n = n < MAX_TOP ? n : MAX_TOP;
|
|
173
|
+
for (int k = 0; k < top_n; ++k) {
|
|
174
|
+
int bi = k;
|
|
175
|
+
for (int j = k + 1; j < n; ++j) if (all[j].v > all[bi].v) bi = j;
|
|
176
|
+
Node tmp = all[k]; all[k] = all[bi]; all[bi] = tmp;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
Orders best = all[0].o;
|
|
180
|
+
double best_v = -1e300;
|
|
181
|
+
for (int k = 0; k < top_n; ++k) {
|
|
182
|
+
if (k > 0 && tank::budget_left() < 8) break;
|
|
183
|
+
const Orders mine = all[k].o;
|
|
184
|
+
double worst_sum = 0;
|
|
185
|
+
for (const Orders* eo : {&stay, &greedy}) {
|
|
186
|
+
State t = s;
|
|
187
|
+
if (me == 0) step(t, mine, *eo);
|
|
188
|
+
else step(t, *eo, mine);
|
|
189
|
+
// 第二层:我方在 t 上做一步搜索,对手同样两种模型
|
|
190
|
+
Cmd c2[3][N_CMDS];
|
|
191
|
+
int nc2[3];
|
|
192
|
+
candidates(t, me, c2, nc2);
|
|
193
|
+
const Orders g2 = enemy_greedy(t, me);
|
|
194
|
+
double inner = -1e300;
|
|
195
|
+
for (int a = 0; a < nc2[0]; ++a)
|
|
196
|
+
for (int b = 0; b < nc2[1]; ++b)
|
|
197
|
+
for (int c = 0; c < nc2[2]; ++c) {
|
|
198
|
+
Orders m2;
|
|
199
|
+
m2.cmd[0] = c2[0][a];
|
|
200
|
+
m2.cmd[1] = c2[1][b];
|
|
201
|
+
m2.cmd[2] = c2[2][c];
|
|
202
|
+
const double v = 0.5 * value_after(t, me, m2, stay) + 0.5 * value_after(t, me, m2, g2);
|
|
203
|
+
if (v > inner) inner = v;
|
|
204
|
+
}
|
|
205
|
+
worst_sum += 0.5 * inner;
|
|
206
|
+
}
|
|
207
|
+
if (worst_sum > best_v) { best_v = worst_sum; best = mine; }
|
|
208
|
+
}
|
|
209
|
+
return best;
|
|
210
|
+
}
|