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,651 @@
|
|
|
1
|
+
// bot.cpp —— v2:一层博弈搜索(我方联合命令 × 对方联合应答,tank::step 精确结算)
|
|
2
|
+
// + 启发式评估(基地血量、坦克价值、火力覆盖危险图、炮线攻击成本、吃星)。
|
|
3
|
+
#include "tank.hpp"
|
|
4
|
+
#include <algorithm>
|
|
5
|
+
|
|
6
|
+
using namespace tank;
|
|
7
|
+
|
|
8
|
+
namespace {
|
|
9
|
+
|
|
10
|
+
constexpr int BRICK_COST = 5;
|
|
11
|
+
constexpr int ATK_INF = 500;
|
|
12
|
+
|
|
13
|
+
static int ME = 0;
|
|
14
|
+
|
|
15
|
+
static int16_t dist_me[CELLS]; // 我方到对方基地的 Dijkstra 代价
|
|
16
|
+
static int16_t dist_opp[CELLS]; // 敌方到我基地的 Dijkstra 代价
|
|
17
|
+
static int16_t atk_me[CELLS]; // 从该格沿炮线打到对方基地的近似回合数
|
|
18
|
+
static int16_t atk_opp[CELLS]; // 从该格沿炮线打到我基地的近似回合数
|
|
19
|
+
static int16_t star_dist[CELLS]; // 到最近补给点的 BFS 距离(只走空地)
|
|
20
|
+
static int16_t hot[CELLS]; // 敌方火力覆盖
|
|
21
|
+
static int16_t guard_bfs[CELLS]; // 从我方守家装甲出发的真实寻路距离(砖算 5)
|
|
22
|
+
static int invader_cell = -1; // 本回合选定的入侵者格子(守家追杀目标)
|
|
23
|
+
|
|
24
|
+
double base_rate(const State& s, int i, int tgt_base); // 前置声明
|
|
25
|
+
|
|
26
|
+
static int last_pos[3];
|
|
27
|
+
static int quiet_turns;
|
|
28
|
+
static int last_ehp;
|
|
29
|
+
|
|
30
|
+
bool aligned_dir(int a, int b, int& d) {
|
|
31
|
+
int ax = a % STRIDE, ay = a / STRIDE, bx = b % STRIDE, by = b / STRIDE;
|
|
32
|
+
if (ax == bx && ay != by) { d = by > ay ? DOWN : UP; return true; }
|
|
33
|
+
if (ay == by && ax != bx) { d = bx > ax ? RIGHT : LEFT; return true; }
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// from 沿 d 到 target 之间(不含两端)全为空地/水且无坦克
|
|
38
|
+
bool line_clear(const State& s, int from, int d, int target) {
|
|
39
|
+
int c = from + DIR_DELTA[d];
|
|
40
|
+
while (c != target) {
|
|
41
|
+
uint8_t g = s.grid[c];
|
|
42
|
+
if (g != EMPTY && g != WATER) return false;
|
|
43
|
+
if (tank_at(s, c) >= 0) return false;
|
|
44
|
+
c += DIR_DELTA[d];
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
template <bool USE_HOT>
|
|
50
|
+
static void dijkstra_impl(const State& s, int target, int16_t out[CELLS]) {
|
|
51
|
+
static int heap_cost[CELLS * 8]; static int heap_cell[CELLS * 8];
|
|
52
|
+
int hn = 0;
|
|
53
|
+
auto push = [&](int cost, int cell) {
|
|
54
|
+
heap_cost[hn] = cost; heap_cell[hn] = cell;
|
|
55
|
+
int i = hn++;
|
|
56
|
+
while (i > 0) {
|
|
57
|
+
int p = (i - 1) / 2;
|
|
58
|
+
if (heap_cost[p] <= heap_cost[i]) break;
|
|
59
|
+
std::swap(heap_cost[p], heap_cost[i]);
|
|
60
|
+
std::swap(heap_cell[p], heap_cell[i]);
|
|
61
|
+
i = p;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
auto pop = [&]() {
|
|
65
|
+
int top = heap_cell[0];
|
|
66
|
+
heap_cell[0] = heap_cell[--hn]; heap_cost[0] = heap_cost[hn];
|
|
67
|
+
int i = 0;
|
|
68
|
+
for (;;) {
|
|
69
|
+
int l = 2 * i + 1, r = l + 1, m = i;
|
|
70
|
+
if (l < hn && heap_cost[l] < heap_cost[m]) m = l;
|
|
71
|
+
if (r < hn && heap_cost[r] < heap_cost[m]) m = r;
|
|
72
|
+
if (m == i) break;
|
|
73
|
+
std::swap(heap_cost[i], heap_cost[m]); std::swap(heap_cell[i], heap_cell[m]);
|
|
74
|
+
i = m;
|
|
75
|
+
}
|
|
76
|
+
return top;
|
|
77
|
+
};
|
|
78
|
+
for (int c = 0; c < CELLS; ++c) out[c] = ATK_INF;
|
|
79
|
+
out[target] = 0;
|
|
80
|
+
push(0, target);
|
|
81
|
+
while (hn) {
|
|
82
|
+
int cell = pop();
|
|
83
|
+
int base = out[cell];
|
|
84
|
+
for (int k = 0; k < 4; ++k) {
|
|
85
|
+
int n = cell + DIR_DELTA[k];
|
|
86
|
+
uint8_t g = s.grid[n];
|
|
87
|
+
int w = (g == EMPTY) ? 1 : (g == BRICK ? BRICK_COST : -1);
|
|
88
|
+
if (w < 0) continue;
|
|
89
|
+
if (USE_HOT) w += hot[n]; // 敌方火力覆盖当寻路代价(轻量,避免绕远)
|
|
90
|
+
if (base + w < out[n]) { out[n] = (int16_t)(base + w); push(out[n], n); }
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
static void dijkstra_to(const State& s, int target, int16_t out[CELLS]) {
|
|
95
|
+
dijkstra_impl<false>(s, target, out);
|
|
96
|
+
}
|
|
97
|
+
static void dijkstra_to_hot(const State& s, int target, int16_t out[CELLS]) {
|
|
98
|
+
dijkstra_impl<true>(s, target, out);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
static void atk_map_to(const State& s, int tgt, const int16_t* d, int16_t out[CELLS]) {
|
|
102
|
+
for (int y = 0; y < s.P.size; ++y)
|
|
103
|
+
for (int x = 0; x < s.P.size; ++x) {
|
|
104
|
+
int c = cell_of(x, y);
|
|
105
|
+
uint8_t g = s.grid[c];
|
|
106
|
+
if (g != EMPTY && g != BRICK) { out[c] = (int16_t)ATK_INF; continue; }
|
|
107
|
+
int best = ATK_INF;
|
|
108
|
+
for (int k = 0; k < 4; ++k) {
|
|
109
|
+
int bricks = 0, steps = 0, cc = c;
|
|
110
|
+
bool ok = false;
|
|
111
|
+
for (;;) {
|
|
112
|
+
cc += DIR_DELTA[k]; ++steps;
|
|
113
|
+
uint8_t gg = s.grid[cc];
|
|
114
|
+
if (gg == OUTSIDE) break;
|
|
115
|
+
if (gg == WATER || gg == EMPTY) continue;
|
|
116
|
+
if (gg == BRICK) { ++bricks; continue; }
|
|
117
|
+
if (cc == tgt) ok = true;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
if (ok) {
|
|
121
|
+
int cost = 2 * bricks + (steps + 1) / 2;
|
|
122
|
+
if (cost < best) best = cost;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (best >= ATK_INF) best = 40 + (d[c] < ATK_INF ? d[c] : 400);
|
|
126
|
+
out[c] = (int16_t)best;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
void compute_maps(const State& s) {
|
|
131
|
+
const int opp = 1 - ME;
|
|
132
|
+
|
|
133
|
+
// ---- hot:敌方火力覆盖(先算,寻路要用) ----
|
|
134
|
+
for (int c = 0; c < CELLS; ++c) hot[c] = 0;
|
|
135
|
+
for (int e = 3 * opp; e < 3 * opp + 3; ++e) {
|
|
136
|
+
const Tank& te = s.tank[e];
|
|
137
|
+
if (!te.alive) continue;
|
|
138
|
+
int rl = reload_left(s, e);
|
|
139
|
+
if (rl > 1) continue; // 一回合内开不了炮的不算
|
|
140
|
+
int w = 1;
|
|
141
|
+
if (can_fire(s, e)) w = 2; // 现在就能开炮
|
|
142
|
+
if (s.tank[e].level >= 1 || s.P.types[type_of(e)].reload == 0) w += 1; // 能连发更危险
|
|
143
|
+
for (int k = 0; k < 4; ++k) {
|
|
144
|
+
int c = te.pos;
|
|
145
|
+
for (;;) {
|
|
146
|
+
c += DIR_DELTA[k];
|
|
147
|
+
uint8_t g = s.grid[c];
|
|
148
|
+
if (g != EMPTY && g != WATER) break; // 砖/钢/基地挡
|
|
149
|
+
if (tank_at(s, c) >= 0) { hot[c] += w; break; } // 第一辆坦克挡(会被打)
|
|
150
|
+
hot[c] += w;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 我方的进攻寻路把敌方火力覆盖当额外代价:绕开架好的炮线
|
|
156
|
+
dijkstra_to_hot(s, s.base_pos[opp], dist_me);
|
|
157
|
+
dijkstra_to(s, s.base_pos[ME], dist_opp);
|
|
158
|
+
atk_map_to(s, s.base_pos[opp], dist_me, atk_me);
|
|
159
|
+
atk_map_to(s, s.base_pos[ME], dist_opp, atk_opp);
|
|
160
|
+
|
|
161
|
+
// 守家的追杀目标:离我基地最近(寻路距离)或正在打我基地的敌车;从它做真实寻路图
|
|
162
|
+
invader_cell = -1;
|
|
163
|
+
{
|
|
164
|
+
int best_key = 99, best_cell = -1;
|
|
165
|
+
for (int e = 3 * opp; e < 3 * opp + 3; ++e) {
|
|
166
|
+
if (!s.tank[e].alive) continue;
|
|
167
|
+
int hb = dist_opp[s.tank[e].pos] >= ATK_INF ? 20 : dist_opp[s.tank[e].pos];
|
|
168
|
+
bool pounding = base_rate(s, e, s.base_pos[ME]) >= 0.3;
|
|
169
|
+
if (hb > 8 && !pounding) continue;
|
|
170
|
+
if (hb < best_key) { best_key = hb; best_cell = s.tank[e].pos; }
|
|
171
|
+
}
|
|
172
|
+
invader_cell = best_cell;
|
|
173
|
+
if (invader_cell >= 0) dijkstra_to(s, invader_cell, guard_bfs);
|
|
174
|
+
else for (int c = 0; c < CELLS; ++c) guard_bfs[c] = ATK_INF;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ---- star_dist:从"没被敌方火力覆盖"的补给点出发 ----
|
|
178
|
+
static int q[CELLS];
|
|
179
|
+
int head = 0, tail = 0;
|
|
180
|
+
for (int c = 0; c < CELLS; ++c) star_dist[c] = (int16_t)ATK_INF;
|
|
181
|
+
int usable = 0;
|
|
182
|
+
for (int k = 0; k < s.n_supply; ++k) {
|
|
183
|
+
int sc = s.supply[k];
|
|
184
|
+
if (s.grid[sc] == EMPTY && hot[sc] < 3) { star_dist[sc] = 0; q[tail++] = sc; ++usable; }
|
|
185
|
+
}
|
|
186
|
+
if (usable == 0) // 全被占就退回全部补给点
|
|
187
|
+
for (int k = 0; k < s.n_supply; ++k) {
|
|
188
|
+
int sc = s.supply[k];
|
|
189
|
+
if (s.grid[sc] == EMPTY && star_dist[sc] > 0) { star_dist[sc] = 0; q[tail++] = sc; }
|
|
190
|
+
}
|
|
191
|
+
while (head < tail) {
|
|
192
|
+
int c = q[head++];
|
|
193
|
+
for (int k = 0; k < 4; ++k) {
|
|
194
|
+
int n = c + DIR_DELTA[k];
|
|
195
|
+
if (s.grid[n] == EMPTY && star_dist[n] > star_dist[c] + 1) {
|
|
196
|
+
star_dist[n] = (int16_t)(star_dist[c] + 1);
|
|
197
|
+
q[tail++] = n;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ---------------- 评估 ----------------
|
|
204
|
+
double tank_value(const State& s, int i) {
|
|
205
|
+
static const int basev[3] = {260, 750, 520};
|
|
206
|
+
const Tank& t = s.tank[i];
|
|
207
|
+
if (!t.alive) return 40;
|
|
208
|
+
return basev[type_of(i)] + 130 * t.hp + 240 * t.level;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// pos 处的坦克 m 是否有敌方子弹正朝它飞来(对穿可抵消时 reduc 返回 true)
|
|
212
|
+
double bullet_threat(const State& s, int m, bool& cancelable) {
|
|
213
|
+
const Tank& tm = s.tank[m];
|
|
214
|
+
double worst = 0; cancelable = false;
|
|
215
|
+
for (int b = 0; b < s.n_bullets; ++b) {
|
|
216
|
+
const Bullet& bl = s.bullet[b];
|
|
217
|
+
if (seat_of(bl.owner) == seat_of(m) && bl.owner != m) continue; // 己方子弹也算(友伤),自己的不算
|
|
218
|
+
int d;
|
|
219
|
+
if (!aligned_dir(bl.pos, tm.pos, d) || d != bl.dir) continue;
|
|
220
|
+
if (!line_clear(s, bl.pos, d, tm.pos)) continue; // 中间有坦克/墙挡着
|
|
221
|
+
int dist = abs(cell_x(tm.pos) - cell_x(bl.pos)) + abs(cell_y(tm.pos) - cell_y(bl.pos));
|
|
222
|
+
// 子弹每回合 2 格;近了才真致命,远的走开就行
|
|
223
|
+
if (dist <= 6) {
|
|
224
|
+
double p = dist <= 2 ? 0.7 : (dist <= 4 ? 0.25 : 0.08);
|
|
225
|
+
if (p > worst) { worst = p; cancelable = can_fire(s, m); }
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return worst;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// pos 处的坦克朝目标基地的"持续打击率":炮线越清、越近,打基地越快。
|
|
232
|
+
// 返回 rate ∈ (0,1](每回合能打掉几血);打不到返回 0。线上的坦克会挡子弹。
|
|
233
|
+
// occ:格子 -> 坦克编号(-1 无),传 nullptr 就现场算(慢,只在低频路径用)。
|
|
234
|
+
double base_rate_occ(const State& s, int i, int tgt_base, const int* occ) {
|
|
235
|
+
static int local_occ[CELLS];
|
|
236
|
+
if (!occ) {
|
|
237
|
+
for (int c = 0; c < CELLS; ++c) local_occ[c] = -1;
|
|
238
|
+
for (int k = 0; k < 6; ++k) if (s.tank[k].alive) local_occ[s.tank[k].pos] = k;
|
|
239
|
+
occ = local_occ;
|
|
240
|
+
}
|
|
241
|
+
const Tank& t = s.tank[i];
|
|
242
|
+
if (!t.alive) return 0;
|
|
243
|
+
for (int d = 0; d < 4; ++d) {
|
|
244
|
+
int bricks = 0, steps = 0, cc = t.pos;
|
|
245
|
+
bool ok = false;
|
|
246
|
+
for (;;) {
|
|
247
|
+
cc += DIR_DELTA[d]; ++steps;
|
|
248
|
+
uint8_t g = s.grid[cc];
|
|
249
|
+
if (g == OUTSIDE) break;
|
|
250
|
+
if (g == WATER || g == EMPTY) {
|
|
251
|
+
if (occ[cc] >= 0) break; // 有坦克挡着
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (g == BRICK) { ++bricks; continue; }
|
|
255
|
+
if (cc == tgt_base) ok = true;
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
if (ok) {
|
|
259
|
+
int delay = 2 * bricks + (steps + 1) / 2;
|
|
260
|
+
double rate = 1.0 / delay;
|
|
261
|
+
// 炮位被敌方直线火力盯着(能被抵消/击毁)就打折:另选侧翼炮位
|
|
262
|
+
int threat = 0;
|
|
263
|
+
for (int k = 0; k < 4; ++k) {
|
|
264
|
+
int cc = t.pos;
|
|
265
|
+
for (;;) {
|
|
266
|
+
cc += DIR_DELTA[k];
|
|
267
|
+
uint8_t gg = s.grid[cc];
|
|
268
|
+
if (gg != EMPTY && gg != WATER) break;
|
|
269
|
+
int ti = occ[cc];
|
|
270
|
+
if (ti >= 0) {
|
|
271
|
+
if (seat_of(ti) != seat_of(i) && can_fire(s, ti)) ++threat;
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (threat > 0) rate *= (threat >= 2 ? 0.25 : 0.45);
|
|
277
|
+
return rate;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return 0;
|
|
281
|
+
}
|
|
282
|
+
double base_rate(const State& s, int i, int tgt_base) {
|
|
283
|
+
return base_rate_occ(s, i, tgt_base, nullptr);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// 守家目标距离:优先追杀离我基地最近的入侵者(那才是正在打我基地的),否则回家守着。
|
|
287
|
+
int guard_dist_at(const State& s, int i, int pos) {
|
|
288
|
+
const int16_t* home = (seat_of(i) == ME ? dist_opp : dist_me);
|
|
289
|
+
const int my_base = s.base_pos[seat_of(i)];
|
|
290
|
+
int best_key = 99, best_dd = 99;
|
|
291
|
+
for (int e = 0; e < 6; ++e) {
|
|
292
|
+
if (seat_of(e) == seat_of(i) || !s.tank[e].alive) continue;
|
|
293
|
+
int hb = home[s.tank[e].pos] >= ATK_INF ? 20 : home[s.tank[e].pos];
|
|
294
|
+
// 寻路距离会被围墙砖骗(绕路远≠威胁小):正在打我基地的一律算入侵者
|
|
295
|
+
bool pounding = base_rate(s, e, my_base) >= 0.3;
|
|
296
|
+
if (hb > 8 && !pounding) continue; // 只追真正逼近我基地的
|
|
297
|
+
// 我方守家用真实寻路距离(曼哈顿会被水/砖骗,绕不过去)
|
|
298
|
+
int dd;
|
|
299
|
+
if (seat_of(i) == ME && invader_cell >= 0 && guard_bfs[pos] < ATK_INF)
|
|
300
|
+
dd = guard_bfs[pos];
|
|
301
|
+
else
|
|
302
|
+
dd = abs(cell_x(pos) - cell_x(s.tank[e].pos)) + abs(cell_y(pos) - cell_y(s.tank[e].pos));
|
|
303
|
+
if (hb < best_key || (hb == best_key && dd < best_dd)) { best_key = hb; best_dd = dd; }
|
|
304
|
+
}
|
|
305
|
+
if (best_key < 99) return best_dd;
|
|
306
|
+
return home[pos] >= ATK_INF ? 20 : home[pos];
|
|
307
|
+
}
|
|
308
|
+
int guard_dist(const State& s, int i) {
|
|
309
|
+
return s.tank[i].alive ? guard_dist_at(s, i, s.tank[i].pos) : 12;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
double evaluate(const State& s) {
|
|
313
|
+
const int opp = 1 - ME;
|
|
314
|
+
if (s.done) return s.winner == ME ? 1e9 : (s.winner < 0 ? 0.0 : -1e9);
|
|
315
|
+
static const double atkw[3] = {24, 55, 30};
|
|
316
|
+
// ---- 一次性缓存:占格表、各车打击率、两边的守家目标 ----
|
|
317
|
+
static int occ[CELLS];
|
|
318
|
+
static double brate[6];
|
|
319
|
+
for (int c = 0; c < CELLS; ++c) occ[c] = -1;
|
|
320
|
+
for (int i = 0; i < 6; ++i) if (s.tank[i].alive) occ[s.tank[i].pos] = i;
|
|
321
|
+
for (int i = 0; i < 6; ++i)
|
|
322
|
+
brate[i] = base_rate_occ(s, i, s.base_pos[1 - seat_of(i)], occ);
|
|
323
|
+
// 守家目标:离自家基地最近(寻路距离)或正在打自家基地的敌车
|
|
324
|
+
int guard_cell[2] = {-1, -1}; // [seat]:该 seat 的守家坦克该追的格子
|
|
325
|
+
{
|
|
326
|
+
const int16_t* home2[2] = {dist_opp, dist_me}; // [seat] 距自家基地
|
|
327
|
+
for (int seat = 0; seat < 2; ++seat) {
|
|
328
|
+
int best_key = 99, best_cell = -1;
|
|
329
|
+
for (int e = 3 * (1 - seat); e < 3 * (1 - seat) + 3; ++e) {
|
|
330
|
+
if (!s.tank[e].alive) continue;
|
|
331
|
+
int hb = home2[seat][s.tank[e].pos] >= ATK_INF ? 20 : home2[seat][s.tank[e].pos];
|
|
332
|
+
if (hb > 8 && brate[e] < 0.3) continue;
|
|
333
|
+
if (hb < best_key) { best_key = hb; best_cell = s.tank[e].pos; }
|
|
334
|
+
}
|
|
335
|
+
guard_cell[seat] = best_cell;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
double sc = 3000.0 * (s.base_hp[ME] - s.base_hp[opp])
|
|
339
|
+
+ 550.0 * (s.kills[ME] - s.kills[opp])
|
|
340
|
+
+ 380.0 * (s.stars_eaten[ME] - s.stars_eaten[opp]);
|
|
341
|
+
for (int i = 0; i < 6; ++i) {
|
|
342
|
+
int seat = seat_of(i), tp = type_of(i);
|
|
343
|
+
const Tank& t = s.tank[i];
|
|
344
|
+
double v = tank_value(s, i);
|
|
345
|
+
sc += (seat == ME ? v : -0.92 * v);
|
|
346
|
+
double sign = (seat == ME ? -1.0 : +1.15);
|
|
347
|
+
if (tp == ARMOR) {
|
|
348
|
+
// 装甲型守家:追杀入侵者 / 守在基地旁
|
|
349
|
+
const int16_t* home = (seat == ME ? dist_opp : dist_me);
|
|
350
|
+
int g;
|
|
351
|
+
if (!t.alive) g = 12;
|
|
352
|
+
else if (guard_cell[seat] >= 0)
|
|
353
|
+
g = abs(cell_x(t.pos) - cell_x(guard_cell[seat])) + abs(cell_y(t.pos) - cell_y(guard_cell[seat]));
|
|
354
|
+
else g = home[t.pos] >= ATK_INF ? 14 : home[t.pos];
|
|
355
|
+
if (g > 14) g = 14;
|
|
356
|
+
sc += sign * 62.0 * g;
|
|
357
|
+
} else {
|
|
358
|
+
const int16_t* am = (seat == ME ? atk_me : atk_opp);
|
|
359
|
+
const int16_t* dm = (seat == ME ? dist_me : dist_opp);
|
|
360
|
+
int a = t.alive ? (am[t.pos] >= ATK_INF ? 200 : am[t.pos]) : 21;
|
|
361
|
+
if (a > 22) a = 22;
|
|
362
|
+
// 距离项不封顶:保持"向对方基地推进"的梯度;死了按最差算
|
|
363
|
+
int dist = t.alive ? (dm[t.pos] >= ATK_INF ? 40 : dm[t.pos]) : 40;
|
|
364
|
+
if (dist > 40) dist = 40;
|
|
365
|
+
sc += sign * (atkw[tp] * a + 7.0 * dist);
|
|
366
|
+
}
|
|
367
|
+
if (t.alive) {
|
|
368
|
+
// 已进入基地炮位的持续伤害率(最能分出优劣的一项)
|
|
369
|
+
sc += (seat == ME ? +2800.0 : -4000.0) * brate[i];
|
|
370
|
+
if (tp == FAST && t.level < 2) {
|
|
371
|
+
int sd = star_dist[t.pos] >= ATK_INF ? 60 : star_dist[t.pos];
|
|
372
|
+
sc += (seat == ME ? -52.0 : +16.0) * sd;
|
|
373
|
+
} else if (seat == ME && t.level < 2 && star_dist[t.pos] < 4) {
|
|
374
|
+
sc -= 22.0 * star_dist[t.pos];
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// 有敌车正在打我基地(rate ≥ 1/2)时:全员向它收拢(杀死它是最大的一笔分)
|
|
379
|
+
for (int e = 3 * opp; e < 3 * opp + 3; ++e) {
|
|
380
|
+
const Tank& te = s.tank[e];
|
|
381
|
+
if (!te.alive || brate[e] < 0.5) continue;
|
|
382
|
+
for (int m = 3 * ME; m < 3 * ME + 3; ++m) {
|
|
383
|
+
const Tank& tm = s.tank[m];
|
|
384
|
+
if (!tm.alive) continue;
|
|
385
|
+
int dd = abs(cell_x(tm.pos) - cell_x(te.pos)) + abs(cell_y(tm.pos) - cell_y(te.pos));
|
|
386
|
+
sc -= 22.0 * (dd > 14 ? 14 : dd);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
// 拥堵惩罚:我方两辆车贴在一起(会互相挡路、挡炮线)
|
|
390
|
+
for (int m1 = 3 * ME; m1 < 3 * ME + 3; ++m1)
|
|
391
|
+
for (int m2 = m1 + 1; m2 < 3 * ME + 3; ++m2) {
|
|
392
|
+
if (!s.tank[m1].alive || !s.tank[m2].alive) continue;
|
|
393
|
+
int dd = abs(cell_x(s.tank[m1].pos) - cell_x(s.tank[m2].pos)) + abs(cell_y(s.tank[m1].pos) - cell_y(s.tank[m2].pos));
|
|
394
|
+
if (dd == 1) sc -= 160;
|
|
395
|
+
}
|
|
396
|
+
// 站在敌方出生点上:对方复活时会被直接炸毁(telefrag),常驻打基地必须避开这些格
|
|
397
|
+
for (int m = 3 * ME; m < 3 * ME + 3; ++m) {
|
|
398
|
+
const Tank& tm = s.tank[m];
|
|
399
|
+
if (!tm.alive) continue;
|
|
400
|
+
for (int e = 3 * opp; e < 3 * opp + 3; ++e)
|
|
401
|
+
if (tm.pos == s.spawn[e])
|
|
402
|
+
sc -= (s.tank[e].alive ? 350.0 : 900.0);
|
|
403
|
+
}
|
|
404
|
+
// 威胁:对方下一回合能沿清空炮线打到我(危险图 + 动态对齐都算)
|
|
405
|
+
for (int m = 3 * ME; m < 3 * ME + 3; ++m) {
|
|
406
|
+
const Tank& tm = s.tank[m];
|
|
407
|
+
if (!tm.alive) continue;
|
|
408
|
+
double vm = tank_value(s, m);
|
|
409
|
+
double danger = 0;
|
|
410
|
+
for (int e = 3 * opp; e < 3 * opp + 3; ++e) {
|
|
411
|
+
const Tank& te = s.tank[e];
|
|
412
|
+
if (!te.alive || !can_fire(s, e)) continue;
|
|
413
|
+
int d;
|
|
414
|
+
if (aligned_dir(te.pos, tm.pos, d) && line_clear(s, te.pos, d, tm.pos)) {
|
|
415
|
+
// 子弹每回合只飞 2 格:远处的炮线只要保持移动就能躲,贴脸才致命
|
|
416
|
+
int D = abs(cell_x(tm.pos) - cell_x(te.pos)) + abs(cell_y(tm.pos) - cell_y(te.pos));
|
|
417
|
+
double p = (D <= 1) ? 0.45 : (D == 2 ? 0.2 : 0.06);
|
|
418
|
+
int back = d ^ 1; // 对向(U<->D, L<->R)
|
|
419
|
+
bool mutual = can_fire(s, m) && line_clear(s, tm.pos, back, te.pos);
|
|
420
|
+
danger += mutual ? p * 0.45 : p;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (tm.pos >= 0 && tm.pos < CELLS) danger += 0.05 * hot[tm.pos];
|
|
424
|
+
// 在飞的子弹
|
|
425
|
+
bool canc;
|
|
426
|
+
danger += bullet_threat(s, m, canc);
|
|
427
|
+
if (danger > 0.8) danger = 0.8;
|
|
428
|
+
// 守家装甲多怕 60%:活着就能拖住对方攻基地
|
|
429
|
+
sc -= danger * vm * (type_of(m) == ARMOR ? 1.6 : 1.0);
|
|
430
|
+
}
|
|
431
|
+
for (int e = 3 * opp; e < 3 * opp + 3; ++e) {
|
|
432
|
+
const Tank& te = s.tank[e];
|
|
433
|
+
if (!te.alive) continue;
|
|
434
|
+
double ve = tank_value(s, e);
|
|
435
|
+
double danger = 0;
|
|
436
|
+
for (int m = 3 * ME; m < 3 * ME + 3; ++m) {
|
|
437
|
+
const Tank& tm = s.tank[m];
|
|
438
|
+
if (!tm.alive || !can_fire(s, m)) continue;
|
|
439
|
+
int d;
|
|
440
|
+
if (aligned_dir(tm.pos, te.pos, d) && line_clear(s, tm.pos, d, te.pos)) {
|
|
441
|
+
int D = abs(cell_x(te.pos) - cell_x(tm.pos)) + abs(cell_y(te.pos) - cell_y(tm.pos));
|
|
442
|
+
danger += (D <= 1) ? 0.4 : (D == 2 ? 0.18 : 0.06);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
bool canc;
|
|
446
|
+
danger += 0.6 * bullet_threat(s, e, canc);
|
|
447
|
+
if (danger > 0.8) danger = 0.8;
|
|
448
|
+
sc += danger * ve * 0.85;
|
|
449
|
+
}
|
|
450
|
+
return sc;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// ---------------- 候选生成 ----------------
|
|
454
|
+
struct Cand { Cmd c; double pre; };
|
|
455
|
+
|
|
456
|
+
bool base_line(const State& s, int pos, int d, int base, int& bricks) {
|
|
457
|
+
bricks = 0;
|
|
458
|
+
int c = pos;
|
|
459
|
+
for (;;) {
|
|
460
|
+
c += DIR_DELTA[d];
|
|
461
|
+
uint8_t g = s.grid[c];
|
|
462
|
+
if (g == OUTSIDE) return false;
|
|
463
|
+
if (g == WATER || g == EMPTY) continue;
|
|
464
|
+
if (g == BRICK) { ++bricks; continue; }
|
|
465
|
+
if (c == base) return true;
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// 当前 pos 是否在某发子弹接下来的路径上(1..2 小步内)
|
|
471
|
+
bool in_bullet_path(const State& s, int pos) {
|
|
472
|
+
for (int b = 0; b < s.n_bullets; ++b) {
|
|
473
|
+
const Bullet& bl = s.bullet[b];
|
|
474
|
+
int c = bl.pos;
|
|
475
|
+
for (int k = 1; k <= 2; ++k) {
|
|
476
|
+
c += DIR_DELTA[bl.dir];
|
|
477
|
+
if (c == pos) return true;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
void gen_cand(const State& s, int i, int me, Cand out[26], int& n, bool enemy, int keep) {
|
|
484
|
+
Cmd list[N_CMDS];
|
|
485
|
+
int ncmd = legal_commands(s, i, list);
|
|
486
|
+
const Tank& t = s.tank[i];
|
|
487
|
+
const int tgt_base = enemy ? s.base_pos[me] : s.base_pos[1 - me];
|
|
488
|
+
const int16_t* amap = enemy ? atk_opp : atk_me;
|
|
489
|
+
double a1 = amap[t.pos] >= ATK_INF ? 200 : amap[t.pos];
|
|
490
|
+
double h1 = t.alive ? hot[t.pos] : 0;
|
|
491
|
+
bool in_path = t.alive && in_bullet_path(s, t.pos);
|
|
492
|
+
n = 0;
|
|
493
|
+
for (int k = 0; k < ncmd; ++k) {
|
|
494
|
+
Cmd c = list[k];
|
|
495
|
+
double sc = 0;
|
|
496
|
+
if (is_fire(c)) {
|
|
497
|
+
int d = c - 5;
|
|
498
|
+
int bricks;
|
|
499
|
+
if (base_line(s, t.pos, d, tgt_base, bricks)) sc += 240 - 26 * bricks;
|
|
500
|
+
for (int b = 0; b < s.n_bullets; ++b) {
|
|
501
|
+
const Bullet& bl = s.bullet[b];
|
|
502
|
+
// 来弹正对着我飞(对向 = d^1),且在 1..6 格内:开炮可抵消
|
|
503
|
+
int ad;
|
|
504
|
+
if (bl.dir == (d ^ 1) && seat_of(bl.owner) != seat_of(i) &&
|
|
505
|
+
aligned_dir(bl.pos, t.pos, ad) && ad == d) {
|
|
506
|
+
int dist = abs(cell_x(bl.pos) - cell_x(t.pos)) + abs(cell_y(bl.pos) - cell_y(t.pos));
|
|
507
|
+
if (dist >= 1 && dist <= 6) sc += 150;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
for (int e = 0; e < 6; ++e) {
|
|
511
|
+
if (e == i || !s.tank[e].alive) continue;
|
|
512
|
+
int d2;
|
|
513
|
+
if (aligned_dir(t.pos, s.tank[e].pos, d2) && d2 == d &&
|
|
514
|
+
line_clear(s, t.pos, d, s.tank[e].pos)) {
|
|
515
|
+
bool friendly = seat_of(e) == seat_of(i);
|
|
516
|
+
double bonus = friendly ? -180 : 110;
|
|
517
|
+
// 正在打我基地的敌车( camper):必须打
|
|
518
|
+
if (!friendly && base_rate(s, e, s.base_pos[seat_of(i)]) >= 0.5) bonus = 500;
|
|
519
|
+
sc += bonus;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
int cc = t.pos;
|
|
523
|
+
for (;;) {
|
|
524
|
+
cc += DIR_DELTA[d];
|
|
525
|
+
uint8_t g = s.grid[cc];
|
|
526
|
+
if (g == OUTSIDE) break;
|
|
527
|
+
if (g == WATER || g == EMPTY) continue;
|
|
528
|
+
if (g == BRICK || g == STEEL) break;
|
|
529
|
+
if (cc == s.base_pos[seat_of(i)]) sc -= 260;
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
} else if (c != STAY) {
|
|
533
|
+
int dest;
|
|
534
|
+
if (is_move2(c)) {
|
|
535
|
+
int d1 = (c - 9) >> 2, d2 = (c - 9) & 3;
|
|
536
|
+
int p1 = t.pos + DIR_DELTA[d1];
|
|
537
|
+
dest = (s.grid[p1 + DIR_DELTA[d2]] == EMPTY) ? p1 + DIR_DELTA[d2] : p1;
|
|
538
|
+
} else dest = t.pos + DIR_DELTA[c - 1];
|
|
539
|
+
if (dest != t.pos && s.grid[dest] == EMPTY) {
|
|
540
|
+
if (type_of(i) == ARMOR) {
|
|
541
|
+
int g1 = guard_dist_at(s, i, t.pos), g2 = guard_dist_at(s, i, dest);
|
|
542
|
+
sc += 58 * (g1 - g2); // 守家梯度
|
|
543
|
+
} else {
|
|
544
|
+
double a2 = amap[dest] >= ATK_INF ? 200 : amap[dest];
|
|
545
|
+
double w = enemy ? 30 : (type_of(i) == FIRE ? 55 : 24);
|
|
546
|
+
sc += w * (a1 - a2);
|
|
547
|
+
}
|
|
548
|
+
// 全员:靠近正在打我基地的敌车
|
|
549
|
+
for (int e = 0; e < 6; ++e) {
|
|
550
|
+
if (seat_of(e) == seat_of(i) || !s.tank[e].alive) continue;
|
|
551
|
+
if (base_rate(s, e, s.base_pos[seat_of(i)]) >= 0.5) {
|
|
552
|
+
int dd1 = abs(cell_x(t.pos) - cell_x(s.tank[e].pos)) + abs(cell_y(t.pos) - cell_y(s.tank[e].pos));
|
|
553
|
+
int dd2 = abs(cell_x(dest) - cell_x(s.tank[e].pos)) + abs(cell_y(dest) - cell_y(s.tank[e].pos));
|
|
554
|
+
sc += 20 * (dd1 - dd2);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
sc -= 12 * hot[dest] * (enemy ? 0.3 : 1.0);
|
|
558
|
+
sc += 10 * (h1 - hot[dest]);
|
|
559
|
+
if (star_at(s, dest) && s.tank[i].level < 2) sc += 190;
|
|
560
|
+
if (type_of(i) == FAST && s.tank[i].level < 2) {
|
|
561
|
+
int sd1 = star_dist[t.pos] >= ATK_INF ? 60 : star_dist[t.pos];
|
|
562
|
+
int sd2 = star_dist[dest] >= ATK_INF ? 60 : star_dist[dest];
|
|
563
|
+
sc += 32 * (sd1 - sd2);
|
|
564
|
+
}
|
|
565
|
+
if (in_path && !in_bullet_path(s, dest)) sc += 220; // 逃出弹道
|
|
566
|
+
for (int b = 0; b < s.n_bullets; ++b) {
|
|
567
|
+
const Bullet& bl = s.bullet[b];
|
|
568
|
+
int c2 = bl.pos;
|
|
569
|
+
for (int kk = 1; kk <= 2; ++kk) {
|
|
570
|
+
c2 += DIR_DELTA[bl.dir];
|
|
571
|
+
if (c2 == dest) sc -= 130;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
} else if (dest == t.pos) {
|
|
575
|
+
// 原地等一小步的两步命令
|
|
576
|
+
}
|
|
577
|
+
} else {
|
|
578
|
+
if (in_path) sc -= 200; // 站在弹道里别不动
|
|
579
|
+
}
|
|
580
|
+
out[n].c = c; out[n].pre = sc; ++n;
|
|
581
|
+
}
|
|
582
|
+
std::sort(out, out + n, [](const Cand& x, const Cand& y) {
|
|
583
|
+
if (x.pre != y.pre) return x.pre > y.pre;
|
|
584
|
+
return x.c < y.c;
|
|
585
|
+
});
|
|
586
|
+
if (n > keep) n = keep;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
} // namespace
|
|
590
|
+
|
|
591
|
+
void init(const tank::State& s, int me) {
|
|
592
|
+
ME = me;
|
|
593
|
+
for (int k = 0; k < 3; ++k) last_pos[k] = -1;
|
|
594
|
+
quiet_turns = 0;
|
|
595
|
+
last_ehp = s.base_hp[1 - me];
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
Orders decide(const tank::State& s, int me) {
|
|
599
|
+
ME = me;
|
|
600
|
+
compute_maps(s);
|
|
601
|
+
quiet_turns = s.base_hp[1 - me] < last_ehp ? 0 : quiet_turns + 1;
|
|
602
|
+
last_ehp = s.base_hp[1 - me];
|
|
603
|
+
|
|
604
|
+
Orders best;
|
|
605
|
+
const int opp = 1 - me;
|
|
606
|
+
|
|
607
|
+
Cand mc[3][26]; int mn[3];
|
|
608
|
+
for (int tp = 0; tp < 3; ++tp) {
|
|
609
|
+
int i = tank_id(me, tp);
|
|
610
|
+
gen_cand(s, i, me, mc[tp], mn[tp], false, 6);
|
|
611
|
+
if (mn[tp] < 1) { mc[tp][0].c = STAY; mn[tp] = 1; }
|
|
612
|
+
}
|
|
613
|
+
Cand ec[3][26]; int en[3];
|
|
614
|
+
for (int tp = 0; tp < 3; ++tp) {
|
|
615
|
+
int i = tank_id(opp, tp);
|
|
616
|
+
gen_cand(s, i, me, ec[tp], en[tp], true, 4);
|
|
617
|
+
if (en[tp] < 1) { ec[tp][0].c = STAY; en[tp] = 1; }
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
double best_score = -1e18;
|
|
621
|
+
bool have = false;
|
|
622
|
+
for (int a = 0; a < mn[0]; ++a)
|
|
623
|
+
for (int b = 0; b < mn[1]; ++b)
|
|
624
|
+
for (int c = 0; c < mn[2]; ++c) {
|
|
625
|
+
Orders mine;
|
|
626
|
+
mine.cmd[0] = mc[0][a].c; mine.cmd[1] = mc[1][b].c; mine.cmd[2] = mc[2][c].c;
|
|
627
|
+
double mn_min = 1e18, mn_sum = 0; int cnt = 0;
|
|
628
|
+
for (int p = 0; p < en[0]; ++p)
|
|
629
|
+
for (int q = 0; q < en[1]; ++q)
|
|
630
|
+
for (int r = 0; r < en[2]; ++r) {
|
|
631
|
+
Orders theirs;
|
|
632
|
+
theirs.cmd[0] = ec[0][p].c; theirs.cmd[1] = ec[1][q].c; theirs.cmd[2] = ec[2][r].c;
|
|
633
|
+
State sim = s;
|
|
634
|
+
step_as(sim, me, mine, theirs);
|
|
635
|
+
double v = evaluate(sim);
|
|
636
|
+
if (v < mn_min) mn_min = v;
|
|
637
|
+
mn_sum += v; ++cnt;
|
|
638
|
+
}
|
|
639
|
+
double sc = 0.65 * mn_min + 0.35 * (mn_sum / cnt);
|
|
640
|
+
if (quiet_turns >= 14)
|
|
641
|
+
for (int tp = 0; tp < 3; ++tp)
|
|
642
|
+
if (mine.cmd[tp] != STAY) sc += 0.6;
|
|
643
|
+
if (!have || sc > best_score + 1e-9) { best_score = sc; best = mine; have = true; }
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
for (int tp = 0; tp < 3; ++tp) {
|
|
647
|
+
int i = tank_id(me, tp);
|
|
648
|
+
last_pos[tp] = s.tank[i].alive ? s.tank[i].pos : -1;
|
|
649
|
+
}
|
|
650
|
+
return best;
|
|
651
|
+
}
|