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,233 @@
|
|
|
1
|
+
// First-half entry: weighted terrain routing, joint task allocation,
|
|
2
|
+
// simultaneous-action matrix search and exact three-turn tactical rollouts.
|
|
3
|
+
// Search keeps a legal one-turn fallback and checks the CPU budget.
|
|
4
|
+
#include "tank.hpp"
|
|
5
|
+
#include <algorithm>
|
|
6
|
+
#include <array>
|
|
7
|
+
#include <cmath>
|
|
8
|
+
#include <queue>
|
|
9
|
+
#include <vector>
|
|
10
|
+
#include <cstdio>
|
|
11
|
+
using namespace tank;
|
|
12
|
+
namespace ai {
|
|
13
|
+
constexpr float INF=10000;
|
|
14
|
+
struct Context { float field[6][CELLS]; float attack[2][2][CELLS]; float sd[2][MAX_SUPPLY][CELLS]; float ed[6][CELLS]; int occ[CELLS]; float wallval[CELLS]; int walls[CELLS],nw; uint64_t wh; int me; } ctx;
|
|
15
|
+
int hist[6][CELLS];
|
|
16
|
+
unsigned rng=7123671;
|
|
17
|
+
unsigned rnd(){rng^=rng<<13;rng^=rng>>17;rng^=rng<<5;return rng;}
|
|
18
|
+
int md(int a,int b){return std::abs(cell_x(a)-cell_x(b))+std::abs(cell_y(a)-cell_y(b));}
|
|
19
|
+
void dijkstra(const State&s,float* d,bool steel,float speed){
|
|
20
|
+
using P=std::pair<float,int>;std::priority_queue<P,std::vector<P>,std::greater<P>>q;
|
|
21
|
+
for(int y=0;y<s.P.size;y++)for(int x=0;x<s.P.size;x++){int c=cell_of(x,y);if(d[c]<INF)q.push({d[c],c});}
|
|
22
|
+
while(!q.empty()){auto [v,c]=q.top();q.pop();if(v>d[c]+.001)continue;
|
|
23
|
+
int gc=s.grid[c];float cost=1/speed+(gc==BRICK?1.8f:gc==STEEL?2.0f:0);
|
|
24
|
+
for(int k=0;k<4;k++){int n=c+DIR_DELTA[k],g=s.grid[n];if(g!=EMPTY&&g!=BRICK&&!(g==STEEL&&steel))continue;
|
|
25
|
+
float nv=v+cost;if(nv<d[n]){d[n]=nv;q.push({nv,n});}}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
void setup(const State&s,int me){
|
|
29
|
+
ctx.me=me;ctx.nw=0;ctx.wh=s.wall_hash;std::fill(ctx.occ,ctx.occ+CELLS,-1);for(int i=0;i<6;i++)if(s.tank[i].alive)ctx.occ[s.tank[i].pos]=i;
|
|
30
|
+
for(int seat=0;seat<2;seat++)for(int st=0;st<2;st++){
|
|
31
|
+
float* d=ctx.attack[seat][st];std::fill(d,d+CELLS,INF);
|
|
32
|
+
int base=s.base_pos[1-seat];
|
|
33
|
+
for(int dir=0;dir<4;dir++){float cost=0;int len=0;for(int c=base+DIR_DELTA[dir];s.grid[c]!=OUTSIDE;c+=DIR_DELTA[dir]){
|
|
34
|
+
int g=s.grid[c];if(g==BASE0||g==BASE1||(g==STEEL&&!st))break;len++;cost+=(g==BRICK||g==STEEL)?1.9f:0;
|
|
35
|
+
if(g!=WATER)d[c]=cost+len*.75f;
|
|
36
|
+
int blocker=ctx.occ[c];if(blocker>=0)cost+=(blocker/3==seat?2.0f:6.0f);}}
|
|
37
|
+
dijkstra(s,d,st,1);
|
|
38
|
+
}
|
|
39
|
+
for(int st=0;st<2;st++)for(int k=0;k<s.n_supply;k++){float*d=ctx.sd[st][k];std::fill(d,d+CELLS,INF);d[s.supply[k]]=0;dijkstra(s,d,st,1);}
|
|
40
|
+
|
|
41
|
+
for(int j=0;j<6;j++){
|
|
42
|
+
float*d=ctx.ed[j];std::fill(d,d+CELLS,INF);if(!s.tank[j].alive)continue;
|
|
43
|
+
int p=s.tank[j].pos;d[p]=1;
|
|
44
|
+
for(int k=0;k<4;k++)for(int n=1,c=p+DIR_DELTA[k];n<=5;n++,c+=DIR_DELTA[k]){
|
|
45
|
+
if(s.grid[c]!=EMPTY&&s.grid[c]!=WATER)break;
|
|
46
|
+
if(s.grid[c]==EMPTY)d[c]=n*.65f;
|
|
47
|
+
}
|
|
48
|
+
dijkstra(s,d,false,1);
|
|
49
|
+
}
|
|
50
|
+
for(int seat=0;seat<2;seat++){
|
|
51
|
+
float util[3][8];
|
|
52
|
+
for(int t=0;t<3;t++){int i=3*seat+t;const Tank&a=s.tank[i];int p=a.alive?a.pos:s.spawn[i];float speed=t==0?1.65f:1;
|
|
53
|
+
util[t][0]=34-1.6f*ctx.attack[seat][a.level>=2][p]/speed;
|
|
54
|
+
for(int k=0;k<s.n_supply;k++){
|
|
55
|
+
float dis=ctx.sd[a.level>=2][k][p]/speed;
|
|
56
|
+
int wait=(s.star_mask>>k&1)?0:(s.turn<10?11-s.turn:21-(s.turn-10)%20);
|
|
57
|
+
float val=33-2.25f*dis-3.6f*std::max(0.0f,wait-dis);
|
|
58
|
+
if(a.level>=2)val=-1000;
|
|
59
|
+
if(a.level==0)val+=7;else val-=12;
|
|
60
|
+
if(t==0)val+=3;
|
|
61
|
+
util[t][k+1]=val;
|
|
62
|
+
}
|
|
63
|
+
for(int e=0;e<3;e++){
|
|
64
|
+
int j=(1-seat)*3+e;float bd=ctx.attack[1-seat][s.tank[j].level>=2][s.tank[j].pos];
|
|
65
|
+
int home=md(s.tank[j].pos,s.base_pos[seat]);float threat=std::max(0,8-home)*4.5f;
|
|
66
|
+
float dis=ctx.ed[j][p]/speed;
|
|
67
|
+
util[t][s.n_supply+1+e]=s.tank[j].alive&&home<=7?40+threat-2.5f*dis:-1000;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
int goals[3]={};float best=-1e9;
|
|
71
|
+
for(int a=0;a<=s.n_supply+3;a++)for(int b=0;b<=s.n_supply+3;b++)for(int c=0;c<=s.n_supply+3;c++){
|
|
72
|
+
if((a&&a==b)||(a&&a==c)||(b&&b==c))continue;
|
|
73
|
+
int nd=(a>s.n_supply)+(b>s.n_supply)+(c>s.n_supply);
|
|
74
|
+
float v=util[0][a]+util[1][b]+util[2][c]-std::max(0,nd-1)*24;if(v>best){best=v;goals[0]=a;goals[1]=b;goals[2]=c;}}
|
|
75
|
+
for(int t=0;t<3;t++){int i=seat*3+t;const Tank&a=s.tank[i];float speed=t==0?1.65f:1;int goal=goals[t];
|
|
76
|
+
for(int c=0;c<CELLS;c++){
|
|
77
|
+
float atk=34-1.6f*std::min(40.f,ctx.attack[seat][a.level>=2][c]/speed);
|
|
78
|
+
float v=atk;
|
|
79
|
+
if(goal&&goal<=s.n_supply){int k=goal-1;float dis=ctx.sd[a.level>=2][k][c]/speed;v=47-2.25f*std::min(40.f,dis);}
|
|
80
|
+
if(goal>s.n_supply){int j=(1-seat)*3+goal-s.n_supply-1;v=50-2.5f*std::min(40.f,ctx.ed[j][c]/speed);}
|
|
81
|
+
ctx.field[i][c]=v;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Credit terrain changes immediately, including bullets consumed this turn.
|
|
86
|
+
for(int c=0;c<CELLS;c++)if(s.grid[c]==BRICK||s.grid[c]==STEEL){
|
|
87
|
+
ctx.walls[ctx.nw++]=c;float vv[2]={};
|
|
88
|
+
for(int i=0;i<6;i++)if(s.tank[i].alive){int p=s.tank[i].pos;int d=md(p,c);float gain=ctx.field[i][c]-ctx.field[i][p];
|
|
89
|
+
if(d<=6&&gain>0)vv[i/3]=std::max(vv[i/3],3.8f+gain*.45f-std::max(0,d-2)*.6f);
|
|
90
|
+
}
|
|
91
|
+
if(md(c,s.base_pos[0])<=2)vv[1]+=7;
|
|
92
|
+
if(md(c,s.base_pos[1])<=2)vv[0]+=7;
|
|
93
|
+
ctx.wallval[c]=vv[0]-vv[1];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
float bulletval(const State&s,const Bullet&b){
|
|
97
|
+
int seat=seat_of(b.owner),pos=b.pos;
|
|
98
|
+
float val=0;
|
|
99
|
+
for(int n=1;n<=s.P.size+1;n++){
|
|
100
|
+
pos+=DIR_DELTA[b.dir];int g=s.grid[pos];if(g==OUTSIDE)break;
|
|
101
|
+
if(g==BRICK||(g==STEEL&&b.steel)){
|
|
102
|
+
float gain=ctx.wallval[pos]*(seat==0?1:-1);
|
|
103
|
+
val=gain;break;
|
|
104
|
+
}
|
|
105
|
+
if(g==STEEL)break;
|
|
106
|
+
if(g==BASE0||g==BASE1){int side=g-BASE0;val=(side!=seat?1.f:-1.f)*135.f/(1+.035f*n);break;}
|
|
107
|
+
int hit=tank_at(s,pos);
|
|
108
|
+
if(hit>=0){
|
|
109
|
+
int free=0;for(int k=0;k<4;k++)if((k/2)!=(b.dir/2)&&s.grid[pos+DIR_DELTA[k]]==EMPTY&&tank_at(s,pos+DIR_DELTA[k])<0)free++;
|
|
110
|
+
float p=(n<=2?.36f:.20f);if(free==0)p=.8f;
|
|
111
|
+
if(can_fire(s,hit))p*=.55f;
|
|
112
|
+
float damage=16+(s.tank[hit].hp==1?45.f:0);
|
|
113
|
+
val=damage*p*(seat_of(hit)==seat?-1.f:1.f);break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return seat==0?val:-val;
|
|
117
|
+
}
|
|
118
|
+
float eval(const State&s){
|
|
119
|
+
if(s.done)return s.winner==0?1e6f:s.winner==1?-1e6f:0;
|
|
120
|
+
float z=(s.base_hp[0]-s.base_hp[1])*180.f+(s.kills[0]-s.kills[1])*28.f+(s.stars_eaten[0]-s.stars_eaten[1])*2;
|
|
121
|
+
for(int i=0;i<6;i++){
|
|
122
|
+
const Tank&t=s.tank[i];float v;
|
|
123
|
+
if(!t.alive)v=-3*respawn_in(s,i);
|
|
124
|
+
else {
|
|
125
|
+
v=40+t.hp*15+t.level*8+ctx.field[i][t.pos];
|
|
126
|
+
if(can_fire(s,i))v+=1.4f;
|
|
127
|
+
int base=s.base_pos[1-i/3];int dir=-1;
|
|
128
|
+
if(cell_x(base)==cell_x(t.pos))dir=base>t.pos?DOWN:UP;
|
|
129
|
+
if(cell_y(base)==cell_y(t.pos))dir=base>t.pos?RIGHT:LEFT;
|
|
130
|
+
if(dir>=0){
|
|
131
|
+
bool clear=true;int dis=md(base,t.pos);
|
|
132
|
+
for(int c=t.pos+DIR_DELTA[dir];c!=base;c+=DIR_DELTA[dir])if(s.grid[c]==BRICK||s.grid[c]==STEEL||tank_at(s,c)>=0){clear=false;break;}
|
|
133
|
+
if(clear)v+=100.f/(1+dis*.35f)*(t.level==0&&i%3!=FIRE?.8f:1.f);
|
|
134
|
+
}
|
|
135
|
+
for(int j=0;j<6;j++)if(seat_of(j)!=seat_of(i)&&!s.tank[j].alive&&s.spawn[j]==t.pos)v-=respawn_in(s,j)<=1?100:15;
|
|
136
|
+
// Only a modest exploration penalty; do not abandon productive siege squares.
|
|
137
|
+
v-=std::min(8,hist[i][t.pos])*.11f;
|
|
138
|
+
}
|
|
139
|
+
z+=(i<3?v:-v);
|
|
140
|
+
}
|
|
141
|
+
if(s.wall_hash!=ctx.wh)for(int k=0;k<ctx.nw;k++){int c=ctx.walls[k];if(s.grid[c]==EMPTY)z+=ctx.wallval[c];}
|
|
142
|
+
for(int k=0;k<s.n_bullets;k++)z+=bulletval(s,s.bullet[k]);
|
|
143
|
+
return z;
|
|
144
|
+
}
|
|
145
|
+
float value(const State&s,int seat,const Orders&a,const Orders&b){State n=s;step_as(n,seat,a,b);float v=eval(n);return seat==0?v:-v;}
|
|
146
|
+
struct Choice {Cmd c;float v;};
|
|
147
|
+
struct Pool{std::vector<Orders> a;void add(Orders x){for(auto y:a)if(x==y)return;a.push_back(x);}};
|
|
148
|
+
Orders improve(const State&s,int seat,Orders own,const Orders&enemy, std::array<std::vector<Choice>,3>*rank=nullptr){
|
|
149
|
+
for(int t=0;t<3;t++){
|
|
150
|
+
Cmd cmds[N_CMDS];int n=legal_commands(s,seat*3+t,cmds);float best=-1e20;Cmd bc=0;std::vector<Choice>ls;
|
|
151
|
+
for(int k=0;k<n;k++){Orders a=own;a.cmd[t]=cmds[k];float v=value(s,seat,a,enemy);ls.push_back({cmds[k],v});if(v>best+1e-4){best=v;bc=cmds[k];}}
|
|
152
|
+
own.cmd[t]=bc;
|
|
153
|
+
if(rank){std::sort(ls.begin(),ls.end(),[](Choice a,Choice b){return a.v>b.v;});(*rank)[t]=ls;}
|
|
154
|
+
}
|
|
155
|
+
return own;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
void init(const State&s,int me){using namespace ai;for(auto &row:hist)std::fill(row,row+CELLS,0);rng=7123671+me;}
|
|
159
|
+
Orders decide(const State&s,int me){
|
|
160
|
+
using namespace ai;setup(s,me);
|
|
161
|
+
for(int i=0;i<6;i++)if(s.tank[i].alive)hist[i][s.tank[i].pos]++;
|
|
162
|
+
Orders a,b;
|
|
163
|
+
a=improve(s,me,a,b);b=improve(s,1-me,b,a);
|
|
164
|
+
Pool pa,pb;pa.add(a);pb.add(b);pb.add(Orders{});
|
|
165
|
+
std::array<std::vector<Choice>,3> ra,rb;
|
|
166
|
+
for(int it=0;it<3;it++){
|
|
167
|
+
a=improve(s,me,a,b,&ra);pa.add(a);
|
|
168
|
+
b=improve(s,1-me,b,a,&rb);pb.add(b);
|
|
169
|
+
}
|
|
170
|
+
for(int t=0;t<3;t++){
|
|
171
|
+
for(int k=0;k<std::min(4,(int)ra[t].size());k++){Orders x=a;x.cmd[t]=ra[t][k].c;pa.add(x);}
|
|
172
|
+
for(int k=0;k<std::min(4,(int)rb[t].size());k++){Orders x=b;x.cmd[t]=rb[t][k].c;pb.add(x);}
|
|
173
|
+
}
|
|
174
|
+
// Include coordinated variants; a single best reply cannot express simultaneous dodges.
|
|
175
|
+
for(int k=0;k<9;k++){
|
|
176
|
+
Orders x=a,y=b;for(int t=0;t<3;t++){int r=(k/(t==0?1:t==1?3:1)+t)%3;if(r<(int)ra[t].size())x.cmd[t]=ra[t][r].c;if(r<(int)rb[t].size())y.cmd[t]=rb[t][r].c;}pa.add(x);pb.add(y);
|
|
177
|
+
}
|
|
178
|
+
int na=pa.a.size(),nb=pb.a.size();float mat[32][32];
|
|
179
|
+
for(int i=0;i<na;i++)for(int j=0;j<nb;j++)mat[i][j]=value(s,me,pa.a[i],pb.a[j]);
|
|
180
|
+
// Fictitious play gives a robust mixed policy for the simultaneous-action game.
|
|
181
|
+
float sa[32]={},sb[32]={};int ca[32]={},cb[32]={};int bi=0,bj=0;
|
|
182
|
+
for(int it=0;it<160;it++){
|
|
183
|
+
ca[bi]++;cb[bj]++;
|
|
184
|
+
for(int i=0;i<na;i++)sa[i]+=mat[i][bj];
|
|
185
|
+
for(int j=0;j<nb;j++)sb[j]+=mat[bi][j];
|
|
186
|
+
bi=0;bj=0;for(int i=1;i<na;i++)if(sa[i]>sa[bi])bi=i;for(int j=1;j<nb;j++)if(sb[j]<sb[bj])bj=j;
|
|
187
|
+
}
|
|
188
|
+
int sel=0;float bv=-1e20;
|
|
189
|
+
for(int i=0;i<na;i++){
|
|
190
|
+
float v=0,w=1e20;for(int j=0;j<nb;j++){v+=mat[i][j]*cb[j]/160.f;w=std::min(w,mat[i][j]);}
|
|
191
|
+
v=.85f*v+.15f*w;
|
|
192
|
+
if(v>bv){bv=v;sel=i;}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Extend the most relevant simultaneous choices through three full turns.
|
|
196
|
+
std::vector<int> ia,ib;
|
|
197
|
+
for(int i=0;i<na;i++)ia.push_back(i);
|
|
198
|
+
for(int j=0;j<nb;j++)ib.push_back(j);
|
|
199
|
+
float ar[64]={},br[64]={};
|
|
200
|
+
for(int i=0;i<na;i++){float w=1e20;for(int j=0;j<nb;j++){ar[i]+=mat[i][j]*cb[j]/160.f;w=std::min(w,mat[i][j]);}ar[i]=ar[i]*.85f+.15f*w;}
|
|
201
|
+
for(int j=0;j<nb;j++){float w=-1e20;for(int i=0;i<na;i++){br[j]+=mat[i][j]*ca[i]/160.f;w=std::max(w,mat[i][j]);}br[j]=br[j]*.85f+.15f*w;}
|
|
202
|
+
std::sort(ia.begin(),ia.end(),[&](int x,int y){return ar[x]>ar[y];});
|
|
203
|
+
std::sort(ib.begin(),ib.end(),[&](int x,int y){return br[x]<br[y];});
|
|
204
|
+
ia.erase(std::remove(ia.begin(),ia.end(),sel),ia.end());ia.insert(ia.begin(),sel);
|
|
205
|
+
ia.resize(std::min(12,(int)ia.size()));ib.resize(std::min(10,(int)ib.size()));
|
|
206
|
+
float deep[12][12];int nr=ia.size(),nc=ib.size();
|
|
207
|
+
for(int x=0;x<nr;x++)for(int y=0;y<nc;y++){
|
|
208
|
+
if(budget_left()<6)return pa.a[sel];
|
|
209
|
+
State n=s;step_as(n,me,pa.a[ia[x]],pb.a[ib[y]]);
|
|
210
|
+
float v=eval(n)*(me==0?1:-1)*.25f;
|
|
211
|
+
for(int depth=0;depth<2&&!n.done;depth++){
|
|
212
|
+
Orders aa,bb;
|
|
213
|
+
aa=improve(n,me,aa,bb);bb=improve(n,1-me,bb,aa);
|
|
214
|
+
Orders aa2=improve(n,me,aa,bb);
|
|
215
|
+
step_as(n,me,aa2,bb);
|
|
216
|
+
v+=eval(n)*(me==0?1:-1)*(depth==0?.30f:.45f);
|
|
217
|
+
}
|
|
218
|
+
deep[x][y]=n.done?eval(n)*(me==0?1:-1):v;
|
|
219
|
+
}
|
|
220
|
+
std::fill(sa,sa+32,0);std::fill(sb,sb+32,0);std::fill(ca,ca+32,0);std::fill(cb,cb+32,0);bi=bj=0;
|
|
221
|
+
for(int it=0;it<200;it++){
|
|
222
|
+
ca[bi]++;cb[bj]++;
|
|
223
|
+
for(int i=0;i<nr;i++)sa[i]+=deep[i][bj];
|
|
224
|
+
for(int j=0;j<nc;j++)sb[j]+=deep[bi][j];
|
|
225
|
+
bi=bj=0;for(int i=1;i<nr;i++)if(sa[i]>sa[bi])bi=i;for(int j=1;j<nc;j++)if(sb[j]<sb[bj])bj=j;
|
|
226
|
+
}
|
|
227
|
+
bv=-1e20;
|
|
228
|
+
for(int i=0;i<nr;i++){
|
|
229
|
+
float v=0,w=1e20;for(int j=0;j<nc;j++){v+=deep[i][j]*cb[j]/200.f;w=std::min(w,deep[i][j]);}
|
|
230
|
+
v=.85f*v+.15f*w;if(v>bv){bv=v;sel=ia[i];}
|
|
231
|
+
}
|
|
232
|
+
return pa.a[sel];
|
|
233
|
+
}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
// Tank battle: coordinated supply control, a committed fast flanker, and adaptive defenders.
|
|
2
|
+
// Dijkstra fields provide strategic guidance. Joint orders are evaluated with the exact engine.
|
|
3
|
+
// Quiet positions use three-turn rollouts and double-oracle response expansion; close combat
|
|
4
|
+
// uses a nested simultaneous-action matrix game. All searches retain a legal budget fallback.
|
|
5
|
+
#include "tank.hpp"
|
|
6
|
+
#include <algorithm>
|
|
7
|
+
#include <array>
|
|
8
|
+
#include <cmath>
|
|
9
|
+
#include <queue>
|
|
10
|
+
#include <utility>
|
|
11
|
+
#include <vector>
|
|
12
|
+
#include <cstdio>
|
|
13
|
+
#include <unordered_map>
|
|
14
|
+
using namespace tank;
|
|
15
|
+
namespace bot {
|
|
16
|
+
constexpr float INF=10000;
|
|
17
|
+
float bd[6][CELLS], sd[6][MAX_SUPPLY][CELLS], hd[6][3][CELLS], field[6][CELLS];
|
|
18
|
+
int goal[6], previous[6];float anchor[6];
|
|
19
|
+
uint64_t rng=123456789;
|
|
20
|
+
uint64_t random64(){rng^=rng<<13;rng^=rng>>7;rng^=rng<<17;return rng;}
|
|
21
|
+
float rand01(){return (random64()>>40)*(1.0f/16777216);}
|
|
22
|
+
int md(int a,int b){return std::abs(cell_x(a)-cell_x(b))+std::abs(cell_y(a)-cell_y(b));}
|
|
23
|
+
int direction(int a,int b){if(a==b)return -1;if(cell_x(a)==cell_x(b))return a>b?0:1;if(cell_y(a)==cell_y(b))return a>b?2:3;return -1;}
|
|
24
|
+
bool clear(const State&s,int a,int b){int d=direction(a,b);if(d<0)return false;for(int p=a+DIR_DELTA[d];p!=b;p+=DIR_DELTA[d])if(s.grid[p]!=EMPTY&&s.grid[p]!=WATER)return false;return true;}
|
|
25
|
+
void dijkstra(const State&s,int i,float *dist,const std::vector<std::pair<int,float>>& seeds){
|
|
26
|
+
for(int p=0;p<CELLS;p++)dist[p]=INF;
|
|
27
|
+
using Node=std::pair<float,int>;
|
|
28
|
+
std::priority_queue<Node,std::vector<Node>,std::greater<Node>>q;
|
|
29
|
+
for(auto [p,v]:seeds)if(v<dist[p]){dist[p]=v;q.emplace(v,p);}
|
|
30
|
+
float walk=type_of(i)==FAST?.58f:1.f;
|
|
31
|
+
while(!q.empty()){
|
|
32
|
+
auto [v,p]=q.top();q.pop();if(v>dist[p]+.001f)continue;
|
|
33
|
+
int g=s.grid[p];float extra=g==BRICK?2.f:g==STEEL?3.f:0.f;
|
|
34
|
+
for(int d=0;d<4;d++){
|
|
35
|
+
int n=p+DIR_DELTA[d];if(n<0||n>=CELLS)continue;
|
|
36
|
+
int ng=s.grid[n];if(ng==OUTSIDE||ng==WATER||ng==BASE0||ng==BASE1||(ng==STEEL&&s.tank[i].level<2))continue;
|
|
37
|
+
float nv=v+walk+extra;
|
|
38
|
+
if(nv<dist[n]){dist[n]=nv;q.emplace(nv,n);}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
void prepare(const State&s){
|
|
43
|
+
for(int i=0;i<6;i++){
|
|
44
|
+
int side=seat_of(i),base=s.base_pos[1-side];std::vector<std::pair<int,float>> seeds;
|
|
45
|
+
for(int d=0;d<4;d++){
|
|
46
|
+
int walls=0,range=0;
|
|
47
|
+
for(int p=base+DIR_DELTA[d];p>=0&&p<CELLS;p+=DIR_DELTA[d]){
|
|
48
|
+
int g=s.grid[p];if(g==OUTSIDE||g==BASE0||g==BASE1||(g==STEEL&&s.tank[i].level<2))break;
|
|
49
|
+
range++;if(g==BRICK||g==STEEL)walls++;
|
|
50
|
+
if(g!=WATER)seeds.emplace_back(p,range*(type_of(i)==FAST?1.45f:.85f)+walls*2.0f+((type_of(i)==FAST&&d<2)?5.f:0.f));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
dijkstra(s,i,bd[i],seeds);
|
|
54
|
+
for(int k=0;k<s.n_supply;k++)dijkstra(s,i,sd[i][k],{{s.supply[k],0}});
|
|
55
|
+
}
|
|
56
|
+
for(int i=0;i<6;i++)for(int t=0;t<3;t++){
|
|
57
|
+
int j=(1-seat_of(i))*3+t,p=s.tank[j].pos;
|
|
58
|
+
std::vector<std::pair<int,float>> seeds;
|
|
59
|
+
if(s.tank[j].alive)for(int d=0;d<4;d++)for(int range=1;range<=5;range++){
|
|
60
|
+
int q=p+DIR_DELTA[d]*range;if(q<0||q>=CELLS||s.grid[q]==OUTSIDE)break;
|
|
61
|
+
int g=s.grid[q];if(g==BASE0||g==BASE1)break;if(g==STEEL&&s.tank[i].level<2)break;
|
|
62
|
+
if(g==EMPTY||g==BRICK||g==STEEL)seeds.emplace_back(q,1.0f*range+(g==EMPTY?0.f:2.f));
|
|
63
|
+
if(g==BRICK||g==STEEL)break;
|
|
64
|
+
}
|
|
65
|
+
dijkstra(s,i,hd[i][t],seeds);
|
|
66
|
+
}
|
|
67
|
+
for(int side=0;side<2;side++){
|
|
68
|
+
float options[3][7];int next=s.turn<10?10-s.turn:20-(s.turn-10)%20;
|
|
69
|
+
for(int t=0;t<3;t++){
|
|
70
|
+
int i=side*3+t;int p=s.tank[i].alive?s.tank[i].pos:s.spawn[i];
|
|
71
|
+
options[t][0]=-bd[i][p]*.65f;
|
|
72
|
+
for(int k=0;k<s.n_supply;k++){
|
|
73
|
+
float wait=(s.star_mask&(1<<k))?0.f:float(next+1);
|
|
74
|
+
float eta=sd[i][k][p],enemy=INF;
|
|
75
|
+
for(int j=(1-side)*3;j<(1-side)*3+3;j++)if(s.tank[j].alive&&s.tank[j].level<2)enemy=std::min(enemy,sd[j][k][s.tank[j].pos]);
|
|
76
|
+
float value=(s.tank[i].level==0?17.f:0.f)-1.5f*std::max(eta,wait);
|
|
77
|
+
if(enemy+1<eta&&wait<eta)value-=std::min(10.f,(eta-enemy)*2.f);
|
|
78
|
+
if(!s.tank[i].alive)value=-INF;
|
|
79
|
+
if(s.tank[i].level>=2)value=-INF;
|
|
80
|
+
options[t][k+1]=value;
|
|
81
|
+
}
|
|
82
|
+
for(int e=0;e<3;e++){
|
|
83
|
+
int j=(1-side)*3+e;
|
|
84
|
+
float threat=-INF;
|
|
85
|
+
if(s.tank[j].alive){
|
|
86
|
+
int dist=md(s.tank[j].pos,s.base_pos[side]);
|
|
87
|
+
if(dist<=7){threat=27.f-2.f*dist+3.f*s.tank[j].level;
|
|
88
|
+
if(clear(s,s.tank[j].pos,s.base_pos[side]))threat+=18.f;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
options[t][4+e]=threat-1.5f*hd[i][e][p];
|
|
92
|
+
if(t==0||!s.tank[i].alive)options[t][4+e]=-INF;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
float best=-INF*10;int choice[3]={};
|
|
96
|
+
for(int a=0;a<7;a++)for(int b=0;b<7;b++)for(int c=0;c<7;c++){
|
|
97
|
+
if((a&&a==b)||(a&&a==c)||(b&&b==c))continue;
|
|
98
|
+
float v=options[0][a]+options[1][b]+options[2][c];
|
|
99
|
+
if(v>best){best=v;choice[0]=a;choice[1]=b;choice[2]=c;}
|
|
100
|
+
}
|
|
101
|
+
for(int t=0;t<3;t++){
|
|
102
|
+
int i=side*3+t;goal[i]=choice[t]-1;
|
|
103
|
+
for(int p=0;p<CELLS;p++)field[i][p]=goal[i]<0?-bd[i][p]:goal[i]<3?-sd[i][goal[i]][p]:-hd[i][goal[i]-3][p];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
float projectile(const State&s,const Bullet&b){
|
|
108
|
+
int side=seat_of(b.owner),p=b.pos,steps=0;
|
|
109
|
+
for(;;){
|
|
110
|
+
if(p<0||p>=CELLS)return 0;
|
|
111
|
+
int g=s.grid[p];
|
|
112
|
+
if(g==OUTSIDE||g==BRICK||g==STEEL)return 0;
|
|
113
|
+
if(g==BASE0||g==BASE1){
|
|
114
|
+
float value=60.f/(1.f+.08f*steps);
|
|
115
|
+
for(int j=0;j<6;j++)if(seat_of(j)!=side&&s.tank[j].alive){int dd=md(s.tank[j].pos,p);if(dd<steps/2+2)value*=.72f;}
|
|
116
|
+
return (g-BASE0==side?-1.f:1.f)*value;
|
|
117
|
+
}
|
|
118
|
+
for(int j=0;j<6;j++)if(s.tank[j].alive&&s.tank[j].pos==p){
|
|
119
|
+
int escapes=0;for(int d=0;d<4;d++){int n=p+DIR_DELTA[d];if(s.grid[n]==EMPTY&&tank_at(s,n)<0)escapes++;}
|
|
120
|
+
float value=(s.tank[j].hp==1?19.f:9.f)/(1.f+.35f*steps);
|
|
121
|
+
if(steps>0)value*=escapes? .3f:.9f;
|
|
122
|
+
if(steps>0&&can_fire(s,j)&&direction(p,b.pos)>=0)value*=.6f;
|
|
123
|
+
return (seat_of(j)==side?-1.f:1.f)*value;
|
|
124
|
+
}
|
|
125
|
+
p+=DIR_DELTA[b.dir];steps++;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
float evaluate(const State&s,int side){
|
|
129
|
+
if(s.done)return s.winner<0?0.f:(s.winner==side?100000.f:-100000.f);
|
|
130
|
+
float v=140.f*(s.base_hp[side]-s.base_hp[1-side])+30.f*(s.kills[side]-s.kills[1-side])+4.f*(s.stars_eaten[side]-s.stars_eaten[1-side]);
|
|
131
|
+
for(int i=0;i<6;i++){
|
|
132
|
+
auto&t=s.tank[i];float val;
|
|
133
|
+
if(t.alive){
|
|
134
|
+
val=34.f+11.f*t.hp+14.f*t.level;
|
|
135
|
+
val+=5.f*(std::max(-45.f,field[i][t.pos])-anchor[i]);
|
|
136
|
+
if(clear(s,t.pos,s.base_pos[1-seat_of(i)]))val+=25.f/(1.f+.18f*md(t.pos,s.base_pos[1-seat_of(i)]));
|
|
137
|
+
}else val=-4.f*respawn_in(s,i)-20.f;
|
|
138
|
+
v+=(seat_of(i)==side?1.f:-1.f)*val;
|
|
139
|
+
}
|
|
140
|
+
for(int k=0;k<s.n_bullets;k++)v+=(seat_of(s.bullet[k].owner)==side?1.f:-1.f)*projectile(s,s.bullet[k]);
|
|
141
|
+
return v;
|
|
142
|
+
}
|
|
143
|
+
void path(const State&s,int i,Cmd c,int&p1,int&p2){
|
|
144
|
+
p1=p2=s.tank[i].pos;
|
|
145
|
+
int d1=-1,d2=-1;if(is_move(c))d1=c-1;else if(is_move2(c)){d1=(c-9)/4;d2=(c-9)%4;}
|
|
146
|
+
if(d1>=0){int n=p1+DIR_DELTA[d1];if(s.grid[n]==EMPTY&&tank_at(s,n)<0)p1=n;}
|
|
147
|
+
p2=p1;if(d2>=0){int n=p2+DIR_DELTA[d2];if(s.grid[n]==EMPTY&&tank_at(s,n)<0)p2=n;}
|
|
148
|
+
}
|
|
149
|
+
float shot_value(const State&s,int i,int d){
|
|
150
|
+
int p=s.tank[i].pos,side=seat_of(i),steps=0;
|
|
151
|
+
for(int n=p+DIR_DELTA[d];;n+=DIR_DELTA[d]){
|
|
152
|
+
steps++;int g=s.grid[n];if(g==OUTSIDE)return 0;
|
|
153
|
+
if(g==BASE0||g==BASE1)return g-BASE0==side?-200.f:70.f/(1.f+.08f*steps);
|
|
154
|
+
int j=tank_at(s,n);
|
|
155
|
+
if(j>=0){
|
|
156
|
+
if(seat_of(j)==side)return -25.f/(1.f+.1f*steps);
|
|
157
|
+
int exits=0;for(int k=0;k<4;k++)if(s.grid[n+DIR_DELTA[k]]==EMPTY&&tank_at(s,n+DIR_DELTA[k])<0)exits++;
|
|
158
|
+
float value=(s.tank[j].hp==1?20.f:13.f)/(1+.35f*steps);
|
|
159
|
+
if(exits>1)value*=.65f;
|
|
160
|
+
if(can_fire(s,j))value*=.65f;
|
|
161
|
+
return value;
|
|
162
|
+
}
|
|
163
|
+
if(g==BRICK||g==STEEL){
|
|
164
|
+
if(g==STEEL&&s.tank[i].level<2)return 0;
|
|
165
|
+
float gain=field[i][n]-field[i][p];
|
|
166
|
+
float value=std::max(0.f,2.0f*gain-1.f)/(1.f+.2f*steps);
|
|
167
|
+
int base=s.base_pos[1-side];
|
|
168
|
+
if(direction(p,base)==d){
|
|
169
|
+
bool line=true;for(int q=n+DIR_DELTA[d];q!=base;q+=DIR_DELTA[d]){if(s.grid[q]==OUTSIDE||(s.grid[q]==STEEL&&s.tank[i].level<2)){line=false;break;}}
|
|
170
|
+
if(line)value+=12.f/(1.f+.1f*md(p,base));
|
|
171
|
+
}
|
|
172
|
+
int own=s.base_pos[side];if(md(n,own)<=2&&md(p,own)<6)value-=8.f;
|
|
173
|
+
return value;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
float primitive(const State&s,int i,Cmd c){
|
|
178
|
+
if(!s.tank[i].alive)return 0;
|
|
179
|
+
int p=s.tank[i].pos,p1,p2;path(s,i,c,p1,p2);
|
|
180
|
+
float val=3.2f*(field[i][p2]-field[i][p]);
|
|
181
|
+
bool fire=is_fire(c)&&can_fire(s,i);int fd=fire?c-5:-1;
|
|
182
|
+
if(fire)val+=shot_value(s,i,fd);
|
|
183
|
+
if(c==STAY)val-=.12f;
|
|
184
|
+
if(p2==p&&is_move(c))val-=.4f;
|
|
185
|
+
int damage=0;
|
|
186
|
+
for(int k=0;k<s.n_bullets;k++){
|
|
187
|
+
const auto&b=s.bullet[k];int n=b.pos+DIR_DELTA[b.dir],nn=n+DIR_DELTA[b.dir];
|
|
188
|
+
bool cancel=false;
|
|
189
|
+
if(fire){int f1=p+DIR_DELTA[fd],f2=f1+DIR_DELTA[fd];cancel=(f1==n)||(f1==b.pos&&n==p)||(f2==nn)||(f2==n&&nn==f1);}
|
|
190
|
+
if(cancel)continue;
|
|
191
|
+
bool hit=n==p1||(n==p&&b.pos==p1);
|
|
192
|
+
if(!hit&&s.grid[n]!=BRICK&&s.grid[n]!=STEEL&&s.grid[n]!=OUTSIDE&&tank_at(s,n)<0)hit=nn==p2||(nn==p1&&n==p2);
|
|
193
|
+
if(hit)damage++;
|
|
194
|
+
}
|
|
195
|
+
if(damage)val-=damage*17.f+(damage>=s.tank[i].hp?65.f+15*s.tank[i].level:0.f);
|
|
196
|
+
for(int j=0;j<6;j++)if(seat_of(i)!=seat_of(j)&&s.tank[j].alive&&can_fire(s,j)){
|
|
197
|
+
int e=s.tank[j].pos;
|
|
198
|
+
for(int d=0;d<4;d++){
|
|
199
|
+
int n=e+DIR_DELTA[d],nn=n+DIR_DELTA[d];
|
|
200
|
+
bool hit=(n==p1)||(nn==p2&&s.grid[n]!=BRICK&&s.grid[n]!=STEEL&&tank_at(s,n)<0)||(p1==e&&n==p);
|
|
201
|
+
if(hit){if(fire&&fd==(d^1)&&direction(p,e)==fd)continue;val-=(s.tank[i].hp==1?17.f:7.f);}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
for(int j=0;j<6;j++)if(seat_of(j)!=seat_of(i)&&!s.tank[j].alive&&s.spawn[j]==p2&&respawn_in(s,j)<=1)val-=200;
|
|
205
|
+
if(goal[i]>=0&&goal[i]<3&&p2==s.supply[goal[i]]&&s.tank[i].level<2&&(s.star_mask&(1<<goal[i])))val+=28;
|
|
206
|
+
if(p2==previous[i]&&p2!=p)val-=.18f;
|
|
207
|
+
return val;
|
|
208
|
+
}
|
|
209
|
+
struct Rated {Cmd c;float v;};
|
|
210
|
+
struct Plan {Orders o;float v;};
|
|
211
|
+
std::vector<Rated> ranked(const State&s,int i){
|
|
212
|
+
Cmd a[N_CMDS];int n=legal_commands(s,i,a);std::vector<Rated>r;
|
|
213
|
+
for(int k=0;k<n;k++)r.push_back({a[k],primitive(s,i,a[k])});
|
|
214
|
+
std::stable_sort(r.begin(),r.end(),[](auto&a,auto&b){return a.v>b.v;});return r;
|
|
215
|
+
}
|
|
216
|
+
Orders policy(const State&s,int side){
|
|
217
|
+
Orders o;int p1[3],p2[3];
|
|
218
|
+
for(int t=0;t<3;t++){
|
|
219
|
+
int i=side*3+t;auto r=ranked(s,i);float best=-INF;
|
|
220
|
+
for(auto x:r){int a,b;path(s,i,x.c,a,b);float v=x.v;
|
|
221
|
+
for(int j=0;j<t;j++)if(s.tank[side*3+j].alive&&s.tank[i].alive){if(a==p1[j])v-=20;if(b==p2[j])v-=20;}
|
|
222
|
+
if(v>best){best=v;o.cmd[t]=x.c;p1[t]=a;p2[t]=b;}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return o;
|
|
226
|
+
}
|
|
227
|
+
std::vector<Orders> plans(const State&s,int side){
|
|
228
|
+
std::vector<Rated> r[3];for(int t=0;t<3;t++)r[t]=ranked(s,side*3+t);
|
|
229
|
+
std::vector<Plan> all;
|
|
230
|
+
for(int a=0;a<std::min(4,(int)r[0].size());a++)for(int b=0;b<std::min(4,(int)r[1].size());b++)for(int c=0;c<std::min(4,(int)r[2].size());c++){
|
|
231
|
+
Orders o;o.cmd[0]=r[0][a].c;o.cmd[1]=r[1][b].c;o.cmd[2]=r[2][c].c;
|
|
232
|
+
int p1[3],p2[3];float v=r[0][a].v+r[1][b].v+r[2][c].v;
|
|
233
|
+
for(int t=0;t<3;t++)path(s,side*3+t,o.cmd[t],p1[t],p2[t]);
|
|
234
|
+
for(int t=0;t<3;t++)for(int j=t+1;j<3;j++)if(s.tank[side*3+t].alive&&s.tank[side*3+j].alive){if(p1[t]==p1[j])v-=20;if(p2[t]==p2[j])v-=20;}
|
|
235
|
+
all.push_back({o,v});
|
|
236
|
+
}
|
|
237
|
+
std::stable_sort(all.begin(),all.end(),[](auto&a,auto&b){return a.v>b.v;});
|
|
238
|
+
std::vector<Orders>out;
|
|
239
|
+
auto add=[&](Orders o){for(auto q:out)if(q==o)return;out.push_back(o);};
|
|
240
|
+
add(policy(s,side));for(int k=0;k<std::min(14,(int)all.size());k++)add(all[k].o);
|
|
241
|
+
Orders best=out[0];
|
|
242
|
+
for(int t=0;t<3;t++)for(int a=0;a<std::min(7,(int)r[t].size());a++){Orders o=best;o.cmd[t]=r[t][a].c;add(o);}
|
|
243
|
+
add(Orders{});return out;
|
|
244
|
+
}
|
|
245
|
+
std::vector<Orders> smallplans(const State&s,int side){
|
|
246
|
+
Orders base=policy(s,side);std::vector<Orders> a{base};
|
|
247
|
+
for(int t=0;t<3;t++){
|
|
248
|
+
auto r=ranked(s,side*3+t);int got=0;
|
|
249
|
+
for(auto x:r)if(x.c!=base.cmd[t]){Orders q=base;q.cmd[t]=x.c;if(std::find(a.begin(),a.end(),q)==a.end())a.push_back(q);if(++got>=2)break;}
|
|
250
|
+
}
|
|
251
|
+
return a;
|
|
252
|
+
}
|
|
253
|
+
float subgame(const State&s,int side){
|
|
254
|
+
if(s.done)return evaluate(s,side);
|
|
255
|
+
auto a=smallplans(s,side),b=smallplans(s,1-side);int n=a.size(),m=b.size();
|
|
256
|
+
float M[8][8];for(int i=0;i<n;i++)for(int j=0;j<m;j++){State z=s;step_as(z,side,a[i],b[j]);M[i][j]=evaluate(z,side);}
|
|
257
|
+
double u[8]={},v[8]={},ca[8]={},cb[8]={};int x=0,y=0;
|
|
258
|
+
for(int it=0;it<160;it++){
|
|
259
|
+
if(it>=40){ca[x]++;cb[y]++;}
|
|
260
|
+
for(int i=0;i<n;i++)u[i]+=M[i][y];
|
|
261
|
+
for(int j=0;j<m;j++)v[j]+=M[x][j];
|
|
262
|
+
x=std::max_element(u,u+n)-u;y=std::min_element(v,v+m)-v;
|
|
263
|
+
}
|
|
264
|
+
double ans=0;for(int i=0;i<n;i++)for(int j=0;j<m;j++)ans+=ca[i]*cb[j]*M[i][j];return ans/14400;
|
|
265
|
+
}
|
|
266
|
+
struct GameMix {std::vector<double> a,b;};
|
|
267
|
+
GameMix solvegame(const std::vector<std::vector<float>>&M){
|
|
268
|
+
int n=M.size(),m=M[0].size();
|
|
269
|
+
std::vector<double>u(n,0),v(m,0),ca(n,0),cb(m,0);int x=0,y=0;
|
|
270
|
+
for(int it=0;it<1200;it++){
|
|
271
|
+
if(it>=300){ca[x]++;cb[y]++;}
|
|
272
|
+
for(int j=0;j<n;j++)u[j]+=M[j][y];
|
|
273
|
+
for(int j=0;j<m;j++)v[j]+=M[x][j];
|
|
274
|
+
x=std::max_element(u.begin(),u.end())-u.begin();y=std::min_element(v.begin(),v.end())-v.begin();
|
|
275
|
+
}
|
|
276
|
+
for(double &x:ca)x/=900;
|
|
277
|
+
for(double &x:cb)x/=900;
|
|
278
|
+
return {ca,cb};
|
|
279
|
+
}
|
|
280
|
+
uint64_t code(Orders o){return o.cmd[0]|(uint64_t(o.cmd[1])<<8)|(uint64_t(o.cmd[2])<<16);}
|
|
281
|
+
Orders choose_oracle(const State&s,int side){
|
|
282
|
+
prepare(s);for(int i=0;i<6;i++)anchor[i]=std::max(-45.f,field[i][s.tank[i].alive?s.tank[i].pos:s.spawn[i]]);auto a=plans(s,side),b=plans(s,1-side);
|
|
283
|
+
std::unordered_map<uint64_t,float>cache;cache.reserve(4000);
|
|
284
|
+
auto ev=[&](Orders a,Orders b){
|
|
285
|
+
uint64_t key=code(a)|(code(b)<<24);auto it=cache.find(key);if(it!=cache.end())return it->second;
|
|
286
|
+
State z=s;step_as(z,side,a,b);float first=evaluate(z,side);
|
|
287
|
+
for(int h=0;h<2&&!z.done;h++){Orders u=policy(z,side),v=policy(z,1-side);step_as(z,side,u,v);}
|
|
288
|
+
float ans=.3f*first+.7f*evaluate(z,side);cache.emplace(key,ans);return ans;
|
|
289
|
+
};
|
|
290
|
+
GameMix mix;
|
|
291
|
+
for(int round=0;round<3;round++){
|
|
292
|
+
std::vector<std::vector<float>>M(a.size(),std::vector<float>(b.size()));
|
|
293
|
+
for(int x=0;x<(int)a.size();x++)for(int y=0;y<(int)b.size();y++)M[x][y]=ev(a[x],b[y]);
|
|
294
|
+
mix=solvegame(M);if(round==2||budget_left()<18)break;
|
|
295
|
+
std::vector<std::pair<double,Orders>>sa,sb;
|
|
296
|
+
for(int x=0;x<(int)a.size();x++)if(mix.a[x]>.001)sa.emplace_back(mix.a[x],a[x]);
|
|
297
|
+
for(int y=0;y<(int)b.size();y++)if(mix.b[y]>.001)sb.emplace_back(mix.b[y],b[y]);
|
|
298
|
+
auto prep=[](auto&v){std::sort(v.begin(),v.end(),[](auto&a,auto&b){return a.first>b.first;});if(v.size()>6)v.resize(6);double sum=0;for(auto &x:v)sum+=x.first;for(auto &x:v)x.first/=sum;};prep(sa);prep(sb);
|
|
299
|
+
auto response=[&](int who,Orders best,const std::vector<std::pair<double,Orders>>&support){
|
|
300
|
+
auto val=[&](Orders o){double v=0;for(auto &[p,e]:support)v+=p*(who==side?ev(o,e):-ev(e,o));return v;};
|
|
301
|
+
double current=val(best);
|
|
302
|
+
for(int pass=0;pass<2;pass++)for(int t=0;t<3;t++){
|
|
303
|
+
if(budget_left()<7)return best;
|
|
304
|
+
Cmd list[N_CMDS];int n=legal_commands(s,who*3+t,list);Orders old=best;
|
|
305
|
+
for(int c=0;c<n;c++){Orders trial=old;trial.cmd[t]=list[c];double v=val(trial);if(v>current+.001){current=v;best=trial;}}
|
|
306
|
+
}
|
|
307
|
+
return best;
|
|
308
|
+
};
|
|
309
|
+
int ai=std::max_element(mix.a.begin(),mix.a.end())-mix.a.begin(),bi=std::max_element(mix.b.begin(),mix.b.end())-mix.b.begin();
|
|
310
|
+
Orders ar=response(side,a[ai],sb),br=response(1-side,b[bi],sa);
|
|
311
|
+
if(std::find(a.begin(),a.end(),ar)==a.end())a.push_back(ar);
|
|
312
|
+
if(std::find(b.begin(),b.end(),br)==b.end())b.push_back(br);
|
|
313
|
+
}
|
|
314
|
+
double target=rand01(),sum=0;for(int x=0;x<(int)a.size();x++){sum+=mix.a[x];if(sum>=target)return a[x];}
|
|
315
|
+
return a.back();
|
|
316
|
+
}
|
|
317
|
+
Orders choose_nested(const State&s,int side){
|
|
318
|
+
prepare(s);for(int i=0;i<6;i++)anchor[i]=std::max(-45.f,field[i][s.tank[i].alive?s.tank[i].pos:s.spawn[i]]);
|
|
319
|
+
auto a=plans(s,side),b=plans(s,1-side);
|
|
320
|
+
int n=a.size(),m=b.size();std::vector<float>matrix(n*m);
|
|
321
|
+
for(int x=0;x<n;x++)for(int y=0;y<m;y++){
|
|
322
|
+
if((y&3)==0&&budget_left()<5)return policy(s,side);
|
|
323
|
+
State z=s;step_as(z,side,a[x],b[y]);float first=evaluate(z,side);
|
|
324
|
+
matrix[x*m+y]=.15f*first+.85f*subgame(z,side);
|
|
325
|
+
}
|
|
326
|
+
std::vector<double>u(n,0),v(m,0),counts(n,0),opp(m,0);int x=0,y=0;
|
|
327
|
+
for(int it=0;it<220;it++){
|
|
328
|
+
counts[x]++;opp[y]++;
|
|
329
|
+
for(int j=0;j<n;j++)u[j]+=matrix[j*m+y];
|
|
330
|
+
for(int j=0;j<m;j++)v[j]+=matrix[x*m+j];
|
|
331
|
+
x=std::max_element(u.begin(),u.end())-u.begin();y=std::min_element(v.begin(),v.end())-v.begin();
|
|
332
|
+
}
|
|
333
|
+
double target=rand01()*220,acc=0;int chosen=n-1;
|
|
334
|
+
for(int j=0;j<n;j++){acc+=counts[j];if(acc>target){chosen=j;break;}}
|
|
335
|
+
return a[chosen];
|
|
336
|
+
}
|
|
337
|
+
Orders choose(const State&s,int side){
|
|
338
|
+
bool combat=false;
|
|
339
|
+
for(int i=side*3;i<side*3+3;i++)if(s.tank[i].alive){
|
|
340
|
+
for(int j=(1-side)*3;j<(1-side)*3+3;j++)if(s.tank[j].alive){
|
|
341
|
+
int d=md(s.tank[i].pos,s.tank[j].pos);
|
|
342
|
+
if(d<=3||(d<=6&&clear(s,s.tank[i].pos,s.tank[j].pos)))combat=true;
|
|
343
|
+
}
|
|
344
|
+
for(int k=0;k<s.n_bullets;k++)if(md(s.bullet[k].pos,s.tank[i].pos)<=4)combat=true;
|
|
345
|
+
}
|
|
346
|
+
return combat?choose_nested(s,side):choose_oracle(s,side);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
void init(const State&s,int me){bot::rng=0x7d2543a71b9ULL^s.wall_hash^(me*987654321ULL);for(int i=0;i<6;i++)bot::previous[i]=-1;}
|
|
350
|
+
Orders decide(const State&s,int me){Orders o=bot::choose(s,me);for(int i=0;i<6;i++)bot::previous[i]=s.tank[i].alive?s.tank[i].pos:-1;return o;}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Three coordinated vehicles: rapid upgrade and siege, two flexible defenders.
|
|
2
|
+
// Weighted path fields include the cost of opening brick/steel corridors.
|
|
3
|
+
// All tactical transitions use the authoritative simultaneous-action engine.
|
|
4
|
+
// Opponent response weights come from regret matching; risk tolerance follows
|
|
5
|
+
// its demonstrated upgrade progress. The final choice includes a second turn.
|
|
6
|
+
#include "tank.hpp"
|
|
7
|
+
#include <algorithm>
|
|
8
|
+
#include <cmath>
|
|
9
|
+
#include <queue>
|
|
10
|
+
#include <utility>
|
|
11
|
+
#include <vector>
|
|
12
|
+
using namespace tank;
|
|
13
|
+
namespace {
|
|
14
|
+
constexpr float INF=10000;
|
|
15
|
+
struct Context { const State* root; float attack[6][CELLS],supply[6][MAX_SUPPLY][CELLS],wall[2][CELLS],defend[6][CELLS]; int goal[6],threat[6]; };
|
|
16
|
+
static Context ctx;
|
|
17
|
+
static int priorGoal[6],visits[6][CELLS];
|
|
18
|
+
float walkCost(const State&s,int i,int c){int g=s.grid[c];float v=i%3==FAST?.56f:1.f;if(g==EMPTY)return v;if(g==BRICK||(g==STEEL&&s.tank[i].level>=2))return v+3.4f;return INF;}
|
|
19
|
+
void flood(const State&s,int i,float*dis){using Node=std::pair<float,int>;std::priority_queue<Node,std::vector<Node>,std::greater<Node>>q;for(int y=0;y<s.P.size;y++)for(int x=0;x<s.P.size;x++){int c=cell_of(x,y);if(dis[c]<INF)q.push({dis[c],c});}while(!q.empty()){auto[v,c]=q.top();q.pop();if(v!=dis[c])continue;float cost=walkCost(s,i,c);if(cost>=INF)continue;for(int d=0;d<4;d++){int n=c+DIR_DELTA[d];if(walkCost(s,i,n)>=INF)continue;float z=v+cost;if(z<dis[n]){dis[n]=z;q.push({z,n});}}}}
|
|
20
|
+
void prepare(const State&s){ctx.root=&s;for(int i=0;i<6;i++){float*a=ctx.attack[i];std::fill(a,a+CELLS,INF);int base=s.base_pos[1-i/3];for(int d=0;d<4;d++){float walls=0;int r=0;for(int c=base+DIR_DELTA[d];s.grid[c]!=OUTSIDE;c+=DIR_DELTA[d]){r++;int g=s.grid[c];if(g==BASE0||g==BASE1||(g==STEEL&&s.tank[i].level<2))break;if(g==BRICK||g==STEEL)walls+=3.4f;if(g==EMPTY)a[c]=walls+.85f*r;}}flood(s,i,a);for(int k=0;k<s.n_supply;k++){float*b=ctx.supply[i][k];std::fill(b,b+CELLS,INF);b[s.supply[k]]=0;flood(s,i,b);}}
|
|
21
|
+
int wait=s.turn<10?10-s.turn:20-(s.turn-10)%20;
|
|
22
|
+
for(int seat=0;seat<2;seat++){float best=-1e9;int bg[3]={-1,-1,-1};for(int a=-1;a<s.n_supply;a++)for(int b=-1;b<s.n_supply;b++)for(int c=-1;c<s.n_supply;c++){int gs[3]={a,b,c};if((a>=0&&a==b)||(a>=0&&a==c)||(b>=0&&b==c))continue;float v=0;bool valid=true;for(int t=0;t<3;t++){int i=3*seat+t,k=gs[t];const auto&tk=s.tank[i];int p=tk.alive?tk.pos:s.spawn[i];float av=18-1.35f*ctx.attack[i][p];if(k<0){v+=av*(t==0?2.5f:1.f);continue;}if(tk.level>=2||(t==0&&tk.level>=1)){valid=false;break;}float dis=ctx.supply[i][k][p];int w=(s.star_mask>>k&1)?0:wait;float sv=(tk.level==0?30.f:24.f)-2.4f*dis-2.4f*std::max(0.f,float(w)-dis);if(priorGoal[i]==k)sv+=1.1f;if(!tk.alive)sv-=4;v+=sv*(t==0?2.5f:1.f);}if(valid&&v>best){best=v;for(int t=0;t<3;t++)bg[t]=gs[t];}}for(int t=0;t<3;t++)ctx.goal[3*seat+t]=bg[t];}
|
|
23
|
+
for(int seat=0;seat<2;seat++) {
|
|
24
|
+
std::vector<std::pair<float,int>> threats;
|
|
25
|
+
for(int i=3*seat;i<3*seat+3;i++)ctx.threat[i]=-1;
|
|
26
|
+
for(int j=3*(1-seat);j<3*(1-seat)+3;j++)if(s.tank[j].alive) {
|
|
27
|
+
int p=s.tank[j].pos,base=s.base_pos[seat],dy=std::abs(cell_y(p)-cell_y(base));float severity=8-dy;
|
|
28
|
+
if(cell_x(p)==cell_x(base)||cell_y(p)==cell_y(base)) {
|
|
29
|
+
int de=cell_x(p)==cell_x(base)?(base>p?STRIDE:-STRIDE):(base>p?1:-1);int walls=0;
|
|
30
|
+
for(int c=p+de;c!=base;c+=de){if(s.grid[c]==BRICK)walls++;if(s.grid[c]==STEEL)walls+=5;}if(walls<=1)severity+=9;
|
|
31
|
+
}if(dy<8&&severity>0)threats.push_back({severity,j});
|
|
32
|
+
}std::sort(threats.rbegin(),threats.rend());
|
|
33
|
+
for(int k=0;k<std::min(2,int(threats.size()));k++){
|
|
34
|
+
int threat=threats[k].second;if(k>0&&threats[k].first<4)break;
|
|
35
|
+
int besti=-1;float bestcost=1e9;
|
|
36
|
+
for(int i=3*seat;i<3*seat+3;i++)if(ctx.goal[i]!=-2){
|
|
37
|
+
float dis[CELLS];std::fill(dis,dis+CELLS,INF);int target=s.tank[threat].pos;
|
|
38
|
+
for(int d=0;d<4;d++){float walls=0;for(int c=target+DIR_DELTA[d],r=1;r<=8;c+=DIR_DELTA[d],r++) {
|
|
39
|
+
int g=s.grid[c];if(g==OUTSIDE||g==BASE0||g==BASE1||(g==STEEL&&s.tank[i].level<2))break;
|
|
40
|
+
if(g==BRICK||g==STEEL)walls+=3.4f;if(g==EMPTY)dis[c]=walls+.6f*r;
|
|
41
|
+
}}flood(s,i,dis);int p=s.tank[i].alive?s.tank[i].pos:s.spawn[i];float cost=dis[p];
|
|
42
|
+
if(!s.tank[i].alive)cost+=respawn_in(s,i);if(ctx.attack[i][p]<2.5f&&s.tank[i].level>=1)cost+=16;if(i%3==FAST)cost+=20;
|
|
43
|
+
if(cost<bestcost){bestcost=cost;besti=i;std::copy(dis,dis+CELLS,ctx.defend[i]);}
|
|
44
|
+
}if(besti>=0&&bestcost<20){ctx.goal[besti]=-2;ctx.threat[besti]=threat;}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
for(int seat=0;seat<2;seat++){std::fill(ctx.wall[seat],ctx.wall[seat]+CELLS,0);for(int i=3*seat;i<3*seat+3;i++){int p=s.tank[i].alive?s.tank[i].pos:s.spawn[i];const float*dis=ctx.goal[i]==-2?ctx.defend[i]:(ctx.goal[i]<0?ctx.attack[i]:ctx.supply[i][ctx.goal[i]]);for(int d=0;d<4;d++){int n=p+DIR_DELTA[d];if(s.grid[n]==BRICK||(s.grid[n]==STEEL&&s.tank[i].level>=2))ctx.wall[seat][n]=std::max(ctx.wall[seat][n],std::max(0.f,dis[p]-dis[n])*.85f);}int base=ctx.goal[i]==-2?s.tank[ctx.threat[i]].pos:s.base_pos[1-seat];if(cell_x(p)==cell_x(base)||cell_y(p)==cell_y(base)){int delta=cell_x(p)==cell_x(base)?(base>p?STRIDE:-STRIDE):(base>p?1:-1);for(int c=p+delta;c!=base;c+=delta){if(s.grid[c]==OUTSIDE||(s.grid[c]==STEEL&&s.tank[i].level<2))break;if(s.grid[c]==BRICK||s.grid[c]==STEEL)ctx.wall[seat][c]=std::max(ctx.wall[seat][c],ctx.goal[i]==-2?12.f:4.5f);}}}}}
|
|
48
|
+
float position(const State&s,int i){const auto&t=s.tank[i];if(!t.alive)return -3.f*respawn_in(s,i);float v=18-1.35f*ctx.attack[i][t.pos];int k=ctx.goal[i];if(k==-2)v=60-4.2f*ctx.defend[i][t.pos];if(k>=0&&t.level<2&&t.level==ctx.root->tank[i].level)v=(t.level==0?30.f:24.f)-2.4f*ctx.supply[i][k][t.pos];v=std::max(-35.f,v);if(t.pos!=ctx.root->tank[i].pos)v-=std::min(2.f,visits[i][t.pos]*.12f);for(int j=3*(1-i/3);j<3*(1-i/3)+3;j++)if(!s.tank[j].alive&&s.spawn[j]==t.pos&&respawn_in(s,j)<=2)v-=280;return v;}
|
|
49
|
+
float evaluation(const State&s,int me){if(s.done)return s.winner<0?0:(s.winner==me?100000.f:-100000.f);float vals[2]={};for(int seat=0;seat<2;seat++){vals[seat]=70.f*s.base_hp[seat]+10.f*s.kills[seat]+1.5f*s.stars_eaten[seat];for(int i=3*seat;i<3*seat+3;i++){const Tank&t=s.tank[i];if(t.alive)vals[seat]+=35+48.f*t.level+18.f*t.hp+position(s,i);else vals[seat]+=position(s,i);}}
|
|
50
|
+
int occ[CELLS];std::fill(occ,occ+CELLS,-1);for(int i=0;i<6;i++)if(s.tank[i].alive)occ[s.tank[i].pos]=i;for(int b=0;b<s.n_bullets;b++){const auto&bl=s.bullet[b];int seat=bl.owner/3;for(int c=bl.pos+DIR_DELTA[bl.dir],r=1;r<=s.P.size+1;c+=DIR_DELTA[bl.dir],r++){int g=s.grid[c];if(g==OUTSIDE)break;if(g==BRICK||g==STEEL){if(g==BRICK||bl.steel)vals[seat]+=ctx.wall[seat][c]/(1+.12f*r);break;}if(g==BASE0||g==BASE1){int target=g-BASE0;vals[target]-=30.f/(1+.18f*r);break;}int j=occ[c];if(j>=0){float danger=(s.tank[j].hp<=1?17.f:6.f)/(1+.30f*r);int exits=0;for(int d=0;d<4;d++)if((d/2)!=(bl.dir/2)&&s.grid[c+DIR_DELTA[d]]==EMPTY&&occ[c+DIR_DELTA[d]]<0)exits++;if(exits==0)danger*=1.8f;vals[j/3]-=danger;break;}}}
|
|
51
|
+
if(s.wall_hash!=ctx.root->wall_hash)for(int c=0;c<CELLS;c++)if((ctx.root->grid[c]==BRICK||ctx.root->grid[c]==STEEL)&&s.grid[c]==EMPTY)for(int seat=0;seat<2;seat++)vals[seat]+=ctx.wall[seat][c];return vals[me]-vals[1-me];}
|
|
52
|
+
struct Choice{Orders o;float value;};
|
|
53
|
+
std::vector<Choice>choices(const State&s,int seat,int limit,const Orders&other){std::vector<std::pair<float,Cmd>>ls[3];for(int t=0;t<3;t++){Cmd legal[N_CMDS];int n=legal_commands(s,3*seat+t,legal);for(int j=0;j<n;j++){Orders o;o.cmd[t]=legal[j];State z=s;step_as(z,seat,o,other);ls[t].push_back({evaluation(z,seat),legal[j]});}std::stable_sort(ls[t].begin(),ls[t].end(),[](auto a,auto b){return a.first>b.first;});if(ls[t].size()>7)ls[t].resize(7);}std::vector<Choice>out;for(auto a:ls[0])for(auto b:ls[1])for(auto c:ls[2]){Orders o;o.cmd[0]=a.second;o.cmd[1]=b.second;o.cmd[2]=c.second;State z=s;step_as(z,seat,o,other);out.push_back({o,evaluation(z,seat)});}std::stable_sort(out.begin(),out.end(),[](const Choice&a,const Choice&b){return a.value>b.value;});if(int(out.size())>limit)out.resize(limit);return out;}
|
|
54
|
+
Orders greedy(const State&s,int seat,const Orders&other){Orders o;for(int pass=0;pass<2;pass++)for(int t=0;t<3;t++){Cmd legal[N_CMDS];int n=legal_commands(s,3*seat+t,legal);float best=-1e9;Cmd bc=0;for(int k=0;k<n;k++){Orders a=o;a.cmd[t]=legal[k];State z=s;step_as(z,seat,a,other);float v=evaluation(z,seat);if(v>best){best=v;bc=legal[k];}}o.cmd[t]=bc;}return o;}
|
|
55
|
+
}
|
|
56
|
+
void init(const State&,int){std::fill(priorGoal,priorGoal+6,-1);for(auto&row:visits)std::fill(row,row+CELLS,0);}
|
|
57
|
+
Orders decide(const State&s,int me){prepare(s);float sophistication=std::clamp((s.stars_eaten[1-me]-1)*.5f,0.f,1.f); float rootRisk=.75f-.45f*sophistication, continuationRisk=.8f-.4f*sophistication;Orders idle;auto enemy=choices(s,1-me,24,idle);auto seeds=enemy;
|
|
58
|
+
for(int b=0;b<std::min(2,int(seeds.size()));b++)for(int t=0;t<3;t++) {
|
|
59
|
+
Cmd legal[N_CMDS];int n=legal_commands(s,3*(1-me)+t,legal);
|
|
60
|
+
for(int k=0;k<n;k++){Orders o=seeds[b].o;o.cmd[t]=legal[k];
|
|
61
|
+
if(std::none_of(enemy.begin(),enemy.end(),[&](const Choice&z){return z.o==o;}))enemy.push_back({o,0});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
auto mine=choices(s,me,60,enemy[0].o);auto quiet=choices(s,me,10,idle);for(auto a:quiet)if(std::none_of(mine.begin(),mine.end(),[&](const Choice&b){return a.o==b.o;}))mine.push_back(a);struct Ranked{int index;float value;int response;};std::vector<Ranked>rank;
|
|
65
|
+
int n=mine.size(),m=enemy.size();std::vector<float> matrix(n*m);
|
|
66
|
+
for(int a=0;a<n;a++)for(int b=0;b<m;b++){State z=s;step_as(z,me,mine[a].o,enemy[b].o);matrix[a*m+b]=evaluation(z,me);}
|
|
67
|
+
std::vector<float> r1(n),r2(m),p1(n),p2(m),avg2(m),u1(n),u2(m);
|
|
68
|
+
for(int iter=0;iter<80;iter++) {
|
|
69
|
+
float sum1=0,sum2=0;for(int a=0;a<n;a++)sum1+=std::max(0.f,r1[a]);for(int b=0;b<m;b++)sum2+=std::max(0.f,r2[b]);
|
|
70
|
+
for(int a=0;a<n;a++)p1[a]=sum1>1e-5?std::max(0.f,r1[a])/sum1:1.f/n;
|
|
71
|
+
for(int b=0;b<m;b++){p2[b]=sum2>1e-5?std::max(0.f,r2[b])/sum2:1.f/m;avg2[b]+=p2[b];}
|
|
72
|
+
std::fill(u1.begin(),u1.end(),0);std::fill(u2.begin(),u2.end(),0);float v=0;
|
|
73
|
+
for(int a=0;a<n;a++)for(int b=0;b<m;b++){float x=matrix[a*m+b];u1[a]+=p2[b]*x;u2[b]-=p1[a]*x;}
|
|
74
|
+
for(int a=0;a<n;a++)v+=p1[a]*u1[a];for(int a=0;a<n;a++)r1[a]+=u1[a]-v;for(int b=0;b<m;b++)r2[b]+=u2[b]+v;
|
|
75
|
+
}
|
|
76
|
+
for(int a=0;a<n;a++){float worst=1e9,sum=0;int wi=0;for(int b=0;b<m;b++){float v=matrix[a*m+b];if(v<worst){worst=v;wi=b;}sum+=v*avg2[b]/80;}
|
|
77
|
+
rank.push_back({a,rootRisk*worst+(1-rootRisk)*sum,wi});}
|
|
78
|
+
std::stable_sort(rank.begin(),rank.end(),[](auto a,auto b){return a.value>b.value;});Orders result=mine[rank[0].index].o;float best=-1e9;for(int k=0;k<std::min(8,int(rank.size()));k++){if(budget_left()<12)break;auto r=rank[k];float worst=1e9,sum=0;int responses[3]={r.response,0,int(enemy.size())>1?1:0};for(int b:responses){if(budget_left()<6)return result;State z=s;step_as(z,me,mine[r.index].o,enemy[b].o);float v=evaluation(z,me);if(!z.done){Orders eo=greedy(z,1-me,idle),mo=greedy(z,me,eo);eo=greedy(z,1-me,mo);mo=greedy(z,me,eo);State q=z;step_as(q,me,mo,eo);v=.35f*v+.65f*evaluation(q,me);}worst=std::min(worst,v);sum+=v;}float value=.8f*(continuationRisk*worst+(1-continuationRisk)*sum/3)+.2f*r.value;if(value>best){best=value;result=mine[r.index].o;}}for(int i=0;i<6;i++){priorGoal[i]=ctx.goal[i];if(s.tank[i].alive)visits[i][s.tank[i].pos]++;}return result;}
|