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,560 @@
|
|
|
1
|
+
#include "tank.hpp"
|
|
2
|
+
#include <algorithm>
|
|
3
|
+
#include <cmath>
|
|
4
|
+
#include <cstring>
|
|
5
|
+
using namespace tank;
|
|
6
|
+
|
|
7
|
+
// ---------- distance fields (cached by terrain hash) ----------
|
|
8
|
+
struct Fields {
|
|
9
|
+
bool valid = false;
|
|
10
|
+
uint64_t key = 0;
|
|
11
|
+
float atk[2][CELLS]; // cost (steps) to start damaging base b from cell
|
|
12
|
+
float sup[MAX_SUPPLY][CELLS];
|
|
13
|
+
int fireB[2][CELLS]; // bricks between cell and base b (99 = no line / steel)
|
|
14
|
+
int fireD[2][CELLS]; // cells to base b along line
|
|
15
|
+
};
|
|
16
|
+
static Fields Fcache[8];
|
|
17
|
+
static int Fslot = 0;
|
|
18
|
+
static const float FINF = 1e9f;
|
|
19
|
+
|
|
20
|
+
static uint64_t terrain_key(const State& s) {
|
|
21
|
+
uint64_t h = 1469598103934665603ULL;
|
|
22
|
+
for (int c = 0; c < CELLS; ++c) { h ^= (uint64_t)(s.grid[c] + 1); h *= 1099511628211ULL; }
|
|
23
|
+
h ^= (uint64_t)(s.base_pos[0] * 1000 + s.base_pos[1]);
|
|
24
|
+
for (int k = 0; k < s.n_supply; ++k) h ^= (uint64_t)(s.supply[k] * (k + 7));
|
|
25
|
+
return h;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static float enter_cost(const State& s, int cell) {
|
|
29
|
+
uint8_t g = s.grid[cell];
|
|
30
|
+
if (g == EMPTY) return 1.0f;
|
|
31
|
+
if (g == BRICK) return 3.0f;
|
|
32
|
+
return FINF;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Dijkstra over playable cells; dist[] pre-seeded at sources, expanded by enter costs.
|
|
36
|
+
static void dijkstra(const State& s, float* dist) {
|
|
37
|
+
static bool done[CELLS];
|
|
38
|
+
for (int c = 0; c < CELLS; ++c) done[c] = false;
|
|
39
|
+
int N = s.P.size;
|
|
40
|
+
for (;;) {
|
|
41
|
+
int u = -1; float best = FINF;
|
|
42
|
+
for (int y = 0; y < N; ++y) for (int x = 0; x < N; ++x) {
|
|
43
|
+
int c = cell_of(x, y);
|
|
44
|
+
if (!done[c] && dist[c] < best) { best = dist[c]; u = c; }
|
|
45
|
+
}
|
|
46
|
+
if (u < 0) break;
|
|
47
|
+
done[u] = true;
|
|
48
|
+
for (int d = 0; d < 4; ++d) {
|
|
49
|
+
int v = u + DIR_DELTA[d];
|
|
50
|
+
if (done[v]) continue;
|
|
51
|
+
float ec = enter_cost(s, v);
|
|
52
|
+
if (ec >= FINF) continue;
|
|
53
|
+
float nd = dist[u] + ec;
|
|
54
|
+
if (nd < dist[v]) dist[v] = nd;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static void compute_fields(const State& s, Fields& F) {
|
|
60
|
+
int N = s.P.size;
|
|
61
|
+
for (int b = 0; b < 2; ++b) {
|
|
62
|
+
int base = s.base_pos[b], bx = cell_x(base), by = cell_y(base);
|
|
63
|
+
for (int c = 0; c < CELLS; ++c) { F.fireB[b][c] = 99; F.fireD[b][c] = 99; F.atk[b][c] = FINF; }
|
|
64
|
+
for (int y = 0; y < N; ++y) for (int x = 0; x < N; ++x) {
|
|
65
|
+
int c = cell_of(x, y);
|
|
66
|
+
if (c == base) continue;
|
|
67
|
+
if (x != bx && y != by) continue;
|
|
68
|
+
int dx = (bx > x) ? 1 : (bx < x ? -1 : 0);
|
|
69
|
+
int dy = (by > y) ? 1 : (by < y ? -1 : 0);
|
|
70
|
+
int bricks = 0, dist = 0; bool steel = false;
|
|
71
|
+
int cx = x, cy = y;
|
|
72
|
+
while (cx != bx || cy != by) {
|
|
73
|
+
cx += dx; cy += dy; ++dist;
|
|
74
|
+
if (cx == bx && cy == by) break;
|
|
75
|
+
uint8_t g = s.grid[cell_of(cx, cy)];
|
|
76
|
+
if (g == BRICK) ++bricks;
|
|
77
|
+
else if (g == STEEL) steel = true;
|
|
78
|
+
else if (g == BASE0 || g == BASE1) steel = true;
|
|
79
|
+
}
|
|
80
|
+
if (steel) continue;
|
|
81
|
+
F.fireB[b][c] = bricks; F.fireD[b][c] = dist;
|
|
82
|
+
uint8_t g = s.grid[c];
|
|
83
|
+
if (g == EMPTY || g == BRICK) {
|
|
84
|
+
float flightPen = (float)(((dist + 1) / 2 - 1) > 0 ? ((dist + 1) / 2 - 1) : 0) * 3.5f;
|
|
85
|
+
float init = bricks * 3.0f + flightPen + dist * 0.2f + (g == BRICK ? 3.0f : 0.0f);
|
|
86
|
+
if (init < F.atk[b][c]) F.atk[b][c] = init;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
dijkstra(s, F.atk[b]);
|
|
90
|
+
}
|
|
91
|
+
for (int k = 0; k < MAX_SUPPLY; ++k) {
|
|
92
|
+
for (int c = 0; c < CELLS; ++c) F.sup[k][c] = FINF;
|
|
93
|
+
if (k < s.n_supply) { F.sup[k][s.supply[k]] = 0.0f; dijkstra(s, F.sup[k]); }
|
|
94
|
+
}
|
|
95
|
+
F.key = terrain_key(s); F.valid = true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static const Fields& get_fields(const State& s) {
|
|
99
|
+
uint64_t k = terrain_key(s);
|
|
100
|
+
for (int i = 0; i < 8; ++i) if (Fcache[i].valid && Fcache[i].key == k) return Fcache[i];
|
|
101
|
+
Fslot = (Fslot + 1) & 7;
|
|
102
|
+
compute_fields(s, Fcache[Fslot]);
|
|
103
|
+
return Fcache[Fslot];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ---------- raycast ----------
|
|
107
|
+
struct Ray { int kind; int cell; int dist; int tank; int seat; }; // kind 0 none 1 brick 2 steel 3 base 4 tank
|
|
108
|
+
static Ray raycast(const State& s, const int* tpos, int from, int dir) {
|
|
109
|
+
int c = from, dist = 0;
|
|
110
|
+
for (;;) {
|
|
111
|
+
c += DIR_DELTA[dir]; ++dist;
|
|
112
|
+
uint8_t g = s.grid[c];
|
|
113
|
+
if (g == OUTSIDE) return Ray{0, c, dist, -1, -1};
|
|
114
|
+
if (g == BRICK) return Ray{1, c, dist, -1, -1};
|
|
115
|
+
if (g == STEEL) return Ray{2, c, dist, -1, -1};
|
|
116
|
+
if (g == BASE0 || g == BASE1) return Ray{3, c, dist, -1, (int)g - BASE0};
|
|
117
|
+
int t = tpos[c];
|
|
118
|
+
if (t >= 0) return Ray{4, c, dist, t, seat_of(t)};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
static bool is_ring(const State& s, int seatB, int cell) {
|
|
123
|
+
int base = s.base_pos[seatB], bx = cell_x(base), by = cell_y(base);
|
|
124
|
+
int dy = (by == 0) ? 1 : -1;
|
|
125
|
+
int cx = cell_x(cell), cy = cell_y(cell);
|
|
126
|
+
if (cy == by && (cx == bx - 1 || cx == bx + 1)) return true;
|
|
127
|
+
if (cy == by + dy && cx >= bx - 1 && cx <= bx + 1) return true;
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
static int ring_bricks(const State& s, int seatB) {
|
|
131
|
+
int base = s.base_pos[seatB], bx = cell_x(base), by = cell_y(base);
|
|
132
|
+
int dy = (by == 0) ? 1 : -1, n = 0;
|
|
133
|
+
const int offs[5][2] = {{-1,0},{1,0},{-1,dy},{0,dy},{1,dy}};
|
|
134
|
+
for (auto& o : offs) if (s.grid[cell_of(bx + o[0], by + o[1])] == BRICK) ++n;
|
|
135
|
+
return n;
|
|
136
|
+
}
|
|
137
|
+
static int dir_toward(int from, int to) { // aligned cells
|
|
138
|
+
int fx = cell_x(from), fy = cell_y(from), tx = cell_x(to), ty = cell_y(to);
|
|
139
|
+
if (tx > fx) return RIGHT; if (tx < fx) return LEFT;
|
|
140
|
+
if (ty > fy) return DOWN; return UP;
|
|
141
|
+
}
|
|
142
|
+
static bool aligned(int a, int b) { return cell_x(a) == cell_x(b) || cell_y(a) == cell_y(b); }
|
|
143
|
+
static int turns_to_spawn(const State& s) {
|
|
144
|
+
int t = s.turn, f = s.P.supply_first, p = s.P.supply_period;
|
|
145
|
+
if (t < f) return f - t;
|
|
146
|
+
int r = (t - f) % p;
|
|
147
|
+
return r == 0 ? p : p - r;
|
|
148
|
+
}
|
|
149
|
+
static float speed_of(const State& s, int i) { return (float)s.P.types[type_of(i)].speed; }
|
|
150
|
+
|
|
151
|
+
// Unweighted BFS (only EMPTY passable: real movement) from src; dist in steps, -1 unreachable.
|
|
152
|
+
static void bfs_dist(const State& s, int src, int* dist) {
|
|
153
|
+
for (int c = 0; c < CELLS; ++c) dist[c] = -1;
|
|
154
|
+
static int q[CELLS];
|
|
155
|
+
int head = 0, tail = 0;
|
|
156
|
+
dist[src] = 0; q[tail++] = src;
|
|
157
|
+
while (head < tail) {
|
|
158
|
+
int u = q[head++];
|
|
159
|
+
for (int d = 0; d < 4; ++d) {
|
|
160
|
+
int v = u + DIR_DELTA[d];
|
|
161
|
+
if (dist[v] >= 0) continue;
|
|
162
|
+
if (s.grid[v] != EMPTY) continue;
|
|
163
|
+
dist[v] = dist[u] + 1; q[tail++] = v;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
static int manh(int a, int b) { return std::abs(cell_x(a) - cell_x(b)) + std::abs(cell_y(a) - cell_y(b)); }
|
|
168
|
+
|
|
169
|
+
// ---------- evaluation ----------
|
|
170
|
+
static float side_score(const State& s, const Fields& F, const int* tpos, int seat) {
|
|
171
|
+
const int foe = 1 - seat;
|
|
172
|
+
float sc = 0.0f;
|
|
173
|
+
// material
|
|
174
|
+
for (int type = 0; type < 3; ++type) {
|
|
175
|
+
int i = tank_id(seat, type); const Tank& t = s.tank[i];
|
|
176
|
+
if (t.alive) sc += 135.0f + t.hp * 50.0f + t.level * 70.0f + (type == FIRE ? 18 : (type == FAST ? 12 : 10)) + (can_fire(s, i) ? 6.0f : 0.0f);
|
|
177
|
+
else sc -= respawn_in(s, i) * 22.0f;
|
|
178
|
+
}
|
|
179
|
+
// attack ETA (assigned weights: best attacker matters most)
|
|
180
|
+
float eta[3]; int nAlive = 0; float etas[3];
|
|
181
|
+
for (int type = 0; type < 3; ++type) {
|
|
182
|
+
int i = tank_id(seat, type); const Tank& t = s.tank[i];
|
|
183
|
+
eta[type] = FINF;
|
|
184
|
+
if (!t.alive) continue;
|
|
185
|
+
float a = F.atk[foe][t.pos];
|
|
186
|
+
eta[type] = (a >= FINF / 2 ? 60.0f : a / speed_of(s, i)) + (can_fire(s, i) ? 0.0f : 0.4f);
|
|
187
|
+
etas[nAlive++] = eta[type];
|
|
188
|
+
}
|
|
189
|
+
std::sort(etas, etas + nAlive);
|
|
190
|
+
const float wgt[3] = {1.0f, 0.5f, 0.22f};
|
|
191
|
+
for (int k = 0; k < nAlive; ++k) {
|
|
192
|
+
float e = std::min(etas[k], 45.0f);
|
|
193
|
+
sc -= wgt[k] * e * 6.0f;
|
|
194
|
+
sc += wgt[k] * 500.0f / (1.0f + e * 0.30f); // base-pressure: strong pull toward firing range
|
|
195
|
+
}
|
|
196
|
+
// firing positions vs enemy base
|
|
197
|
+
float fb[3] = {0,0,0}, fb2[3] = {0,0,0}; int nfb = 0, nfb2 = 0;
|
|
198
|
+
for (int type = 0; type < 3; ++type) {
|
|
199
|
+
int i = tank_id(seat, type); const Tank& t = s.tank[i];
|
|
200
|
+
if (!t.alive || F.fireB[foe][t.pos] >= 99) continue;
|
|
201
|
+
int dir = dir_toward(t.pos, s.base_pos[foe]);
|
|
202
|
+
Ray r = raycast(s, tpos, t.pos, dir);
|
|
203
|
+
if (F.fireB[foe][t.pos] == 0 && r.kind == 3 && r.seat == foe) {
|
|
204
|
+
float flightT = (float)((r.dist + 1) / 2);
|
|
205
|
+
float interval = std::max((float)(reload_turns(s, i) + 1), flightT / (float)std::max(1, bullet_cap(s, i)));
|
|
206
|
+
fb[nfb++] = 430.0f / interval + (can_fire(s, i) ? 40.0f : 12.0f);
|
|
207
|
+
} else if (r.kind == 1)
|
|
208
|
+
fb2[nfb2++] = 95.0f - r.dist * 6.0f - F.fireB[foe][t.pos] * 10.0f + (can_fire(s, i) ? 25.0f : 0.0f);
|
|
209
|
+
}
|
|
210
|
+
std::sort(fb, fb + nfb, std::greater<float>()); std::sort(fb2, fb2 + nfb2, std::greater<float>());
|
|
211
|
+
if (nfb > 0) sc += fb[0] + (nfb > 1 ? 0.35f * fb[1] : 0.0f);
|
|
212
|
+
if (nfb2 > 0) sc += fb2[0] + (nfb2 > 1 ? 0.3f * fb2[1] : 0.0f);
|
|
213
|
+
// defense: enemy threat to my base
|
|
214
|
+
float eEtaMin = 60.0f; int attacker = -1;
|
|
215
|
+
for (int type = 0; type < 3; ++type) {
|
|
216
|
+
int i = tank_id(foe, type); const Tank& t = s.tank[i];
|
|
217
|
+
if (!t.alive) continue;
|
|
218
|
+
float a = F.atk[seat][t.pos];
|
|
219
|
+
float e = (a >= FINF / 2 ? 60.0f : a / speed_of(s, i));
|
|
220
|
+
if (e < eEtaMin) { eEtaMin = e; attacker = i; }
|
|
221
|
+
}
|
|
222
|
+
float threat = std::clamp(11.0f - eEtaMin, 0.0f, 11.0f);
|
|
223
|
+
if (threat > 0.5f && attacker >= 0) {
|
|
224
|
+
float bestDef = 60.0f;
|
|
225
|
+
for (int type = 0; type < 3; ++type) {
|
|
226
|
+
int i = tank_id(seat, type); const Tank& t = s.tank[i];
|
|
227
|
+
if (!t.alive) continue;
|
|
228
|
+
float a = F.atk[seat][t.pos];
|
|
229
|
+
float d = (a >= FINF / 2 ? 60.0f : a / speed_of(s, i));
|
|
230
|
+
if (d < bestDef) bestDef = d;
|
|
231
|
+
}
|
|
232
|
+
sc += threat * std::max(0.0f, 10.0f - std::min(bestDef, 10.0f)) * 2.2f;
|
|
233
|
+
// intercept: gradient toward the attacker's actual position
|
|
234
|
+
bool firingNow = false;
|
|
235
|
+
{
|
|
236
|
+
const Tank& at = s.tank[attacker];
|
|
237
|
+
if (F.fireB[seat][at.pos] == 0) {
|
|
238
|
+
Ray r = raycast(s, tpos, at.pos, dir_toward(at.pos, s.base_pos[seat]));
|
|
239
|
+
firingNow = (r.kind == 3 && r.seat == seat);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
float urgency = threat + (firingNow ? 12.0f : 0.0f);
|
|
243
|
+
static int datt[CELLS];
|
|
244
|
+
bfs_dist(s, s.tank[attacker].pos, datt);
|
|
245
|
+
float dd[3]; int nd = 0;
|
|
246
|
+
for (int type = 0; type < 3; ++type) {
|
|
247
|
+
int i = tank_id(seat, type); const Tank& t = s.tank[i];
|
|
248
|
+
if (!t.alive) continue;
|
|
249
|
+
int d = datt[t.pos];
|
|
250
|
+
dd[nd++] = (d < 0 ? (12.0f + manh(t.pos, s.tank[attacker].pos) * 1.5f) / speed_of(s, i) : d / speed_of(s, i));
|
|
251
|
+
}
|
|
252
|
+
std::sort(dd, dd + nd);
|
|
253
|
+
float dw1 = firingNow ? 95.0f : (2.0f + urgency * 1.7f);
|
|
254
|
+
float dw2 = firingNow ? 35.0f : urgency * 0.5f;
|
|
255
|
+
if (nd > 0) sc -= std::min(dd[0], 30.0f) * dw1;
|
|
256
|
+
if (nd > 1) sc -= std::min(dd[1], 30.0f) * dw2;
|
|
257
|
+
float bestShot = 0.0f;
|
|
258
|
+
int apos = s.tank[attacker].pos;
|
|
259
|
+
for (int type = 0; type < 3; ++type) {
|
|
260
|
+
int i = tank_id(seat, type); const Tank& t = s.tank[i];
|
|
261
|
+
if (!t.alive) continue;
|
|
262
|
+
// lane block between attacker and my base
|
|
263
|
+
int base = s.base_pos[seat];
|
|
264
|
+
if (aligned(t.pos, base) && aligned(t.pos, apos) && F.fireB[seat][t.pos] == 0) {
|
|
265
|
+
int d1 = std::abs(cell_x(apos)-cell_x(base)) + std::abs(cell_y(apos)-cell_y(base));
|
|
266
|
+
int d2 = std::abs(cell_x(t.pos)-cell_x(base)) + std::abs(cell_y(t.pos)-cell_y(base));
|
|
267
|
+
int d3 = std::abs(cell_x(apos)-cell_x(t.pos)) + std::abs(cell_y(apos)-cell_y(t.pos));
|
|
268
|
+
if (d2 + d3 == d1) sc += (t.hp >= 2 ? 45.0f : 18.0f) * threat / 11.0f;
|
|
269
|
+
}
|
|
270
|
+
if (!aligned(t.pos, apos)) continue;
|
|
271
|
+
Ray r = raycast(s, tpos, t.pos, dir_toward(t.pos, apos));
|
|
272
|
+
if (r.kind == 4 && r.tank == attacker) bestShot = std::max(bestShot, 30.0f + threat * 4.0f + (can_fire(s, i) ? 30.0f : 0.0f));
|
|
273
|
+
}
|
|
274
|
+
sc += bestShot;
|
|
275
|
+
}
|
|
276
|
+
// stars
|
|
277
|
+
{
|
|
278
|
+
int tts = turns_to_spawn(s);
|
|
279
|
+
bool usedMine[3] = {false,false,false}, usedFoe[3] = {false,false,false};
|
|
280
|
+
for (int k = 0; k < s.n_supply; ++k) {
|
|
281
|
+
bool present = (s.star_mask >> k) & 1;
|
|
282
|
+
if (!present && tts > 10) continue;
|
|
283
|
+
float myArr = 60.0f, enArr = 60.0f; int myT = -1, enT = -1;
|
|
284
|
+
for (int type = 0; type < 3; ++type) {
|
|
285
|
+
int i = tank_id(seat, type); const Tank& t = s.tank[i];
|
|
286
|
+
if (!t.alive || t.level >= s.P.max_level || usedMine[type]) continue;
|
|
287
|
+
float a = F.sup[k][t.pos]; float arr = (a >= FINF/2 ? 60.0f : a / speed_of(s, i));
|
|
288
|
+
if (arr < myArr) { myArr = arr; myT = type; }
|
|
289
|
+
}
|
|
290
|
+
for (int type = 0; type < 3; ++type) {
|
|
291
|
+
int i = tank_id(foe, type); const Tank& t = s.tank[i];
|
|
292
|
+
if (!t.alive || t.level >= s.P.max_level || usedFoe[type]) continue;
|
|
293
|
+
float a = F.sup[k][t.pos]; float arr = (a >= FINF/2 ? 60.0f : a / speed_of(s, i));
|
|
294
|
+
if (arr < enArr) { enArr = arr; enT = type; }
|
|
295
|
+
}
|
|
296
|
+
if (myT >= 0 && myArr <= enArr + 2.0f) usedMine[myT] = true;
|
|
297
|
+
if (enT >= 0 && enArr <= myArr + 2.0f) usedFoe[enT] = true;
|
|
298
|
+
float value = present ? 110.0f : 55.0f * (1.0f - tts / 22.0f);
|
|
299
|
+
if (myArr <= enArr + 0.5f) sc += value * std::clamp(1.05f - myArr * 0.05f, 0.15f, 1.0f) + std::min(std::max(enArr - myArr, 0.0f), 6.0f) * 3.0f;
|
|
300
|
+
else if (myArr <= enArr + 1.5f || myArr < 4.0f) sc += value * 0.18f * std::clamp(1.0f - myArr * 0.05f, 0.0f, 1.0f);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// aim at enemy tanks
|
|
304
|
+
for (int type = 0; type < 3; ++type) {
|
|
305
|
+
int i = tank_id(seat, type); const Tank& t = s.tank[i];
|
|
306
|
+
if (!t.alive) continue;
|
|
307
|
+
float bestAim = 0.0f;
|
|
308
|
+
for (int et = 0; et < 3; ++et) {
|
|
309
|
+
int j = tank_id(foe, et); const Tank& e = s.tank[j];
|
|
310
|
+
if (!e.alive || !aligned(t.pos, e.pos)) continue;
|
|
311
|
+
Ray r = raycast(s, tpos, t.pos, dir_toward(t.pos, e.pos));
|
|
312
|
+
if (r.kind == 4 && r.tank == j)
|
|
313
|
+
bestAim = std::max(bestAim, (can_fire(s, i) ? 26.0f : 8.0f) + (e.hp <= 1 ? 14.0f : 7.0f) + e.level * 6.0f - r.dist * 0.8f);
|
|
314
|
+
}
|
|
315
|
+
sc += bestAim;
|
|
316
|
+
}
|
|
317
|
+
// bullets in flight
|
|
318
|
+
{
|
|
319
|
+
float bb1 = 0.0f, bb2 = 0.0f;
|
|
320
|
+
for (int b = 0; b < s.n_bullets; ++b) {
|
|
321
|
+
const Bullet& bul = s.bullet[b];
|
|
322
|
+
if (seat_of(bul.owner) != seat) continue;
|
|
323
|
+
Ray r = raycast(s, tpos, bul.pos, bul.dir);
|
|
324
|
+
float flight = (r.dist + 1) / 2.0f;
|
|
325
|
+
if (r.kind == 3) {
|
|
326
|
+
float v = 175.0f / (1.0f + flight * 0.7f);
|
|
327
|
+
if (r.seat == foe) { if (v > bb1) { bb2 = bb1; bb1 = v; } else if (v > bb2) bb2 = v; }
|
|
328
|
+
else sc -= v;
|
|
329
|
+
} else if (r.kind == 4) {
|
|
330
|
+
const Tank& vct = s.tank[r.tank];
|
|
331
|
+
float v = (vct.hp <= 1 ? 70.0f : 40.0f) / (1.0f + flight * 0.6f);
|
|
332
|
+
sc += (r.seat == foe ? v : -v * 1.15f);
|
|
333
|
+
} else if (r.kind == 1 || (r.kind == 2 && bul.steel)) {
|
|
334
|
+
float v = 50.0f / (1.0f + flight * 0.5f);
|
|
335
|
+
if (is_ring(s, foe, r.cell)) sc += v;
|
|
336
|
+
else if (is_ring(s, seat, r.cell)) sc -= v;
|
|
337
|
+
else if (aligned(bul.pos, s.base_pos[foe]) && dir_toward(bul.pos, s.base_pos[foe]) == bul.dir) sc += v * 0.55f;
|
|
338
|
+
else sc += 3.0f;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
sc += bb1 + 0.3f * bb2;
|
|
342
|
+
}
|
|
343
|
+
// spawn hazards + own-ring pocket (trapped, blocks own base exits)
|
|
344
|
+
for (int type = 0; type < 3; ++type) {
|
|
345
|
+
int i = tank_id(seat, type); const Tank& t = s.tank[i];
|
|
346
|
+
if (!t.alive) continue;
|
|
347
|
+
if (is_ring(s, seat, t.pos)) sc -= 35.0f;
|
|
348
|
+
for (int e = 0; e < 6; ++e) {
|
|
349
|
+
if (s.tank[e].alive || s.spawn[e] != t.pos) continue;
|
|
350
|
+
int ri = respawn_in(s, e);
|
|
351
|
+
if (seat_of(e) == foe) sc -= (ri <= 1 ? 380.0f : (ri == 2 ? 70.0f : 15.0f));
|
|
352
|
+
else if (ri <= 1) sc -= 80.0f;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return sc;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
static float evaluate(const State& s, int seat) {
|
|
359
|
+
if (s.done) {
|
|
360
|
+
if (s.winner == seat) return 1000000.0f + s.base_hp[seat] * 10.0f;
|
|
361
|
+
if (s.winner == 1 - seat) return -1000000.0f - s.base_hp[1 - seat] * 10.0f;
|
|
362
|
+
return 0.0f;
|
|
363
|
+
}
|
|
364
|
+
const Fields& F = get_fields(s);
|
|
365
|
+
static int tpos[CELLS];
|
|
366
|
+
for (int c = 0; c < CELLS; ++c) tpos[c] = -1;
|
|
367
|
+
for (int i = 0; i < 6; ++i) if (s.tank[i].alive) tpos[s.tank[i].pos] = i;
|
|
368
|
+
float tieScale = 1.0f + std::max(0, (int)s.turn - 230) / 60.0f;
|
|
369
|
+
float sc = (s.base_hp[seat] - s.base_hp[1 - seat]) * 620.0f
|
|
370
|
+
+ (s.kills[seat] - s.kills[1 - seat]) * 70.0f * tieScale
|
|
371
|
+
+ (s.stars_eaten[seat] - s.stars_eaten[1 - seat]) * 50.0f * tieScale
|
|
372
|
+
+ (ring_bricks(s, seat) - ring_bricks(s, 1 - seat)) * 22.0f;
|
|
373
|
+
sc += side_score(s, F, tpos, seat) - side_score(s, F, tpos, 1 - seat);
|
|
374
|
+
return sc;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ---------- robust coordinate-ascent search ----------
|
|
378
|
+
static Orders heuristic_orders(const State& s, int side, int mode = 0); // defined below
|
|
379
|
+
static float robust_eval(const State& s, int side, const Orders& mine, const Orders* scen, int nScen) {
|
|
380
|
+
float mn = 1e18f, sum = 0.0f;
|
|
381
|
+
for (int k = 0; k < nScen; ++k) {
|
|
382
|
+
State ns = s;
|
|
383
|
+
step_as(ns, side, mine, scen[k]);
|
|
384
|
+
float v = evaluate(ns, side);
|
|
385
|
+
mn = std::min(mn, v); sum += v;
|
|
386
|
+
}
|
|
387
|
+
return mn;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Optimize `side`'s orders against fixed opponent scenario(s), starting from `start`.
|
|
391
|
+
static Orders optimize(const State& s, int side, const Orders* scen, int nScen, Orders start, int passes) {
|
|
392
|
+
Orders cur = start;
|
|
393
|
+
float curScore = robust_eval(s, side, cur, scen, nScen);
|
|
394
|
+
Cmd list[N_CMDS];
|
|
395
|
+
for (int pass = 0; pass < passes; ++pass) {
|
|
396
|
+
bool improved = false;
|
|
397
|
+
for (int type = 0; type < 3; ++type) {
|
|
398
|
+
if (budget_left() < 5.0) return cur;
|
|
399
|
+
int i = tank_id(side, type);
|
|
400
|
+
if (!s.tank[i].alive) { cur.cmd[type] = STAY; continue; }
|
|
401
|
+
int n = legal_commands(s, i, list);
|
|
402
|
+
Cmd bestCmd = cur.cmd[type];
|
|
403
|
+
float bestScore = curScore;
|
|
404
|
+
for (int k = 0; k < n; ++k) {
|
|
405
|
+
if (list[k] == cur.cmd[type]) continue;
|
|
406
|
+
Orders cand = cur; cand.cmd[type] = list[k];
|
|
407
|
+
float v = robust_eval(s, side, cand, scen, nScen);
|
|
408
|
+
if (v > bestScore + 0.01f) { bestScore = v; bestCmd = list[k]; }
|
|
409
|
+
}
|
|
410
|
+
if (bestCmd != cur.cmd[type]) { cur.cmd[type] = bestCmd; curScore = bestScore; improved = true; }
|
|
411
|
+
}
|
|
412
|
+
if (!improved) break;
|
|
413
|
+
}
|
|
414
|
+
return cur;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Simple rusher heuristic: fire at base/enemy/lane-brick, else advance along attack field (or to a nearby star).
|
|
418
|
+
static Orders heuristic_orders(const State& s, int side, int mode) {
|
|
419
|
+
const Fields& F = get_fields(s);
|
|
420
|
+
const int foe = 1 - side;
|
|
421
|
+
static int tpos[CELLS];
|
|
422
|
+
for (int c = 0; c < CELLS; ++c) tpos[c] = -1;
|
|
423
|
+
for (int i = 0; i < 6; ++i) if (s.tank[i].alive) tpos[s.tank[i].pos] = i;
|
|
424
|
+
// defense assignment: closest tank intercepts an enemy that is firing at (or about to fire at) our base
|
|
425
|
+
int attacker = -1; float atkEta = 60.0f;
|
|
426
|
+
for (int et = 0; et < 3; ++et) {
|
|
427
|
+
int j = tank_id(foe, et); const Tank& e = s.tank[j];
|
|
428
|
+
if (!e.alive) continue;
|
|
429
|
+
bool firing = false;
|
|
430
|
+
if (F.fireB[side][e.pos] == 0) {
|
|
431
|
+
Ray r = raycast(s, tpos, e.pos, dir_toward(e.pos, s.base_pos[side]));
|
|
432
|
+
firing = (r.kind == 3 && r.seat == side);
|
|
433
|
+
}
|
|
434
|
+
float a = F.atk[side][e.pos];
|
|
435
|
+
float eta = (a >= FINF / 2 ? 60.0f : a / speed_of(s, j));
|
|
436
|
+
if (firing) { attacker = j; atkEta = 0.0f; break; }
|
|
437
|
+
if (eta < atkEta) { atkEta = eta; attacker = j; }
|
|
438
|
+
}
|
|
439
|
+
static float defField[CELLS];
|
|
440
|
+
int defenderType = -1;
|
|
441
|
+
if (mode != 1 && attacker >= 0 && atkEta < 3.5f) {
|
|
442
|
+
static int datt[CELLS];
|
|
443
|
+
bfs_dist(s, s.tank[attacker].pos, datt);
|
|
444
|
+
for (int c = 0; c < CELLS; ++c) defField[c] = (datt[c] < 0 ? 40.0f + (float)manh(c, s.tank[attacker].pos) : (float)datt[c]);
|
|
445
|
+
float bd = 1e9f;
|
|
446
|
+
for (int type = 0; type < 3; ++type) {
|
|
447
|
+
int i = tank_id(side, type); const Tank& t = s.tank[i];
|
|
448
|
+
if (!t.alive) continue;
|
|
449
|
+
float d = (datt[t.pos] < 0 ? 60.0f : datt[t.pos] / speed_of(s, i));
|
|
450
|
+
if (d < bd) { bd = d; defenderType = type; }
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
Orders o;
|
|
454
|
+
for (int type = 0; type < 3; ++type) {
|
|
455
|
+
int i = tank_id(side, type); const Tank& t = s.tank[i];
|
|
456
|
+
if (!t.alive) continue;
|
|
457
|
+
bool done = false;
|
|
458
|
+
if (can_fire(s, i)) {
|
|
459
|
+
int fireD = -1, firePri = 99;
|
|
460
|
+
for (int d = 0; d < 4; ++d) {
|
|
461
|
+
Ray r = raycast(s, tpos, t.pos, d);
|
|
462
|
+
int pri = 99;
|
|
463
|
+
if (r.kind == 3 && r.seat == foe) pri = 0;
|
|
464
|
+
else if (r.kind == 4 && r.seat == foe) pri = 1;
|
|
465
|
+
else if (r.kind == 1 && aligned(t.pos, s.base_pos[foe]) && dir_toward(t.pos, s.base_pos[foe]) == d) pri = 2;
|
|
466
|
+
else if (r.kind == 1 && is_ring(s, foe, r.cell)) pri = 2;
|
|
467
|
+
if (pri < firePri) { firePri = pri; fireD = d; }
|
|
468
|
+
}
|
|
469
|
+
if (fireD >= 0) { o.cmd[type] = cmd_fire(fireD); done = true; }
|
|
470
|
+
}
|
|
471
|
+
if (done) continue;
|
|
472
|
+
const float* field = F.atk[foe];
|
|
473
|
+
if (type == defenderType) field = defField;
|
|
474
|
+
else if (mode != 1 && t.level < s.P.max_level && (s.star_mask || (mode == 2 && turns_to_spawn(s) <= 8))) {
|
|
475
|
+
float bestSup = FINF; int bk = -1;
|
|
476
|
+
for (int k = 0; k < s.n_supply; ++k) if (((s.star_mask >> k) & 1) || mode == 2) {
|
|
477
|
+
if (F.sup[k][t.pos] < bestSup) { bestSup = F.sup[k][t.pos]; bk = k; }
|
|
478
|
+
}
|
|
479
|
+
if (bk >= 0 && bestSup < (mode == 2 ? 16.0f : 9.0f)) field = F.sup[bk];
|
|
480
|
+
}
|
|
481
|
+
auto best_step = [&](int from) {
|
|
482
|
+
int bd = -1; float bv = field[from];
|
|
483
|
+
for (int d = 0; d < 4; ++d) {
|
|
484
|
+
int v = from + DIR_DELTA[d];
|
|
485
|
+
if (s.grid[v] == EMPTY && field[v] < bv) { bv = field[v]; bd = d; }
|
|
486
|
+
}
|
|
487
|
+
return bd;
|
|
488
|
+
};
|
|
489
|
+
int d1 = best_step(t.pos);
|
|
490
|
+
if (d1 < 0) {
|
|
491
|
+
// brick ahead on the gradient? shoot it
|
|
492
|
+
if (can_fire(s, i)) {
|
|
493
|
+
for (int d = 0; d < 4; ++d) {
|
|
494
|
+
int v = t.pos + DIR_DELTA[d];
|
|
495
|
+
if (s.grid[v] == BRICK && field[v] < field[t.pos]) { o.cmd[type] = cmd_fire(d); break; }
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
if (type == FAST) {
|
|
501
|
+
int p1 = t.pos + DIR_DELTA[d1];
|
|
502
|
+
int d2 = best_step(p1);
|
|
503
|
+
o.cmd[type] = (d2 >= 0) ? cmd_move2(d1, d2) : cmd_move(d1);
|
|
504
|
+
} else o.cmd[type] = cmd_move(d1);
|
|
505
|
+
}
|
|
506
|
+
return o;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
void init(const State& s, int me) {
|
|
510
|
+
(void)s; (void)me;
|
|
511
|
+
for (auto& f : Fcache) f.valid = false;
|
|
512
|
+
get_fields(s);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
Orders decide(const State& s, int me) {
|
|
516
|
+
const int foe = 1 - me;
|
|
517
|
+
Orders hMe = heuristic_orders(s, me);
|
|
518
|
+
Orders hFoe = heuristic_orders(s, foe);
|
|
519
|
+
// 1) enemy optimized plan vs my heuristic rush
|
|
520
|
+
Orders e0 = optimize(s, foe, &hMe, 1, hFoe, 2);
|
|
521
|
+
// 2) my robust response to enemy heuristic + optimized plans (keep all scenarios)
|
|
522
|
+
Orders scenA[2] = {hFoe, e0};
|
|
523
|
+
// multi-start: regular / all-attack / star-priority starts, keep the best robust result
|
|
524
|
+
Orders m1 = optimize(s, me, scenA, 2, hMe, 1);
|
|
525
|
+
{
|
|
526
|
+
float bestSc = robust_eval(s, me, m1, scenA, 2);
|
|
527
|
+
Orders starts[2] = {heuristic_orders(s, me, 1), heuristic_orders(s, me, 2)};
|
|
528
|
+
for (int k = 0; k < 2; ++k) {
|
|
529
|
+
if (starts[k] == hMe) continue;
|
|
530
|
+
Orders cand = optimize(s, me, scenA, 2, starts[k], 1);
|
|
531
|
+
float sc = robust_eval(s, me, cand, scenA, 2);
|
|
532
|
+
if (sc > bestSc + 0.01f) { bestSc = sc; m1 = cand; }
|
|
533
|
+
}
|
|
534
|
+
m1 = optimize(s, me, scenA, 2, m1, 1);
|
|
535
|
+
}
|
|
536
|
+
if (budget_left() < 20.0) return m1;
|
|
537
|
+
// 3) enemy best response to my plan
|
|
538
|
+
Orders e1 = optimize(s, foe, &m1, 1, e0, 2);
|
|
539
|
+
Orders scenB[3] = {hFoe, e0, e1};
|
|
540
|
+
Orders m2 = optimize(s, me, scenB, 3, m1, 2);
|
|
541
|
+
if (budget_left() < 22.0) return m2;
|
|
542
|
+
// 4) one more fictitious-play round, scenarios accumulate
|
|
543
|
+
Orders e2 = optimize(s, foe, &m2, 1, e1, 1);
|
|
544
|
+
Orders scenC[4] = {hFoe, e0, e1, e2};
|
|
545
|
+
Orders m3 = optimize(s, me, scenC, 4, m2, 1);
|
|
546
|
+
if (budget_left() < 25.0) return m3;
|
|
547
|
+
// 5) second extra round when budget is plentiful
|
|
548
|
+
Orders e3 = optimize(s, foe, &m3, 1, e2, 1);
|
|
549
|
+
Orders scenD[5] = {hFoe, e0, e1, e2, e3};
|
|
550
|
+
Orders m4 = optimize(s, me, scenD, 5, m3, 1);
|
|
551
|
+
if (budget_left() < 28.0) return m4;
|
|
552
|
+
Orders e4 = optimize(s, foe, &m4, 1, e3, 1);
|
|
553
|
+
Orders scenE[6] = {hFoe, e0, e1, e2, e3, e4};
|
|
554
|
+
Orders m5 = optimize(s, me, scenE, 6, m4, 2);
|
|
555
|
+
if (budget_left() < 30.0) return m5;
|
|
556
|
+
Orders e5 = optimize(s, foe, &m5, 1, e4, 1);
|
|
557
|
+
Orders scenF[7] = {hFoe, e0, e1, e2, e3, e4, e5};
|
|
558
|
+
Orders m6 = optimize(s, me, scenF, 7, m5, 1);
|
|
559
|
+
return m6;
|
|
560
|
+
}
|