код алгоритма (19.6 КБ)
function решить(в) {
try {
const ход = в.ход | 0;
const всего = (в.всего_ходов | 0) > 0 ? (в.всего_ходов | 0) : 150;
const N = в.размер || 15;
const я = в.я;
const видно = в.видно || [];
const МБ = в.моя_база;
const ЧБ = в.чужая_база;
const память = в.память && typeof в.память === "object" ? в.память : {};
const O = в.общая && typeof в.общая === "object" ? в.общая : {};
const DX = [0, 0, -1, 1];
const DY = [-1, 1, 0, 0];
const DK = ["С", "Ю", "З", "В"];
const осталось = всего - ход;
const груз = я.груз ? 1 : 0;
const hp = я.здоровье | 0;
const наБазе = я.x === МБ.x && я.y === МБ.y;
const idStr = String(я.id);
let idn = 0;
for (let i = 0; i < idStr.length; i++) idn = (idn * 31 + idStr.charCodeAt(i)) | 0;
if (idn < 0) idn = -idn;
const homeTL = МБ.x + МБ.y < 14;
if (!O.n || O.n.length !== N) {
O.n = [];
O.w = [];
for (let i = 0; i < N; i++) {
O.n[i] = 0;
O.w[i] = 0;
}
for (let i = 0; i < N; i++) {
O.n[0] |= 1 << i;
O.n[N - 1] |= 1 << i;
O.n[i] |= 1 | (1 << (N - 1));
}
}
if (!O.f || typeof O.f !== "object") O.f = {};
if (!O.tg || typeof O.tg !== "object") O.tg = {};
if (!O.cl || typeof O.cl !== "object") O.cl = {};
O.le = O.le || null;
O.sc = O.sc | 0;
function isW(x, y) {
if (x <= 0 || x >= N - 1 || y <= 0 || y >= N - 1) return 1;
return (O.w[y] >>> x) & 1;
}
function setW(x, y) {
if (x <= 0 || x >= N - 1 || y <= 0 || y >= N - 1) return;
O.w[y] |= 1 << x;
const ox = N - 1 - x, oy = N - 1 - y;
if (ox > 0 && ox < N - 1 && oy > 0 && oy < N - 1) O.w[oy] |= 1 << ox;
}
function markS(x, y) {
if (x < 0 || y < 0 || x >= N || y >= N) return;
O.n[y] |= 1 << x;
}
function isS(x, y) {
if (x < 0 || y < 0 || x >= N || y >= N) return 1;
return (O.n[y] >>> x) & 1;
}
function манх(x1, y1, x2, y2) {
const ax = x1 > x2 ? x1 - x2 : x2 - x1;
const ay = y1 > y2 ? y1 - y2 : y2 - y1;
return ax + ay;
}
function key(x, y) { return (x << 4) + y; }
const vis = {};
const враги = [];
const своиВид = [];
for (let i = 0; i < видно.length; i++) {
const c = видно[i];
const k = key(c.x, c.y);
vis[k] = c;
markS(c.x, c.y);
if (c.что === "стена") setW(c.x, c.y);
const е = c.еда | 0;
if (е > 0) {
O.f[k] = [е, ход, 0];
const ox = N - 1 - c.x, oy = N - 1 - c.y;
const ok = key(ox, oy);
if (!isS(ox, oy) && !isW(ox, oy)) {
const prev = O.f[ok];
if (!prev || prev[2]) O.f[ok] = [е, ход, 1];
}
} else if (O.f[k]) {
delete O.f[k];
}
if (c.x === я.x && c.y === я.y) continue;
if (c.что === "враг") {
враги.push(c);
O.le = { x: c.x, y: c.y, t: ход };
} else if (c.что === "свой") своиВид.push(c);
}
if ((ход & 7) === (idn & 7)) {
for (const k in O.f) {
const inf = O.f[k];
const age = ход - (inf[1] | 0);
if (inf[2]) {
if (age > 70) delete O.f[k];
} else if (age > 200) delete O.f[k];
}
for (const k in O.cl) {
if ((O.cl[k].t | 0) < ход - 2) delete O.cl[k];
}
}
const stuck = (память.x === я.x && память.y === я.y) ? ((память.st | 0) + 1) : 0;
память.st = stuck;
память.x = я.x;
память.y = я.y;
if (stuck >= 3) {
память.ex = undefined;
память.tx = undefined;
}
for (const id in O.tg) {
if (!O.tg[id] || (O.tg[id].t | 0) < ход - 2) delete O.tg[id];
}
O.tg[idStr] = { x: я.x, y: я.y, g: груз, t: ход, hp: hp };
const ids = [];
for (const id in O.tg) ids.push(id);
if (ids.indexOf(idStr) < 0) ids.push(idStr);
ids.sort();
let myRank = 0;
for (let i = 0; i < ids.length; i++) if (ids[i] === idStr) myRank = i;
const живых = ids.length;
function nearEnemy(x, y) {
for (let i = 0; i < враги.length; i++) {
if (манх(враги[i].x, враги[i].y, x, y) === 1) return 1;
}
return 0;
}
function occNow(x, y) {
const c = vis[key(x, y)];
if (!c) return 0;
if (c.что === "враг" || c.что === "свой" || c.что === "стена") return 1;
return 0;
}
function bfs(sx, sy, avoidTh) {
const cap = N * N;
const dist = new Array(cap);
const first = new Array(cap);
for (let i = 0; i < cap; i++) { dist[i] = 999; first[i] = 0; }
const q = new Array(cap);
let qh = 0, qt = 0;
const si = sy * N + sx;
dist[si] = 0;
q[qt++] = si;
while (qh < qt) {
const idx = q[qh++];
const x = idx % N;
const y = (idx / N) | 0;
const cd = dist[idx];
for (let i = 0; i < 4; i++) {
const nx = x + DX[i];
const ny = y + DY[i];
if (nx < 0 || nx >= N || ny < 0 || ny >= N) continue;
const ni = ny * N + nx;
if (dist[ni] <= cd + 1) continue;
if (isW(nx, ny)) continue;
if (avoidTh && nearEnemy(nx, ny) && !(nx === МБ.x && ny === МБ.y)) continue;
dist[ni] = cd + 1;
first[ni] = first[idx] || (i + 1);
q[qt++] = ni;
}
}
return { dist: dist, first: first };
}
const avoidTh = груз && враги.length > 0 && stuck < 3;
let D = bfs(я.x, я.y, avoidTh);
let D2 = null;
let DH = null;
function dAt(x, y) {
let d = D.dist[y * N + x];
if (d >= 999) {
if (!D2) D2 = bfs(я.x, я.y, 0);
d = D2.dist[y * N + x];
}
return d;
}
function firstTo(x, y) {
let f = D.first[y * N + x];
if (!f || D.dist[y * N + x] >= 999) {
if (!D2) D2 = bfs(я.x, я.y, 0);
f = D2.first[y * N + x];
}
return f ? DK[f - 1] : null;
}
function hAt(x, y) {
if (!DH) DH = bfs(МБ.x, МБ.y, 0);
const d = DH.dist[y * N + x];
return d < 999 ? d : манх(x, y, МБ.x, МБ.y);
}
function клеткаОк(nx, ny, allowEB) {
if (nx < 0 || ny < 0 || nx >= N || ny >= N) return 0;
if (isW(nx, ny)) return 0;
if (nx === ЧБ.x && ny === ЧБ.y && (груз || !allowEB)) return 0;
if (occNow(nx, ny)) return 0;
if (stuck < 2) {
for (const id in O.tg) {
if (id === idStr) continue;
const t = O.tg[id];
if ((t.t | 0) === ход && t.px === nx && t.py === ny) return 0;
}
}
return 1;
}
function идти(dir, allowEB) {
let di = -1;
for (let i = 0; i < 4; i++) if (DK[i] === dir) di = i;
if (di < 0) return { тип: "ждать" };
const nx = я.x + DX[di];
const ny = я.y + DY[di];
if (!клеткаОк(nx, ny, allowEB)) return null;
return { тип: "идти", куда: dir };
}
function кЦели(tx, ty, allowEB) {
if (я.x === tx && я.y === ty) return { тип: "ждать" };
const dir = firstTo(tx, ty);
if (dir) {
const a = идти(dir, allowEB);
if (a) {
const nx = я.x + (dir === "З" ? -1 : dir === "В" ? 1 : 0);
const ny = я.y + (dir === "С" ? -1 : dir === "Ю" ? 1 : 0);
if (!(груз && nearEnemy(nx, ny) && stuck < 2 && манх(я.x, я.y, МБ.x, МБ.y) > 2)) return a;
}
}
let best = null;
let bestS = 1e9;
const start = (idn + ход) & 3;
for (let k = 0; k < 4; k++) {
const i = (start + k) & 3;
const nx = я.x + DX[i];
const ny = я.y + DY[i];
if (!клеткаОк(nx, ny, allowEB)) continue;
let s = манх(nx, ny, tx, ty) * 12;
const dd = dAt(tx, ty);
s += dd < 999 ? dd : 40;
if (груз && nearEnemy(nx, ny)) s += 28;
if (!груз && vis[key(nx, ny)] && (vis[key(nx, ny)].еда | 0) > 0) s -= 40;
if (s < bestS) {
bestS = s;
best = DK[i];
}
}
if (best) return { тип: "идти", куда: best };
for (let k = 0; k < 4; k++) {
const a = идти(DK[(start + k) & 3], allowEB);
if (a) return a;
}
return { тип: "ждать" };
}
function claimN(fx, fy) {
const c = O.cl[key(fx, fy)];
return (c && (c.t | 0) === ход) ? (c.n | 0) : 0;
}
function markClaim(fx, fy) {
const k = key(fx, fy);
const c = O.cl[k];
if (!c || (c.t | 0) !== ход) O.cl[k] = { t: ход, n: 1 };
else c.n = (c.n | 0) + 1;
}
function closerCount(fx, fy, myd) {
let n = 0;
for (const id in O.tg) {
if (id === idStr) continue;
const t = O.tg[id];
if (t.g) continue;
if ((t.t | 0) < ход - 2) continue;
const d = манх(t.x, t.y, fx, fy);
if (t.x === fx && t.y === fy) n++;
else if (d < myd || (d === myd && id < idStr)) n++;
}
return n;
}
function out(act) {
if (!act || !act.тип) act = { тип: "ждать" };
память.a = act.тип;
O.tg[idStr].a = act.тип;
if (act.тип === "идти" && act.куда) {
let di = -1;
for (let i = 0; i < 4; i++) if (DK[i] === act.куда) di = i;
if (di >= 0) {
O.tg[idStr].px = я.x + DX[di];
O.tg[idStr].py = я.y + DY[di];
}
}
if (память.tx !== undefined) {
O.tg[idStr].tx = память.tx;
O.tg[idStr].ty = память.ty;
}
if (act.тип === "положить" && наБазе && груз) O.sc = (O.sc | 0) + 1;
return { действие: act, память: память, общая: O };
}
const dHomeP = dAt(МБ.x, МБ.y);
const dHome = dHomeP < 999 ? dHomeP : манх(я.x, я.y, МБ.x, МБ.y);
if (груз && наБазе) return out({ тип: "положить" });
const здесь = vis[key(я.x, я.y)];
const едаЗдесь = здесь ? (здесь.еда | 0) : 0;
if (!груз && едаЗдесь > 0) return out({ тип: "взять" });
let битьКуда = null;
let битьПриор = -1;
let foesAdj = 0;
for (let i = 0; i < 4; i++) {
const nx = я.x + DX[i];
const ny = я.y + DY[i];
const c = vis[key(nx, ny)];
if (!c || c.что !== "враг") continue;
foesAdj++;
const eHome = манх(nx, ny, МБ.x, МБ.y);
const eFood = (c.еда | 0) > 0;
let allies = 1;
for (let j = 0; j < своиВид.length; j++) {
if (манх(своиВид[j].x, своиВид[j].y, nx, ny) === 1) allies++;
}
for (const id in O.tg) {
if (id === idStr) continue;
const t = O.tg[id];
if (манх(t.x, t.y, nx, ny) === 1) allies++;
}
if (allies > 4) allies = 4;
let pr = 0;
if (наБазе || dHome <= 1 || eHome <= 1) pr = 10;
else if (eFood) pr = 8;
else if (едаЗдесь > 0) pr = 8;
else if (allies >= 2 && allies > foesAdj) pr = 7;
else if (dHome <= 2) pr = 7;
else if (!груз && hp >= 3 && foesAdj === 1 && eHome <= 5 && allies >= 1) pr = 4;
if (груз) {
if (eHome <= 2 || наБазе) pr = 9;
else if (hp <= 2) pr = 0;
else if (pr < 8) pr = 0;
}
if (hp <= 1 && pr < 8 && !наБазе) pr = 0;
if (pr > битьПриор) {
битьПриор = pr;
битьКуда = DK[i];
}
}
if (битьКуда && битьПриор >= 8) return out({ тип: "бить", куда: битьКуда });
if (!груз) {
let adjFood = null;
let adjSc = -1e9;
for (let i = 0; i < 4; i++) {
const nx = я.x + DX[i];
const ny = я.y + DY[i];
if (!клеткаОк(nx, ny, 0)) continue;
const c = vis[key(nx, ny)];
if (!c || (c.еда | 0) <= 0) continue;
const amt = c.еда | 0;
if (claimN(nx, ny) >= amt) continue;
if (closerCount(nx, ny, 1) >= amt) continue;
const home = hAt(nx, ny);
const sc = amt * 20 - home;
if (sc > adjSc) {
adjSc = sc;
adjFood = DK[i];
память.tx = nx;
память.ty = ny;
}
}
if (adjFood) {
markClaim(память.tx, память.ty);
return out({ тип: "идти", куда: adjFood });
}
}
if (наБазе && !груз) {
const start = myRank & 3;
const order = homeTL ? [3, 1, 2, 0] : [2, 0, 3, 1];
for (let k = 0; k < 4; k++) {
const a = идти(DK[order[(start + k) & 3]], 0);
if (a) return out(a);
}
}
if (груз) {
память.tx = МБ.x;
память.ty = МБ.y;
O.tg[idStr].tx = МБ.x;
O.tg[idStr].ty = МБ.y;
if (битьКуда && битьПриор >= 7 && dHome <= 4) return out({ тип: "бить", куда: битьКуда });
if (hp <= 1 && foesAdj > 0) {
const fl = кЦели(МБ.x, МБ.y, 0);
if (fl && fl.тип === "идти") return out(fl);
}
return out(кЦели(МБ.x, МБ.y, 0));
}
if (битьКуда && битьПриор >= 7) return out({ тип: "бить", куда: битьКуда });
if (hp <= 1 && foesAdj > 0 && !наБазе) {
let bestD = null;
let bestS = -1e9;
const start = (idn + ход) & 3;
for (let k = 0; k < 4; k++) {
const i = (start + k) & 3;
const nx = я.x + DX[i];
const ny = я.y + DY[i];
if (!клеткаОк(nx, ny, 0)) continue;
let s = 0;
if (nearEnemy(nx, ny)) s -= 30;
if (враги.length) s += манх(nx, ny, враги[0].x, враги[0].y) * 8;
s -= манх(nx, ny, МБ.x, МБ.y);
if (s > bestS) {
bestS = s;
bestD = DK[i];
}
}
if (bestD) return out({ тип: "идти", куда: bestD });
return out(кЦели(МБ.x, МБ.y, 0));
}
const foods = [];
for (const k in O.f) {
const info = O.f[k];
const amt = info[0] | 0;
if (amt <= 0) continue;
const fx = (k | 0) >> 4;
const fy = (k | 0) & 15;
if (fx < 1 || fy < 1 || fx > 13 || fy > 13) continue;
if (isW(fx, fy)) continue;
const mydM = манх(я.x, я.y, fx, fy);
const already = claimN(fx, fy);
if (already >= amt) continue;
const cc = closerCount(fx, fy, mydM);
if (cc + already >= amt) continue;
const d = dAt(fx, fy);
if (d >= 999) continue;
const home = hAt(fx, fy);
const eh = манх(fx, fy, ЧБ.x, ЧБ.y);
const inferred = info[2] ? 1 : 0;
const visF = vis[key(fx, fy)];
const realVis = visF && (visF.еда | 0) > 0;
let sc = amt * 48 - d * 9 - home * 4;
if (realVis) sc += 40;
if (!inferred && home <= eh + 1) sc += 10;
if (inferred) sc += ход < 45 ? 6 : -18;
if (d <= 2) sc += 24;
else if (d <= 4) sc += 12;
else if (d <= 7) sc += 5;
if (home < eh) sc += 4;
const canDel = d + home + 1 <= осталось;
if (!canDel && осталось > 12) continue;
if (!canDel) sc -= 20;
foods.push({ x: fx, y: fy, a: amt, d: d, home: home, sc: sc, inf: inferred });
}
const ants = [];
for (const id in O.tg) {
const t = O.tg[id];
if (t.g) continue;
if ((t.t | 0) < ход - 2) continue;
ants.push({ id: id, x: t.x, y: t.y });
}
ants.sort(function (a, b) { return a.id < b.id ? -1 : a.id > b.id ? 1 : 0; });
const usedA = {};
const usedF = {};
let myFood = null;
for (let round = 0; round < ants.length; round++) {
let bestSc = -1e9;
let bi = -1;
let bj = -1;
for (let i = 0; i < ants.length; i++) {
if (usedA[ants[i].id]) continue;
for (let j = 0; j < foods.length; j++) {
const f = foods[j];
const fk = key(f.x, f.y);
if ((usedF[fk] | 0) >= f.a) continue;
const d = манх(ants[i].x, ants[i].y, f.x, f.y);
let sc = f.sc - d * 6;
if (ants[i].id === idStr) sc += (f.d <= 4 ? 8 : 0);
if (sc > bestSc) {
bestSc = sc;
bi = i;
bj = j;
}
}
}
if (bi < 0) break;
const f = foods[bj];
const fk = key(f.x, f.y);
usedA[ants[bi].id] = 1;
usedF[fk] = (usedF[fk] | 0) + 1;
if (ants[bi].id === idStr) myFood = f;
}
if (myFood) {
память.tx = myFood.x;
память.ty = myFood.y;
markClaim(myFood.x, myFood.y);
if (битьКуда && битьПриор >= 4 && myFood.d > 6) return out({ тип: "бить", куда: битьКуда });
return out(кЦели(myFood.x, myFood.y, 0));
}
if (битьКуда && битьПриор >= 4) return out({ тип: "бить", куда: битьКуда });
const late = осталось <= 16;
if (late && hp >= 2 && O.le && ход - (O.le.t | 0) <= 10) {
память.tx = O.le.x;
память.ty = O.le.y;
return out(кЦели(O.le.x, O.le.y, 1));
}
if (late && hp >= 2 && dAt(ЧБ.x, ЧБ.y) + 1 <= осталось) {
память.tx = ЧБ.x;
память.ty = ЧБ.y;
return out(кЦели(ЧБ.x, ЧБ.y, 1));
}
let seenCnt = 0;
for (let y = 0; y < N; y++) {
let v = O.n[y];
while (v) { seenCnt += v & 1; v >>>= 1; }
}
if (осталось > 10) {
const sectors = homeTL
? [[4, 4], [10, 4], [4, 10], [10, 10], [7, 3], [3, 7], [11, 7], [7, 11], [7, 7], [12, 6], [6, 12], [9, 9]]
: [[10, 10], [4, 10], [10, 4], [4, 4], [7, 11], [11, 7], [3, 7], [7, 3], [7, 7], [2, 8], [8, 2], [5, 5]];
const se = sectors[(myRank + ((ход / 12) | 0) * 2) % sectors.length];
let uBest = null;
let uBestD = 99999;
if (память.ex !== undefined && !isS(память.ex, память.ey) && !isW(память.ex, память.ey)) {
const ed = dAt(память.ex, память.ey);
if (ed < 999 && ed > 0) {
uBest = { x: память.ex, y: память.ey };
uBestD = ed * 9;
}
}
for (let y = 1; y <= 13; y++) {
for (let x = 1; x <= 13; x++) {
if (isS(x, y) || isW(x, y)) continue;
let skip = 0;
for (const id in O.tg) {
if (id === idStr) continue;
const t = O.tg[id];
if (t.g) continue;
const df = манх(t.x, t.y, x, y);
const dm = манх(я.x, я.y, x, y);
if (df < dm - 1 || (df === dm && id < idStr)) { skip = 1; break; }
if (t.tx === x && t.ty === y && id < idStr) { skip = 1; break; }
}
if (skip) continue;
const d = dAt(x, y);
if (d >= 999) continue;
const ax = x > se[0] ? x - se[0] : se[0] - x;
const ay = y > se[1] ? y - se[1] : se[1] - y;
const frontier = (isS(x + 1, y) || isS(x - 1, y) || isS(x, y + 1) || isS(x, y - 1)) ? 0 : 14;
const adj = d * 8 + ax * 4 + ay * 4 + frontier;
if (adj < uBestD) {
uBestD = adj;
uBest = { x: x, y: y };
}
}
}
if (uBest) {
память.tx = uBest.x;
память.ty = uBest.y;
память.ex = uBest.x;
память.ey = uBest.y;
return out(кЦели(uBest.x, uBest.y, 0));
}
}
if (hp >= 2 && осталось > 20 && myRank === живых - 1 && seenCnt > 70) {
const hx = homeTL ? 11 : 3;
const hy = homeTL ? 11 : 3;
память.tx = hx;
память.ty = hy;
return out(кЦели(hx, hy, 1));
}
if (stuck >= 2) {
const start = (idn + ход) & 3;
for (let k = 0; k < 4; k++) {
const i = (start + k) & 3;
const nx = я.x + DX[i];
const ny = я.y + DY[i];
if (nx < 1 || ny < 1 || nx > 13 || ny > 13) continue;
if (isW(nx, ny)) continue;
const c = vis[key(nx, ny)];
if (c && (c.что === "стена" || c.что === "враг")) continue;
if (c && c.что === "свой" && stuck < 4) continue;
return out({ тип: "идти", куда: DK[i] });
}
}
const патруль = homeTL
? [[4, 4], [7, 3], [10, 4], [3, 7], [7, 7], [11, 7], [4, 10], [7, 11], [10, 10], [6, 5], [9, 8], [5, 9], [12, 8], [8, 12]]
: [[10, 10], [7, 11], [4, 10], [11, 7], [7, 7], [3, 7], [10, 4], [7, 3], [4, 4], [8, 9], [5, 6], [9, 5], [2, 6], [6, 2]];
const pi = (idn + myRank * 3 + ((ход / 9) | 0)) % патруль.length;
const pt = патруль[pi];
if (осталось > манх(я.x, я.y, pt[0], pt[1]) + 4) {
память.tx = pt[0];
память.ty = pt[1];
return out(кЦели(pt[0], pt[1], 0));
}
if (dHome > 0 && осталось < 6) return out(кЦели(МБ.x, МБ.y, 0));
return out({ тип: "ждать" });
} catch (e) {
return { действие: { тип: "ждать" }, память: в.память || {}, общая: в.общая || {} };
}
}