код алгоритма (34.3 КБ)
function решить(в) {
try {
function num(v, d) {
return (typeof v === "number" && isFinite(v)) ? (v | 0) : d;
}
const size = num(в && в.размер, 15) || 15;
const turn = num(в && в.ход, 0);
const total = num(в && в.всего_ходов, 150);
const rem = total - turn;
function inB(x, y) {
return x >= 0 && x < size && y >= 0 && y < size;
}
const me = (в && в.я && typeof в.я === "object") ? в.я : {};
let hx = num(me.x, 0);
let hy = num(me.y, 0);
if (!inB(hx, hy)) { hx = 0; hy = 0; }
const hp = num(me.здоровье, 3);
const carry = me.груз ? 1 : 0;
const baseIn = (в && в.моя_база && typeof в.моя_база === "object") ? в.моя_база : { x: 1, y: 1 };
const enemyBaseIn = (в && в.чужая_база && typeof в.чужая_база === "object") ? в.чужая_база : { x: 13, y: 13 };
let bx = num(baseIn.x, 1), by = num(baseIn.y, 1);
let ex = num(enemyBaseIn.x, 13), ey = num(enemyBaseIn.y, 13);
if (!inB(bx, by)) { bx = 1; by = 1; }
if (!inB(ex, ey)) { ex = 13; ey = 13; }
const role = ((num(me.id, 0) % 4) + 4) % 4;
const N = size * size;
const mask = (1 << size) - 1;
const T_UNSEEN = -1;
const T_EMPTY = 0;
const T_WALL = 1;
const T_ALLY = 2;
const T_ENEMY = 3;
const T_BASE = 4;
const DIRS = [
{ d: "С", dx: 0, dy: -1 },
{ d: "В", dx: 1, dy: 0 },
{ d: "Ю", dx: 0, dy: 1 },
{ d: "З", dx: -1, dy: 0 }
];
const OPP = { "С": "Ю", "Ю": "С", "В": "З", "З": "В" };
const imul = Math.imul ? Math.imul : function (a, b) {
return ((a | 0) * (b | 0)) | 0;
};
function man(ax, ay, bx2, by2) {
return Math.abs(ax - bx2) + Math.abs(ay - by2);
}
function dirName(dx, dy) {
if (dy < 0) return "С";
if (dy > 0) return "Ю";
if (dx < 0) return "З";
return "В";
}
function clamp(v, a, b) {
return v < a ? a : (v > b ? b : v);
}
function hash2(a, b) {
let h = imul(a | 0, 374761393) ^ imul(b | 0, 668265263);
h = imul(h ^ (h >>> 13), 1274126177);
return (h ^ (h >>> 16)) >>> 0;
}
function hash4(a, b, c, d) {
let h = imul(a | 0, 374761393) ^ imul(b | 0, 668265263) ^ imul(c | 0, 2246822519) ^ imul(d | 0, 3266489917);
h = imul(h ^ (h >>> 13), 1274126177);
return (h ^ (h >>> 16)) >>> 0;
}
const memIn = (в && в.память && typeof в.память === "object") ? в.память : {};
let blk = {};
if (memIn.blk && typeof memIn.blk === "object" && !Array.isArray(memIn.blk)) {
let cnt = 0;
for (const k in memIn.blk) {
const v = memIn.blk[k] | 0;
if (v > turn) {
blk[k] = v;
cnt++;
}
}
if (cnt > 12) {
const keys = Object.keys(blk).sort((a, b) => blk[a] - blk[b]);
for (let i = 0; i < keys.length - 12; i++) delete blk[keys[i]];
}
}
const mem = {
px: num(memIn.px, -1),
py: num(memIn.py, -1),
stuck: num(memIn.stuck, 0),
hp: num(memIn.hp, 3),
tkey: num(memIn.tkey, -1),
ld: typeof memIn.ld === "string" ? memIn.ld : "",
la: typeof memIn.la === "string" ? memIn.la : "",
atkKey: num(memIn.atkKey, -1),
atkTurn: num(memIn.atkTurn, -1),
wx: num(memIn.wx, -1),
wy: num(memIn.wy, -1),
blk: blk
};
function addBlock(k, until) {
if (k < 0 || k >= N) return;
const key = String(k);
mem.blk[key] = until;
const keys = Object.keys(mem.blk);
if (keys.length > 12) {
let old = keys[0];
let oldV = mem.blk[old] | 0;
for (const kk of keys) {
const vv = mem.blk[kk] | 0;
if (vv < oldV) {
oldV = vv;
old = kk;
}
}
delete mem.blk[old];
}
}
const comIn = (в && в.общая && typeof в.общая === "object") ? в.общая : {};
const com = { w: [], s: [], f: [], e: [], c: [], a: [] };
for (let i = 0; i < size; i++) {
const wv = Array.isArray(comIn.w) ? comIn.w[i] : undefined;
const sv = Array.isArray(comIn.s) ? comIn.s[i] : undefined;
com.w[i] = (typeof wv === "number" && isFinite(wv)) ? (wv | 0) & mask : 0;
com.s[i] = (typeof sv === "number" && isFinite(sv)) ? (sv | 0) & mask : 0;
}
if (Array.isArray(comIn.f)) {
for (const it of comIn.f) {
if (Array.isArray(it) && it.length >= 3) {
const k = it[0] | 0;
const amt = it[1] | 0;
const ts = it[2] | 0;
if (k >= 0 && k < N && amt > 0) com.f.push([k, amt, ts]);
}
}
}
if (Array.isArray(comIn.e)) {
for (const it of comIn.e) {
if (Array.isArray(it) && it.length >= 2) {
const k = it[0] | 0;
const ts = it[1] | 0;
if (k >= 0 && k < N) com.e.push([k, ts]);
}
}
}
if (Array.isArray(comIn.c)) {
for (const it of comIn.c) {
if (Array.isArray(it) && it.length >= 3) {
const cid = it[0] | 0;
const ck = it[1] | 0;
const ct = it[2] | 0;
if (ck >= 0 && ck < N) com.c.push([cid, ck, ct]);
}
}
}
if (Array.isArray(comIn.a)) {
for (const it of comIn.a) {
if (Array.isArray(it) && it.length >= 6) {
const aid = it[0] | 0;
const ax = it[1] | 0;
const ay = it[2] | 0;
const ahp = it[3] | 0;
const ac = it[4] | 0;
const at = it[5] | 0;
if (inB(ax, ay)) com.a.push([aid, ax, ay, ahp, ac, at]);
}
}
}
function applyWall(x, y, val) {
if (!inB(x, y)) return;
if (val) com.w[y] = (com.w[y] | (1 << x)) & mask;
else com.w[y] = (com.w[y] & ~(1 << x)) & mask;
}
function setWallSym(x, y, val) {
applyWall(x, y, val);
const mx = size - 1 - x;
const my = size - 1 - y;
if (mx !== x || my !== y) applyWall(mx, my, val);
}
function upsertFood(k, amt, ts) {
for (let i = 0; i < com.f.length; i++) {
if (com.f[i][0] === k) {
com.f[i][1] = amt;
com.f[i][2] = ts;
return;
}
}
com.f.push([k, amt, ts]);
}
function removeFood(k) {
for (let i = com.f.length - 1; i >= 0; i--) {
if (com.f[i][0] === k) com.f.splice(i, 1);
}
}
function upsertEnemy(k, ts) {
for (let i = 0; i < com.e.length; i++) {
if (com.e[i][0] === k) {
com.e[i][1] = ts;
return;
}
}
com.e.push([k, ts]);
}
const tArr = new Array(N).fill(T_UNSEEN);
const fArr = new Array(N).fill(0);
const localFood = [];
const visibleEnemies = [];
const visibleAllies = [];
const vis = (в && Array.isArray(в.видно)) ? в.видно : [];
for (const c of vis) {
if (!c || typeof c !== "object") continue;
const x = num(c.x, -1);
const y = num(c.y, -1);
if (!inB(x, y)) continue;
const k = y * size + x;
const isWall = c.что === "стена";
setWallSym(x, y, isWall);
com.s[y] = (com.s[y] | (1 << x)) & mask;
let t = T_EMPTY;
if (isWall) t = T_WALL;
else if (c.что === "свой") t = T_ALLY;
else if (c.что === "враг") t = T_ENEMY;
else if (c.что === "база") t = T_BASE;
const food = isWall ? 0 : num(c.еда, 0);
tArr[k] = t;
fArr[k] = food;
if (food > 0) {
upsertFood(k, food, turn);
localFood.push({ k: k, amt: food, x: x, y: y });
} else {
removeFood(k);
}
if (t === T_ENEMY) {
visibleEnemies.push({ x: x, y: y, k: k });
upsertEnemy(k, turn);
} else if (t === T_ALLY && !(x === hx && y === hy)) {
visibleAllies.push({ x: x, y: y, k: k });
}
}
const selfK = hy * size + hx;
if (tArr[selfK] === T_UNSEEN) tArr[selfK] = T_ALLY;
com.a.push([role, hx, hy, hp, carry, turn]);
function knownWall(x, y) {
if (!inB(x, y)) return true;
if ((x === bx && y === by) || (x === ex && y === ey)) return false;
if (x === 0 || y === 0 || x === size - 1 || y === size - 1) return true;
const k = y * size + x;
if (tArr[k] !== T_UNSEEN) return tArr[k] === T_WALL;
return (((com.w[y] || 0) >>> x) & 1) !== 0;
}
function occupiedVisible(x, y) {
if (!inB(x, y)) return true;
if (x === hx && y === hy) return false;
const t = tArr[y * size + x];
return t === T_ALLY || t === T_ENEMY;
}
(function sanitize() {
const fmap = Object.create(null);
for (const it of com.f) {
if (!Array.isArray(it) || it.length < 3) continue;
const k = it[0] | 0;
const amt = it[1] | 0;
const ts = it[2] | 0;
if (k < 0 || k >= N || amt <= 0) continue;
if (turn - ts > 50) continue;
const x = k % size;
const y = (k / size) | 0;
if (!inB(x, y) || knownWall(x, y)) continue;
const prev = fmap[k];
if (!prev || ts > prev[2] || (ts === prev[2] && amt > prev[1])) fmap[k] = [k, amt, ts];
}
let fl = [];
for (const key in fmap) fl.push(fmap[key]);
fl.sort((a, b) => b[2] - a[2] || b[1] - a[1]);
if (fl.length > 40) fl.length = 40;
com.f = fl;
const emap = Object.create(null);
for (const it of com.e) {
if (!Array.isArray(it) || it.length < 2) continue;
const k = it[0] | 0;
const ts = it[1] | 0;
if (k < 0 || k >= N) continue;
if (turn - ts > 8) continue;
const prev = emap[k];
if (!prev || ts > prev[1]) emap[k] = [k, ts];
}
let el = [];
for (const key in emap) el.push(emap[key]);
el.sort((a, b) => b[1] - a[1]);
if (el.length > 12) el.length = 12;
com.e = el;
const cmap = Object.create(null);
for (const it of com.c) {
if (!Array.isArray(it) || it.length < 3) continue;
const cid = it[0] | 0;
const ck = it[1] | 0;
const ct = it[2] | 0;
if (ck < 0 || ck >= N) continue;
if (turn - ct > 6) continue;
const x = ck % size;
const y = (ck / size) | 0;
if (knownWall(x, y)) continue;
const prev = cmap[cid];
if (!prev || ct > prev[2]) cmap[cid] = [cid, ck, ct];
}
let cl = [];
for (const key in cmap) cl.push(cmap[key]);
if (cl.length > 8) cl.length = 8;
com.c = cl;
const amap = Object.create(null);
for (const it of com.a) {
if (!Array.isArray(it) || it.length < 6) continue;
const aid = it[0] | 0;
const ax = it[1] | 0;
const ay = it[2] | 0;
const ahp = it[3] | 0;
const ac = it[4] | 0;
const at = it[5] | 0;
if (!inB(ax, ay)) continue;
if (turn - at > 3) continue;
const prev = amap[aid];
if (!prev || at > prev[5]) amap[aid] = [aid, ax, ay, ahp, ac, at];
}
let al = [];
for (const key in amap) al.push(amap[key]);
al.sort((a, b) => a[0] - b[0]);
if (al.length > 4) al.length = 4;
com.a = al;
})();
if (mem.tkey >= 0 && mem.tkey < N) {
const tx = mem.tkey % size;
const ty = (mem.tkey / size) | 0;
if (knownWall(tx, ty) || (tArr[mem.tkey] !== T_UNSEEN && fArr[mem.tkey] <= 0)) {
mem.tkey = -1;
}
}
const samePos = mem.px === hx && mem.py === hy;
mem.stuck = (samePos && mem.la === "идти") ? (mem.stuck + 1) : 0;
const wasDamaged = mem.hp > hp;
mem.hp = hp;
if (mem.stuck >= 2 && mem.ld) {
let dx = 0, dy = 0;
if (mem.ld === "С") dy = -1;
else if (mem.ld === "Ю") dy = 1;
else if (mem.ld === "З") dx = -1;
else if (mem.ld === "В") dx = 1;
const nx = hx + dx, ny = hy + dy;
if (inB(nx, ny)) addBlock(ny * size + nx, turn + 6);
}
if (mem.stuck >= 5) {
if (mem.tkey >= 0) addBlock(mem.tkey, turn + 18);
else if (mem.wx >= 0 && mem.wy >= 0) addBlock(mem.wy * size + mem.wx, turn + 12);
mem.stuck = 0;
}
const adjEnemies = [];
for (const e of visibleEnemies) {
if (man(hx, hy, e.x, e.y) === 1) {
adjEnemies.push({ x: e.x, y: e.y, k: e.k, dir: dirName(e.x - hx, e.y - hy) });
}
}
function threatAt(x, y) {
let s = 0;
for (const e of visibleEnemies) {
const d = man(x, y, e.x, e.y);
if (d <= 1) s += 40;
else if (d <= 2) s += 12;
else if (d <= 3) s += 3;
}
for (const it of com.e) {
if (turn - it[1] <= 2) {
const px = it[0] % size;
const py = (it[0] / size) | 0;
const d = man(x, y, px, py);
if (d <= 1) s += 15;
else if (d <= 2) s += 5;
}
}
if (wasDamaged && x === hx && y === hy) s += 10;
return s;
}
function bfs(sx, sy, blockOcc) {
const dist = new Array(N).fill(-1);
if (!inB(sx, sy)) return dist;
const sk = sy * size + sx;
if (knownWall(sx, sy) && sk !== selfK) return dist;
const q = new Array(N);
let head = 0, tail = 0;
dist[sk] = 0;
q[tail++] = sk;
while (head < tail) {
const k = q[head++];
const x = k % size;
const y = (k / size) | 0;
const nd = dist[k] + 1;
for (let i = 0; i < 4; i++) {
const nx = x + DIRS[i].dx;
const ny = y + DIRS[i].dy;
if (!inB(nx, ny)) continue;
const nk = ny * size + nx;
if (dist[nk] !== -1) continue;
if (knownWall(nx, ny)) continue;
if (blockOcc && occupiedVisible(nx, ny)) continue;
dist[nk] = nd;
q[tail++] = nk;
}
}
return dist;
}
function chooseStep(tx, ty, opts) {
opts = opts || {};
if (!inB(tx, ty)) return null;
if (tx === hx && ty === hy) return null;
if (knownWall(tx, ty)) return null;
const dist = bfs(tx, ty, false);
const myD = dist[selfK];
const curMan = man(hx, hy, tx, ty);
const curThreat = threatAt(hx, hy);
let best = null;
let bestScore = 1e9;
let bestProgress = false;
for (const o of DIRS) {
const nx = hx + o.dx;
const ny = hy + o.dy;
if (!inB(nx, ny)) continue;
const nk = ny * size + nx;
if (knownWall(nx, ny)) continue;
if (occupiedVisible(nx, ny)) continue;
const dGoal = dist[nk] >= 0 ? dist[nk] : man(nx, ny, tx, ty) + 20;
const progress =
(myD >= 0 && dist[nk] >= 0 && dist[nk] < myD) ||
(myD < 0 && man(nx, ny, tx, ty) < curMan);
let score = dGoal * 10;
if (progress) score -= 70;
else score += 45;
const th = threatAt(nx, ny);
score += th * ((opts.carry || opts.flee || hp <= 1) ? 3 : 1);
if (curThreat > 0 && th < curThreat) score -= 18;
if ((opts.carry || hp <= 1) && th >= 40) score += 90;
if (nx === bx && ny === by) {
if (opts.carry && tx === bx && ty === by) score -= 35;
else if (!opts.carry && !(tx === bx && ty === by)) score += 130;
else score += 15;
}
if (nx === ex && ny === ey) score += 45;
if (!opts.carry && fArr[nk] > 0) score -= 10;
if (mem.blk[String(nk)] > turn) score += 150;
if (mem.stuck > 1 && mem.ld && o.d === OPP[mem.ld]) score += 30;
score += hash4(nx, ny, role, turn) & 7;
if (score < bestScore) {
bestScore = score;
best = o;
bestProgress = progress;
}
}
if (!best) return null;
if (!bestProgress && !(opts.urgent || opts.carry || opts.flee || curThreat > 10 || mem.stuck >= 1)) {
return null;
}
return { "тип": "идти", "куда": best.d };
}
function chooseEscape(forCarry) {
const cx = size >> 1;
const cy = size >> 1;
const curScore = threatAt(hx, hy) * 4 +
(forCarry ? man(hx, hy, bx, by) * 6 : (hp <= 1 ? man(hx, hy, bx, by) * 4 : man(hx, hy, cx, cy) * 2));
let best = null;
let bestScore = 1e9;
for (const o of DIRS) {
const nx = hx + o.dx;
const ny = hy + o.dy;
if (!inB(nx, ny)) continue;
const nk = ny * size + nx;
if (knownWall(nx, ny)) continue;
if (occupiedVisible(nx, ny)) continue;
const th = threatAt(nx, ny);
let score = th * 4;
if (forCarry) score += man(nx, ny, bx, by) * 6;
else if (hp <= 1) score += man(nx, ny, bx, by) * 4;
else score += man(nx, ny, cx, cy) * 2;
if (!forCarry && fArr[nk] > 0) score -= 12;
if (nx === bx && ny === by) {
if (forCarry) score -= 20;
else if (hp <= 1) score -= 10;
else score += 50;
}
if (nx === ex && ny === ey) score += 50;
if (mem.blk[String(nk)] > turn) score += 130;
score += hash4(nx, ny, role, turn) & 7;
if (score < bestScore) {
bestScore = score;
best = o;
}
}
if (best && bestScore < curScore) return { "тип": "идти", "куда": best.d };
return null;
}
function chooseAttack() {
if (adjEnemies.length === 0) return null;
let best = null;
let bestScore = 1e9;
for (const e of adjEnemies) {
let score = 0;
const dBase = man(e.x, e.y, bx, by);
if (e.x === bx && e.y === by) score -= 180;
if (dBase <= 2) score -= 80;
if ((fArr[e.k] || 0) > 0) score -= 110;
if (dBase < man(e.x, e.y, ex, ey)) score -= 40;
let support = 0;
for (const a of visibleAllies) {
if (man(a.x, a.y, e.x, e.y) <= 1) support++;
}
if (support > 0) score -= 35 * support;
if (hp === 3) score -= 20;
else if (hp === 2) score += 5;
else score += 220;
if (carry) score += 140;
if (adjEnemies.length >= 2) score += 100;
if (mem.atkKey === e.k && mem.atkTurn === turn - 1) score -= 30;
if (rem <= 12 && !(e.x === bx && e.y === by)) score += 50;
score += dBase * 2;
score += hash4(e.x, e.y, role, turn) & 7;
if (score < bestScore) {
bestScore = score;
best = e;
}
}
if (!best) return null;
return { act: { "тип": "бить", "куда": best.dir }, key: best.k, score: bestScore };
}
function doTake() {
const amt = fArr[selfK] > 0 ? fArr[selfK] : 0;
if (amt <= 1) {
removeFood(selfK);
if (mem.tkey === selfK) mem.tkey = -1;
} else {
upsertFood(selfK, amt - 1, turn);
}
mem.stuck = 0;
return { "тип": "взять" };
}
function chooseFoodTarget(tieMode) {
const cand = [];
const used = Object.create(null);
function add(k, amt, ts) {
if (amt <= 0 || used[k]) return;
const x = k % size;
const y = (k / size) | 0;
if (!inB(x, y)) return;
if (knownWall(x, y)) return;
if (tArr[k] === T_WALL) return;
used[k] = 1;
cand.push({ k: k, x: x, y: y, amt: amt, ts: ts });
}
for (const it of localFood) add(it.k, it.amt, turn);
for (const it of com.f) add(it[0] | 0, it[1] | 0, it[2] | 0);
if (cand.length === 0) return null;
const distSelf = bfs(hx, hy, false);
const distBase = tieMode ? null : bfs(bx, by, false);
let best = null;
let bestScore = 1e9;
for (const c of cand) {
const dS = distSelf[c.k] >= 0 ? distSelf[c.k] : man(hx, hy, c.x, c.y) + 20;
let dB = 0;
if (!tieMode) {
dB = distBase ? (distBase[c.k] >= 0 ? distBase[c.k] : man(c.x, c.y, bx, by) + 20) : man(c.x, c.y, bx, by) + 20;
const canDeliver = (dS + dB + 2 <= rem) || (c.k === selfK && dB + 2 <= rem);
if (rem <= 45 && !canDeliver) continue;
} else {
if (dS > rem) continue;
}
let score;
if (tieMode) {
score = dS * 12 - Math.min(c.amt, 4) * 4;
} else {
score = dS * 9 + dB * 13 - Math.min(c.amt, 4) * 7;
}
const age = Math.max(0, turn - c.ts);
score += tieMode ? Math.min(age, 15) : Math.min(age, 30);
const danger = threatAt(c.x, c.y);
score += danger * (hp <= 1 ? 3 : (tieMode ? 2 : 1));
let claims = 0;
for (const cl of com.c) {
if (cl[0] !== role && cl[1] === c.k && turn - cl[2] <= 6) claims++;
}
if (claims > 0) {
if (c.amt <= claims) score += 170;
else score += claims * 30;
}
for (const a of visibleAllies) {
const ad = man(a.x, a.y, c.x, c.y);
const md = man(hx, hy, c.x, c.y);
if (ad < md) score += 16;
else if (ad <= 1) score += 6;
}
for (const a of com.a) {
if (a[0] !== role && turn - a[5] <= 2 && a[4] === 0) {
const ad = man(a[1], a[2], c.x, c.y);
const md = man(hx, hy, c.x, c.y);
if (ad < md) score += 14;
else if (ad === md && a[0] < role) score += 5;
}
}
const slots = Math.min(c.amt, 4);
let owned = 0;
for (let s = 0; s < slots; s++) {
if ((hash2(c.k, s * 7 + 13) & 3) === role) owned++;
}
if (owned > 0) score -= owned * 20;
else score += 14;
if (c.k === mem.tkey) score -= 20;
if (mem.blk[String(c.k)] > turn) score += 150;
if (c.k === selfK) score -= 30;
if (tArr[c.k] === T_ALLY) score += 50;
if (tArr[c.k] === T_ENEMY) score += 40;
if (c.x === ex && c.y === ey) score += 30;
if (!tieMode && rem <= 15 && dS > 1) score += 70;
score += hash4(c.k, role, turn, rem) & 7;
if (score < bestScore) {
bestScore = score;
best = c;
}
}
return best;
}
function chooseExploreTarget() {
const pts = [];
for (let y = 2; y <= size - 3; y += 2) {
for (let x = 2; x <= size - 3; x += 2) {
if (!knownWall(x, y) && !(x === bx && y === by) && !(x === ex && y === ey)) {
pts.push({ x: x, y: y });
}
}
}
const cx = size >> 1;
const cy = size >> 1;
if (!knownWall(cx, cy) && !(cx === bx && cy === by) && !(cx === ex && cy === ey)) {
pts.push({ x: cx, y: cy });
}
if (pts.length === 0) return { x: hx, y: hy };
const sx = ex > bx ? 1 : -1;
const sy = ey > by ? 1 : -1;
const anchors = [
{ x: clamp(bx + 4 * sx, 2, size - 3), y: clamp(by + 2 * sy, 2, size - 3) },
{ x: clamp(bx + 2 * sx, 2, size - 3), y: clamp(by + 4 * sy, 2, size - 3) },
{ x: clamp(bx + 6 * sx, 2, size - 3), y: clamp(by + 6 * sy, 2, size - 3) },
{ x: clamp(bx + 8 * sx, 2, size - 3), y: clamp(by + 8 * sy, 2, size - 3) }
];
const anchor = anchors[role];
let best = null, bestScore = 1e9;
let second = null, secondScore = 1e9;
for (const p of pts) {
const seen = ((com.s[p.y] || 0) >>> p.x) & 1;
let score = man(hx, hy, p.x, p.y) * 4 + (seen ? 28 : -35) + man(p.x, p.y, anchor.x, anchor.y) * 3;
if (mem.stuck < 2 && mem.wx === p.x && mem.wy === p.y) score -= 35;
if (p.x === ex && p.y === ey) score += 30;
if (p.x === bx && p.y === by) score += 100;
score += hash4(p.x, p.y, role, turn) & 15;
if (score < bestScore) {
second = best;
secondScore = bestScore;
best = p;
bestScore = score;
} else if (score < secondScore) {
second = p;
secondScore = score;
}
}
if (best && best.x === hx && best.y === hy && second) best = second;
if (!best) return { x: hx, y: hy };
mem.wx = best.x;
mem.wy = best.y;
return best;
}
function fallbackExplore() {
const cx = size >> 1;
const cy = size >> 1;
let best = null;
let bestScore = 1e9;
for (const o of DIRS) {
const nx = hx + o.dx;
const ny = hy + o.dy;
if (!inB(nx, ny)) continue;
const nk = ny * size + nx;
if (knownWall(nx, ny)) continue;
if (occupiedVisible(nx, ny)) continue;
const seen = ((com.s[ny] || 0) >>> nx) & 1;
let score = threatAt(nx, ny) * 2 + man(nx, ny, cx, cy) * 3 + (seen ? 12 : -18);
if (nx === ex && ny === ey) score += 35;
if (mem.blk[String(nk)] > turn) score += 120;
score += hash4(nx, ny, role, turn) & 15;
if (score < bestScore) {
bestScore = score;
best = o;
}
}
if (!best) return null;
return { "тип": "идти", "куда": best.d };
}
function chooseLeaveBase() {
let best = null;
let bestScore = 1e9;
const cx = size >> 1;
const cy = size >> 1;
for (const o of DIRS) {
const nx = hx + o.dx;
const ny = hy + o.dy;
if (!inB(nx, ny)) continue;
const nk = ny * size + nx;
if (knownWall(nx, ny)) continue;
if (occupiedVisible(nx, ny)) continue;
let score = threatAt(nx, ny) * 2;
score -= man(nx, ny, bx, by) * 8;
score += man(nx, ny, cx, cy) * 2;
if (fArr[nk] > 0) score -= 25;
if (nx === ex && ny === ey) score += 40;
if (mem.blk[String(nk)] > turn) score += 120;
score += hash4(nx, ny, role, turn) & 7;
if (score < bestScore) {
bestScore = score;
best = o;
}
}
if (!best) return null;
return { "тип": "идти", "куда": best.d };
}
function ret(act) {
if (!act || typeof act !== "object") act = { "тип": "ждать" };
mem.px = hx;
mem.py = hy;
if (act.тип === "идти") {
mem.ld = typeof act.куда === "string" ? act.куда : "";
mem.la = "идти";
} else {
mem.ld = "";
mem.la = typeof act.тип === "string" ? act.тип : "ждать";
}
if (act.тип === "взять" || act.тип === "положить" || act.тип === "бить") {
mem.stuck = 0;
}
if (mem.atkTurn < turn - 1) {
mem.atkKey = -1;
mem.atkTurn = -1;
}
let willCarry = carry;
if (act.тип === "взять") willCarry = 1;
if (act.тип === "положить") willCarry = 0;
const cc = [];
const seen = Object.create(null);
for (const cl of com.c) {
if (!Array.isArray(cl) || cl.length < 3) continue;
const cid = cl[0] | 0;
const ck = cl[1] | 0;
const ct = cl[2] | 0;
if (turn - ct > 6) continue;
if (ck < 0 || ck >= N) continue;
if (cid === role) continue;
if (seen[cid]) continue;
const x = ck % size;
const y = (ck / size) | 0;
if (knownWall(x, y)) continue;
seen[cid] = 1;
cc.push([cid, ck, ct]);
}
if (!willCarry && mem.tkey >= 0 && mem.tkey < N) {
const x = mem.tkey % size;
const y = (mem.tkey / size) | 0;
if (!knownWall(x, y)) cc.push([role, mem.tkey, turn]);
}
com.c = cc.slice(-8);
return { "действие": act, "память": mem, "общая": com };
}
const onBase = hx === bx && hy === by;
const hereFood = fArr[selfK] > 0 ? fArr[selfK] : 0;
if (carry && onBase) {
mem.tkey = -1;
return ret({ "тип": "положить" });
}
if (rem <= 0) {
if (!carry && hereFood > 0) return ret(doTake());
return ret({ "тип": "ждать" });
}
if (onBase && !carry && adjEnemies.length === 0) {
if (hereFood > 0) return ret(doTake());
const lv = chooseLeaveBase();
if (lv) return ret(lv);
}
if (adjEnemies.length > 0) {
const many = adjEnemies.length >= 2;
const enemyOnBase = adjEnemies.some(function (e) { return e.x === bx && e.y === by; });
const enemyOnFood = adjEnemies.some(function (e) { return (fArr[e.k] || 0) > 0; });
const enemyNearBase = adjEnemies.some(function (e) { return man(e.x, e.y, bx, by) <= 2; });
const enemyInOurHalf = adjEnemies.some(function (e) {
return man(e.x, e.y, bx, by) < man(e.x, e.y, ex, ey);
});
const lastTarget = mem.atkTurn === turn - 1 && adjEnemies.some(function (e) { return e.k === mem.atkKey; });
const support = visibleAllies.filter(function (a) { return man(a.x, a.y, hx, hy) <= 1; }).length;
if (carry) {
if (enemyOnBase && hp >= 2 && (!many || support > 0 || hp === 3)) {
const at = chooseAttack();
if (at) {
mem.atkKey = at.key;
mem.atkTurn = turn;
return ret(at.act);
}
}
if (man(hx, hy, bx, by) === 1 && !occupiedVisible(bx, by)) {
return ret({ "тип": "идти", "куда": dirName(bx - hx, by - hy) });
}
if (man(hx, hy, bx, by) === 1 && occupiedVisible(bx, by)) {
return ret({ "тип": "ждать" });
}
const esc = chooseEscape(true);
if (esc && (hp <= 2 || many || wasDamaged || threatAt(hx, hy) >= 40 || !enemyOnBase)) {
return ret(esc);
}
if (hp >= 2 && !many && (enemyOnBase || enemyOnFood || enemyNearBase || lastTarget)) {
const at = chooseAttack();
if (at) {
mem.atkKey = at.key;
mem.atkTurn = turn;
return ret(at.act);
}
}
if (hp === 3 && !many && enemyInOurHalf) {
const at = chooseAttack();
if (at) {
mem.atkKey = at.key;
mem.atkTurn = turn;
return ret(at.act);
}
}
const mv = chooseStep(bx, by, { carry: true, urgent: true });
if (mv) return ret(mv);
if (hp >= 2 && !many) {
const at = chooseAttack();
if (at) {
mem.atkKey = at.key;
mem.atkTurn = turn;
return ret(at.act);
}
}
if (hp === 1 && !many && enemyOnBase) {
const at = chooseAttack();
if (at) {
mem.atkKey = at.key;
mem.atkTurn = turn;
return ret(at.act);
}
}
return ret({ "тип": "ждать" });
} else {
if (hp <= 1) {
const esc = chooseEscape(false);
if (esc) return ret(esc);
if (enemyOnBase && rem > 20) {
const at = chooseAttack();
if (at && at.score < 160) {
mem.atkKey = at.key;
mem.atkTurn = turn;
return ret(at.act);
}
}
if (hereFood > 0 && rem <= 8 && !many) return ret(doTake());
return ret({ "тип": "ждать" });
}
const wantFight =
enemyOnBase ||
enemyOnFood ||
enemyNearBase ||
lastTarget ||
(hp === 3 && !many && enemyInOurHalf) ||
(support > 0 && !many && enemyInOurHalf);
if (wantFight) {
const at = chooseAttack();
if (at && (at.score < 150 || enemyOnBase || enemyOnFood)) {
mem.atkKey = at.key;
mem.atkTurn = turn;
return ret(at.act);
}
}
if (hereFood > 0 && rem <= 10 && !many && hp >= 2) return ret(doTake());
const goal = (hp <= 2 || wasDamaged || enemyNearBase) ? { x: bx, y: by } : chooseExploreTarget();
const mv = chooseStep(goal.x, goal.y, { flee: true, urgent: true, carry: false });
if (mv) return ret(mv);
if (hp >= 2 && !many) {
const at = chooseAttack();
if (at && at.score < 170) {
mem.atkKey = at.key;
mem.atkTurn = turn;
return ret(at.act);
}
}
if (hereFood > 0 && hp >= 2) return ret(doTake());
return ret({ "тип": "ждать" });
}
}
if (!carry && hereFood > 0) {
return ret(doTake());
}
if (carry) {
mem.tkey = -1;
if (man(hx, hy, bx, by) === 1 && occupiedVisible(bx, by)) {
return ret({ "тип": "ждать" });
}
if (man(hx, hy, bx, by) === 1 && !occupiedVisible(bx, by) && (rem > 10 || 2 <= rem)) {
return ret({ "тип": "идти", "куда": dirName(bx - hx, by - hy) });
}
const distBase = bfs(bx, by, false);
let dBase = distBase[selfK];
if (dBase < 0) dBase = man(hx, hy, bx, by) + 20;
if (rem <= 25) {
if (dBase >= 0 && dBase + 1 <= rem) {
const mv = chooseStep(bx, by, { carry: true, urgent: true });
if (mv) return ret(mv);
}
if (threatAt(hx, hy) > 5) {
const esc = chooseEscape(true);
if (esc) return ret(esc);
}
return ret({ "тип": "ждать" });
}
const mv = chooseStep(bx, by, { carry: true, urgent: true });
if (mv) return ret(mv);
return ret({ "тип": "ждать" });
}
if (!carry && hp >= 2 && rem > 15 && visibleEnemies.length > 0) {
let nearBase = false;
for (const e of visibleEnemies) {
if (man(e.x, e.y, bx, by) <= 3) {
nearBase = true;
break;
}
}
if (nearBase && man(hx, hy, bx, by) <= 5) {
const mv = chooseStep(bx, by, { urgent: true, carry: false });
if (mv) return ret(mv);
}
}
if (!carry) {
let fg = chooseFoodTarget(false);
if (!fg && rem <= 35) fg = chooseFoodTarget(true);
if (fg) {
mem.tkey = fg.k;
if (fg.k === selfK) {
if (hereFood > 0) return ret(doTake());
mem.tkey = -1;
addBlock(fg.k, turn + 8);
} else {
let mv = chooseStep(fg.x, fg.y, { carry: false, urgent: rem <= 25 });
if (!mv) mv = chooseStep(fg.x, fg.y, { carry: false, urgent: true });
if (mv) return ret(mv);
if (mem.stuck >= 2 || (tArr[fg.k] !== T_UNSEEN && occupiedVisible(fg.x, fg.y))) {
addBlock(fg.k, turn + 10);
mem.tkey = -1;
} else {
return ret({ "тип": "ждать" });
}
}
} else {
mem.tkey = -1;
}
if (rem <= 10) {
let bestF = null;
let bd = 999;
for (const it of localFood) {
if (knownWall(it.x, it.y)) continue;
const d = man(hx, hy, it.x, it.y);
if (d <= rem && d < bd) {
bd = d;
bestF = it;
}
}
if (bestF) {
if (bestF.k === selfK) {
if (hereFood > 0) return ret(doTake());
} else {
const mv = chooseStep(bestF.x, bestF.y, { carry: false, urgent: true });
if (mv) return ret(mv);
}
}
}
const wp = chooseExploreTarget();
if (wp.x !== hx || wp.y !== hy) {
const mv = chooseStep(wp.x, wp.y, { carry: false, urgent: false });
if (mv) return ret(mv);
}
const fb = fallbackExplore();
if (fb) return ret(fb);
if (hereFood > 0) return ret(doTake());
return ret({ "тип": "ждать" });
}
return ret({ "тип": "ждать" });
} catch (e) {
return {
"действие": { "тип": "ждать" },
"память": (в && в.память) ? в.память : {},
"общая": (в && в.общая) ? в.общая : {}
};
}
}