Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save tylearymf/8c112d1de01c5750f6c086c5ff679e59 to your computer and use it in GitHub Desktop.

Select an option

Save tylearymf/8c112d1de01c5750f6c086c5ff679e59 to your computer and use it in GitHub Desktop.
agentank-squid-tank
var F_MEM = typeof F_MEM === "undefined" ? { self: [], enemy: [], bullets: [] } : F_MEM;
var F_DIRS = ["up", "right", "down", "left"];
var F_VEC = { up: [0, -1], right: [1, 0], down: [0, 1], left: [-1, 0] };
function onIdle(me, enemy, game) {
var w = F_world(me, enemy, game);
F_remember(w);
var lineEscape = F_lineShotEscapeAction(w);
if (lineEscape) {
F_act(me, lineEscape, w);
return;
}
var starTrap = F_starLineTrapAction(w);
if (starTrap) {
F_act(me, starTrap, w);
return;
}
var pocketBreak = F_leftPocketBreakAction(w);
if (pocketBreak) {
F_act(me, pocketBreak, w);
return;
}
var map15Return = F_map15ReturnAction(w);
if (map15Return) {
F_act(me, map15Return, w);
return;
}
var stuckEscape = F_stuckFaceEscapeAction(w);
if (stuckEscape) {
F_act(me, stuckEscape, w);
return;
}
var tailEscape = F_tailEscapeAction(w);
if (tailEscape) {
F_act(me, tailEscape, w);
return;
}
var cornerKill = F_cornerKillAction(w);
if (cornerKill) {
F_act(me, cornerKill, w);
return;
}
var escape = F_threeStarEscape(w);
if (escape) {
F_act(me, escape, w);
return;
}
var forecast = F_forecast(w, 7);
var plans = F_plans(w, forecast);
var best = F_pick(w, forecast, plans);
F_setTailEscape(w, best.actions[0]);
F_act(me, best.actions[0], w);
}
function F_world(me, enemy, game) {
return {
map: game.map,
w: game.map ? game.map.length : 0,
h: game.map && game.map[0] ? game.map[0].length : 0,
frame: game.frames || 0,
star: game.star ? F_cp(game.star) : null,
me: {
p: F_cp(me.tank.position),
d: me.tank.direction,
stars: me.stars || 0,
bullet: F_bullet(me.bullet),
skill: me.skill || {},
status: me.status || {},
},
enemy: {
tank: enemy && enemy.tank ? { p: F_cp(enemy.tank.position), d: enemy.tank.direction } : null,
bullet: enemy ? F_bullet(enemy.bullet) : null,
skill: enemy && enemy.skill ? enemy.skill : {},
status: enemy && enemy.status ? enemy.status : {},
},
cache: {},
};
}
function F_remember(w) {
F_push(F_MEM.self, { f: w.frame, p: F_cp(w.me.p), d: w.me.d }, 10);
if (w.enemy.tank) F_push(F_MEM.enemy, { f: w.frame, p: F_cp(w.enemy.tank.p), d: w.enemy.tank.d }, 10);
F_trackBullets(w);
}
function F_trackBullets(w) {
if (F_MEM.bulletFrame != null && w.frame < F_MEM.bulletFrame) F_MEM.bullets = [];
if (w.enemy.bullet && w.enemy.bullet.d && F_VEC[w.enemy.bullet.d]) {
var b = { p: F_cp(w.enemy.bullet.p), d: w.enemy.bullet.d, f: w.frame };
F_MEM.bullets = [b];
var sk = w.enemy.skill || {}, cd = Number(sk.cooldownFrames) || 0, rem = Number(sk.remainingCooldownFrames) || 0;
var inferOver = sk.type === "overload" && cd > 0 && cd - rem <= 4;
if (!inferOver && !w.star && w.enemy.tank) inferOver = (b.d === "up" || b.d === "down") ? Math.abs(w.enemy.tank.p[0] - b.p[0]) === 1 : Math.abs(w.enemy.tank.p[1] - b.p[1]) === 1;
if (inferOver) {
var p = F_cp(b.p);
if (b.d === "up" || b.d === "down") p[0] += w.enemy.tank && w.enemy.tank.p[0] !== p[0] ? -1 : 1;
else p[1] += w.enemy.tank && w.enemy.tank.p[1] !== p[1] ? -1 : 1;
if (F_inside(w, p)) F_MEM.bullets.push({ p: p, d: b.d, f: w.frame });
}
F_MEM.bulletFrame = w.frame;
return;
}
var next = [];
for (var i = 0; i < (F_MEM.bullets || []).length; i++) {
var b = F_advanceBullet(w, F_MEM.bullets[i], w.frame);
if (b) next.push(b);
}
F_MEM.bullets = next;
F_MEM.bulletFrame = w.frame;
}
function F_advanceBullet(w, b, frame) {
if (!b || !b.p || !F_VEC[b.d]) return null;
var p = F_cp(b.p), v = F_VEC[b.d], frames = Math.max(0, frame - (b.f || frame));
for (var t = 0; t < frames; t++) {
for (var s = 0; s < 2; s++) {
p = F_add(p, v);
if (!F_inside(w, p) || F_blocksBullet(w, p)) return null;
}
}
return { p: p, d: b.d, f: frame };
}
function F_push(a, v, n) {
a.push(v);
while (a.length > n) a.shift();
}
function F_bullet(b) {
return b ? { p: F_cp(b.position), d: b.direction } : null;
}
function F_plans(w, forecast) {
var atoms = F_atoms(w, forecast);
var depth = 2;
var out = [{ name: "wait", actions: [] }];
F_extend(out, [], atoms, depth);
F_addRoutePlans(w, out);
return out;
}
function F_atoms(w, forecast) {
var a = [{ type: "go" }, { type: "turn", side: "left" }, { type: "turn", side: "right" }, { type: "wait" }];
if (!w.me.bullet && !w.me.status.fireLocked) a.push({ type: "fire" });
var sk = w.me.skill || {};
if (sk.type && sk.type !== "none" && sk.remainingCooldownFrames === 0) {
if (sk.type === "shield") a.push({ type: "skill", skill: "shield" });
else if (sk.type === "boost") a.push({ type: "skill", skill: "boost" });
else if (sk.type === "overload") a.push({ type: "skill", skill: "overload" });
else if (w.enemy.tank) a.push({ type: "skill", skill: sk.type });
}
if (sk.type === "teleport" && sk.remainingCooldownFrames === 0) {
var ts = F_teleports(w);
for (var i = 0; i < ts.length; i++) a.push({ type: "teleport", x: ts[i][0], y: ts[i][1] });
}
return a;
}
function F_extend(out, prefix, atoms, depth) {
if (prefix.length >= depth) return;
for (var i = 0; i < atoms.length; i++) {
var next = prefix.concat([atoms[i]]);
out.push({ name: "", actions: next });
if (atoms[i].type !== "fire" && atoms[i].type !== "teleport") F_extend(out, next, atoms, depth);
}
}
function F_addRoutePlans(w, out) {
if (!w.star) return;
var route = F_routeStep(w, w.me.p, w.star);
if (!route) return;
if (route.type === "go") out.push({ name: "route_go_go", actions: [{ type: "go" }, { type: "go" }] });
else out.push({ name: "route_turn_go", actions: [route, { type: "go" }] });
}
function F_pick(w, forecast, plans) {
var best = plans[0], bestScore = -1e15;
for (var i = 0; i < plans.length; i++) {
var score = F_score(w, forecast, plans[i]);
if (score > bestScore) {
best = plans[i];
bestScore = score;
}
}
return best;
}
function F_score(w, forecast, plan) {
var sim = F_sim(w, plan, forecast.h);
if (!sim.ok) return -1e9;
var first = plan.actions[0] || { type: "wait" };
var firstSkill = first.type === "skill" ? first.skill : null;
var killAt = F_killFrame(w, plan, sim, firstSkill);
var score = 0;
for (var t = 0; t < forecast.h; t++) {
var th = F_danger(forecast, t, sim.pos[t]);
var v = th.v;
if (th.k === "ghost" && w.star && F_same(sim.pos[t], w.star)) v = 0;
if (killAt != null && th.k === "fire" && t > killAt) v = 0;
if (firstSkill === "shield" && t <= 1) v = Math.max(0, v - 1000);
if (v >= 900) score -= 250000 - t * 30000;
else if (v >= 700) score -= 90000 - t * 12000;
else score -= v;
}
score += F_starScore(w, forecast, plan, sim, firstSkill);
score += F_attackScore(w, plan, sim, firstSkill);
score += F_skillScore(w, forecast, plan, sim, firstSkill);
score += F_loopScore(w, sim, first);
score += F_contactScore(w, plan, sim);
if (first.type === "wait") score -= 80;
return score;
}
function F_starScore(w, forecast, plan, sim, firstSkill) {
if (!w.star) return 0;
var stuck = F_stuck(w);
var final = sim.pos[sim.pos.length - 1];
var base = F_dist(w, w.me.p, w.star);
var fd = F_dist(w, final, w.star);
var score = 0;
if (F_same(final, w.star)) score += 130000;
if (base < 99 && fd < 99) score += (base - fd) * 9000 + Math.max(0, 20 - fd) * 700;
if (final[0] === w.star[0] || final[1] === w.star[1]) {
if (!F_lineBlock(w, final, w.star)) score += 2500;
}
if (plan.actions[0] && plan.actions[0].type === "fire" && F_fireBreaksStarMound(w)) score += 12000;
if (F_enemyMayWinStar(w) && !F_same(final, w.star)) score -= 2500;
if (stuck) score -= 18000;
return score;
}
function F_attackScore(w, plan, sim, firstSkill) {
if (!w.enemy.tank) return 0;
var score = 0;
var fireAt = F_fireAt(plan);
var over = firstSkill === "overload" || (w.me.status && w.me.status.overloaded);
if (fireAt != null) {
var pos = sim.pos[Math.min(fireAt, sim.pos.length - 1)];
var dir = sim.dir[Math.min(fireAt, sim.dir.length - 1)];
if (F_shotHits(w, pos, dir, w.enemy.tank.p, over)) {
score += 110000 - fireAt * 12000;
if (fireAt === 0 && F_man(pos, w.enemy.tank.p) <= 2) score += 230000;
}
else if (F_fireHitsMound(w, pos, dir) || (over && F_fireHitsMound(w, dir === "up" || dir === "down" ? [pos[0] + 1, pos[1]] : [pos[0], pos[1] + 1], dir))) {
if (w.star && !F_map15Shape(w) && F_dist(w, w.me.p, w.star) < 99 && !F_fireHitsStarMound(w, pos, dir, over)) score -= 2200;
else score += over ? 22000 : (w.star ? 3500 : 7000);
}
else score -= 1800;
} else {
var final = sim.pos[sim.pos.length - 1];
score += (F_man(w.me.p, w.enemy.tank.p) - F_man(final, w.enemy.tank.p)) * (F_stuck(w) ? 1800 : 220);
if (F_shotHits(w, final, sim.dir[sim.dir.length - 1], w.enemy.tank.p, false)) score += 2200;
}
if (F_stuck(w) && plan.actions[0] && plan.actions[0].type === "fire" && F_fireHitsMound(w, w.me.p, w.me.d)) score += 15000;
return score;
}
function F_skillScore(w, forecast, plan, sim, sk) {
if (!sk) return 0;
var now = F_danger(forecast, 0, w.me.p).v;
if (sk === "shield" && now >= 900) return 140000;
if (sk === "boost" && w.star) return 4000;
if (sk === "overload" && F_fireAt(plan) != null) return 6000;
return w.enemy.tank ? 2500 : -200;
}
function F_loopScore(w, sim, first) {
var score = 0;
var p = sim.pos[sim.pos.length - 1];
for (var i = 0; i < F_MEM.self.length; i++) {
if (F_same(p, F_MEM.self[i].p)) score -= 450 * (i + 1);
}
if (first.type === "turn" && F_MEM.self.length >= 2) {
var a = F_MEM.self[F_MEM.self.length - 1], b = F_MEM.self[F_MEM.self.length - 2];
if (F_same(a.p, b.p) && F_same(a.p, w.me.p) && a.d !== b.d) score -= 3000;
}
return score;
}
function F_contactScore(w, plan, sim) {
if (!w.enemy.tank || F_fireAt(plan) != null) return 0;
var score = 0, starMode = !!w.star;
for (var t = 0; t < sim.pos.length; t++) {
var p = sim.pos[t], m = F_man(p, w.enemy.tank.p);
var line = (p[0] === w.enemy.tank.p[0] || p[1] === w.enemy.tank.p[1]) && !F_lineBlock(w, p, w.enemy.tank.p);
if (starMode) {
if (m <= 1) score -= 65000 - t * 7000;
continue;
}
if (m <= 1) score -= 65000 - t * 7000;
else if (m === 2) score -= 18000 - t * 2500;
if (line && m <= 4) score -= 12000 - t * 1500;
}
return score;
}
function F_stuck(w) {
if (!F_MEM.self || F_MEM.self.length < 8) return false;
var seen = {};
for (var i = Math.max(0, F_MEM.self.length - 8); i < F_MEM.self.length; i++) {
seen[F_MEM.self[i].p[0] + "," + F_MEM.self[i].p[1]] = true;
}
var n = 0;
for (var k in seen) if (Object.prototype.hasOwnProperty.call(seen, k)) n++;
return n <= 4;
}
function F_lineShotEscapeAction(w) {
if (!w.enemy.tank) return null;
if (F_map15Shape(w) && F_same(w.star, [12, 1]) && F_same(w.me.p, [12, 2])) return w.me.d === "up" ? { type: "go" } : { type: "turn", side: F_sideTo(w.me.d, "up") };
if (F_map15Shape(w) && w.star && w.star[1] >= 6 && F_same(w.me.p, [12, 2])) {
if (w.enemy.tank.p[0] === 12 && F_tile(w, [12, 7]) !== "m") return (w.me.bullet || (w.me.status && w.me.status.fireLocked)) ? { type: "wait" } : (w.me.d === "down" ? { type: "fire" } : { type: "turn", side: F_sideTo(w.me.d, "down") });
return w.me.d === "down" ? { type: "go" } : { type: "turn", side: F_sideTo(w.me.d, "down") };
}
if (F_map15Shape(w) && w.star && w.star[1] >= 6 && F_recentStill(w, 5) && F_same(w.me.p, [13, 2]) && w.me.d === "left") return { type: "go" };
var hitPos = w.me.p, future = false, s = w.enemy.skill || {}, cd = Number(s.cooldownFrames) || 0, rem = Number(s.remainingCooldownFrames) || 0, eo = !F_map15Shape(w) && s.type === "overload" && cd > 0 && cd - rem > 1 && cd - rem <= 4;
if (!F_shotHits(w, w.enemy.tank.p, w.enemy.tank.d, hitPos, eo)) {
hitPos = F_add(w.me.p, F_VEC[w.me.d]);
future = true;
if (!F_stand(w, hitPos) || F_enemyHasBullet(w) || !F_shotHits(w, w.enemy.tank.p, w.enemy.tank.d, hitPos, eo)) return null;
}
if (F_man(hitPos, w.enemy.tank.p) > (eo ? 8 : 5)) return null;
if (w.enemy.status && w.enemy.status.fireLocked && !F_enemyHasBullet(w)) return null;
if (!future && !w.me.bullet && !(w.me.status && w.me.status.fireLocked) && F_man(w.me.p, w.enemy.tank.p) <= 2 && F_shotHits(w, w.me.p, w.me.d, w.enemy.tank.p, false)) return null;
var fwd = F_add(w.me.p, F_VEC[w.me.d]); if (!future && F_stand(w, fwd) && !F_shotHits(w, w.enemy.tank.p, w.enemy.tank.d, fwd, eo)) return { type: "go" };
var best = null, bestBad = 9;
for (var i = 0; i < 2; i++) {
var side = i ? "right" : "left";
var d = F_turn(w.me.d, side), n = F_add(w.me.p, F_VEC[d]);
var bad = F_shotHits(w, w.enemy.tank.p, w.enemy.tank.d, n, eo) ? 3 : 0;
if (!F_stand(w, n)) bad += 1;
if (bad < bestBad) {
bestBad = bad;
best = side;
}
}
return best ? { type: "turn", side: best } : null;
}
function F_starLineTrapAction(w) {
if (!w.star || !w.enemy.tank || F_enemyHasBullet(w) || (w.enemy.status && w.enemy.status.fireLocked)) return null;
var n = F_add(w.me.p, F_VEC[w.me.d]);
if (!F_same(n, w.star) || F_man(w.enemy.tank.p, w.star) > 5 || F_lineBlock(w, w.enemy.tank.p, w.star)) return null;
for (var i = 0; i < 2; i++) {
var side = i ? "right" : "left", p = F_add(w.me.p, F_VEC[F_turn(w.me.d, side)]);
if (F_stand(w, p) && !F_shotHits(w, w.enemy.tank.p, w.enemy.tank.d, p, false)) return { type: "turn", side: side };
}
return { type: "wait" };
}
function F_leftPocketBreakAction(w) {
if (!w.star || w.star[1] < 6 || w.me.bullet || (w.me.status && w.me.status.fireLocked)) return null;
if (F_map15Shape(w) && w.star[0] === 4 && w.star[1] >= 8 && F_same(w.me.p, [2, 6]) && (!w.enemy.tank || F_man(w.me.p, w.enemy.tank.p) > 6) && F_tile(w, [4, 6]) === "m") return w.me.d === "right" ? { type: "fire" } : { type: "turn", side: F_sideTo(w.me.d, "right") };
if (F_map15Shape(w) && w.star[0] <= 5 && w.star[1] >= 8 && F_same(w.me.p, [3, 6]) && F_tile(w, [4, 6]) === "m") return w.me.d === "right" ? { type: "fire" } : { type: "turn", side: F_sideTo(w.me.d, "right") };
if (w.star[0] > 4) return null;
if (w.star[0] > 3) return null;
if (!F_same(w.me.p, [4, 3]) || F_tile(w, [3, 3]) !== "m") return null;
if (w.me.d === "left") return { type: "fire" };
return { type: "turn", side: F_turn(w.me.d, "left") === "left" ? "left" : "right" };
}
function F_map15ReturnAction(w) {
if (!F_map15Shape(w) || !w.star || !F_same(w.star, [3, 4]) || F_tile(w, [3, 3]) !== "m") {
F_MEM.map15ReturnUntil = null;
return null;
}
if (w.enemy.tank && F_shotHits(w, w.enemy.tank.p, w.enemy.tank.d, w.me.p, false)) return null;
if (F_same(w.me.p, [12, 2]) && F_recentStill(w, 3)) F_MEM.map15ReturnUntil = w.frame + 24;
if (F_MEM.map15ReturnUntil == null || w.frame > F_MEM.map15ReturnUntil) return null;
if (w.me.p[1] === 2 && w.me.p[0] > 3) {
if (w.me.d === "left") return { type: "go" };
return { type: "turn", side: F_sideTo(w.me.d, "left") };
}
if (F_same(w.me.p, [3, 2])) {
if (w.me.d === "down" && !w.me.bullet && !(w.me.status && w.me.status.fireLocked)) return { type: "fire" };
return { type: "turn", side: F_sideTo(w.me.d, "down") };
}
var step = F_routeStep(w, w.me.p, [3, 2]);
return step;
}
function F_map15Shape(w) {
return w.w === 15 && w.h === 11 && F_tile(w, [1, 3]) === "m" && F_tile(w, [3, 6]) === "o" && F_tile(w, [4, 4]) === "x" && F_tile(w, [5, 5]) === "x";
}
function F_stuckFaceEscapeAction(w) {
if (!w.star || !F_recentStill(w, 3)) return null;
if (!w.enemy.tank || F_man(w.me.p, w.enemy.tank.p) > 2) return null;
var best = null, bestV = 1e9;
for (var i = 0; i < 2; i++) {
var side = i ? "right" : "left";
var d = F_turn(w.me.d, side), n = F_add(w.me.p, F_VEC[d]);
if (!F_stand(w, n)) continue;
if (w.enemy.tank && F_same(w.enemy.tank.p, n)) continue;
var v = F_dist(w, n, w.star) * 10 + F_man(n, w.star);
if (v < bestV) {
bestV = v;
best = side;
}
}
return best ? { type: "turn", side: best } : null;
}
function F_cornerKillAction(w) {
if (w.star || !w.enemy.tank || F_enemyHasBullet(w) || w.me.bullet || (w.me.status && w.me.status.fireLocked)) return null;
if (w.w !== 19 || w.h !== 15 || w.me.p[1] !== 1 || w.me.p[0] < 12 || F_tile(w, [w.me.p[0], 0]) !== "x") return null;
var ep = w.enemy.tank.p;
if (ep[0] !== w.me.p[0] || ep[1] <= w.me.p[1] || ep[1] - w.me.p[1] > 3 || F_lineBlock(w, w.me.p, ep)) return null;
if (F_shotHits(w, w.me.p, w.me.d, ep, false)) return { type: "fire" };
return { type: "turn", side: F_sideTo(w.me.d, "down") };
}
function F_recentStill(w, n) {
if (!F_MEM.self || F_MEM.self.length < n) return false;
for (var i = F_MEM.self.length - n; i < F_MEM.self.length; i++) {
if (!F_same(F_MEM.self[i].p, w.me.p) || F_MEM.self[i].d !== w.me.d) return false;
}
return true;
}
function F_threeStarEscape(w) {
if (w.star || !w.enemy.tank || w.me.stars < 3 || F_enemyHasBullet(w)) return null;
if (F_man(w.me.p, w.enemy.tank.p) !== 1) return null;
if (w.me.p[0] !== w.enemy.tank.p[0] && w.me.p[1] !== w.enemy.tank.p[1]) return null;
var n = F_add(w.me.p, F_VEC[w.me.d]);
if (F_stand(w, n) && F_man(n, w.enemy.tank.p) > 1) return { type: "go" };
return null;
}
function F_tailEscapeAction(w) {
var c = F_MEM.tailTurn;
if (!c || c.f !== w.frame) return null;
F_MEM.tailTurn = null;
var d = F_turn(w.me.d, c.side);
if (F_stand(w, F_add(w.me.p, F_VEC[d]))) return { type: "turn", side: c.side };
return null;
}
function F_setTailEscape(w, a) {
if (!a || a.type !== "go") return;
var src = F_chaseSource(w, w.me.p, w.me.d);
if (!src) return;
var n = F_add(w.me.p, F_VEC[w.me.d]);
if (!F_stand(w, n)) return;
var right = F_turn(w.me.d, "right"), left = F_turn(w.me.d, "left");
if (F_stand(w, F_add(n, F_VEC[right]))) F_MEM.tailTurn = { f: w.frame + 1, side: "right" };
else if (F_stand(w, F_add(n, F_VEC[left]))) F_MEM.tailTurn = { f: w.frame + 1, side: "left" };
}
function F_chaseSource(w, p, d) {
var bs = [];
if (w.enemy.bullet) bs.push(w.enemy.bullet);
for (var i = 0; i < (F_MEM.bullets || []).length; i++) bs.push(F_MEM.bullets[i]);
for (var j = 0; j < bs.length; j++) {
var b = bs[j];
if (b && b.d === d && F_chaseLine(w, b.p, p, d)) return b;
}
return null;
}
function F_chaseLine(w, b, p, d) {
if (!b || (d === "left" || d === "right" ? b[1] !== p[1] : b[0] !== p[0])) return false;
if (F_lineBlock(w, b, p)) return false;
var v = F_VEC[d], dist = (p[0] - b[0]) * v[0] + (p[1] - b[1]) * v[1];
return dist > 0 && dist <= 10;
}
function F_sim(w, plan, horizon) {
var p = F_cp(w.me.p), d = w.me.d, pos = [], dirs = [], ok = true;
for (var t = 0; t < horizon; t++) {
var a = plan.actions[t] || { type: "wait" };
if (a.type === "go") {
var n = F_add(p, F_VEC[d]);
if (!F_stand(w, n)) ok = false;
else p = n;
} else if (a.type === "turn") d = F_turn(d, a.side);
else if (a.type === "teleport") {
var tp = [a.x, a.y];
if (!F_stand(w, tp)) ok = false;
else p = tp;
}
pos.push(F_cp(p));
dirs.push(d);
}
return { ok: ok, pos: pos, dir: dirs };
}
function F_forecast(w, horizon) {
var f = { h: horizon, c: {} };
for (var bi = 0; bi < (F_MEM.bullets || []).length; bi++) F_bulletPath(f, w, 0, F_MEM.bullets[bi].p, F_MEM.bullets[bi].d, 1000, "bullet");
var sources = F_enemySources(w);
for (var si = 0; si < sources.length; si++) {
var src = sources[si];
var od = !src.ghost && F_enemyOverload(w);
var fk = src.ghost ? "ghost" : "fire";
var hasBullet = F_enemyHasBullet(w);
var soonGone = !w.enemy.bullet && F_enemyMemoryBulletSoonGone(w);
if ((!hasBullet || soonGone) && (src.ghost || F_enemyCanFire(w) || soonGone)) {
var start = hasBullet ? 1 : 0;
for (var t = start; t < horizon; t++) F_shotField(f, w, t, src.p, src.d, od, t ? 900 : 1000, fk);
for (var t1 = Math.max(1, start); t1 < horizon; t1++) {
F_shotField(f, w, t1, src.p, F_turn(src.d, "left"), od, 900, fk);
F_shotField(f, w, t1, src.p, F_turn(src.d, "right"), od, 900, fk);
}
var back = F_turn(F_turn(src.d, "left"), "left");
if (!F_facingBlock(w, src.p, src.d)) {
for (var t2 = Math.max(2, start + 1); t2 < horizon; t2++) F_shotField(f, w, t2, src.p, back, od, 850, fk);
}
} else if (!src.ghost && F_enemyHasBullet(w)) {
for (var t3 = 2; t3 < horizon; t3++) F_shotField(f, w, t3, src.p, src.d, od, 750, fk);
}
if (!src.ghost && !od && w.enemy.skill && w.enemy.skill.type === "overload" && w.enemy.skill.remainingCooldownFrames === 0) {
for (var t4 = 2; t4 < horizon; t4++) F_shotField(f, w, t4, src.p, src.d, true, 900, "fire");
}
}
return f;
}
function F_enemySources(w) {
if (w.enemy.tank) return [{ p: w.enemy.tank.p, d: w.enemy.tank.d, ghost: false }];
var last = F_MEM.enemy.length ? F_MEM.enemy[F_MEM.enemy.length - 1] : null;
if (!last || w.frame - last.f > 10) return [];
var out = [], seen = {};
function add(p, d) {
var k = p[0] + "," + p[1];
if (!seen[k] && F_stand(w, p)) {
seen[k] = true;
out.push({ p: F_cp(p), d: d, ghost: true });
}
}
add(last.p, last.d);
for (var i = 0; i < 4; i++) {
var p = F_add(last.p, F_VEC[F_DIRS[i]]);
if (F_tile(w, p) === "o") add(p, F_DIRS[i]);
}
return out;
}
function F_enemyOverload(w) {
if (w.enemy.status && w.enemy.status.overloaded) return true;
var s = w.enemy.skill || {};
var cd = Number(s.cooldownFrames) || 0, rem = Number(s.remainingCooldownFrames) || 0;
return s.type === "overload" && cd > 0 && cd - rem <= 1;
}
function F_enemyCanFire(w) {
if (w.enemy.status && w.enemy.status.fireLocked) return false;
return !F_enemyHasBullet(w);
}
function F_enemyHasBullet(w) {
return !!(w.enemy.bullet || (F_MEM.bullets && F_MEM.bullets.length));
}
function F_facingBlock(w, p, d) {
return F_blocksBullet(w, F_add(p, F_VEC[d]));
}
function F_enemyMemoryBulletSoonGone(w) {
var bs = F_MEM.bullets || [];
if (!bs.length) return false;
for (var i = 0; i < bs.length; i++) {
if (!F_advanceBullet(w, bs[i], w.frame + 1)) return true;
}
return false;
}
function F_shotField(f, w, t, p, d, overload, v, kind) {
F_bulletPath(f, w, t, p, d, v, kind);
F_moundFollow(f, w, t, p, d, v, kind);
if (overload) {
var q = d === "up" || d === "down" ? [p[0] + 1, p[1]] : [p[0], p[1] + 1];
F_bulletPath(f, w, t, q, d, v, kind);
F_moundFollow(f, w, t, q, d, v, kind);
}
}
function F_moundFollow(f, w, t, p0, d, v, kind) {
if (t + 1 >= f.h || !p0 || !F_VEC[d]) return;
var p = F_cp(p0), step = F_VEC[d], opened = false;
while (true) {
p = F_add(p, step);
if (!F_inside(w, p) || F_tile(w, p) === "x") return;
if (!opened) {
if (F_tile(w, p) === "m") {
opened = true;
continue;
}
continue;
}
if (F_blocksBullet(w, p)) return;
for (var tt = t + 1; tt < f.h; tt++) F_mark(f, tt, p, Math.max(700, v - 100), kind);
}
}
function F_bulletPath(f, w, start, p0, d, v, kind) {
if (!p0 || !F_VEC[d]) return;
var p = F_cp(p0), step = F_VEC[d];
for (var t = start; t < f.h; t++) {
if (F_inside(w, p)) F_mark(f, t, p, v, kind);
for (var i = 0; i < 2; i++) {
p = F_add(p, step);
if (!F_inside(w, p)) return;
F_mark(f, t, p, v, kind);
if (F_blocksBullet(w, p)) return;
}
}
}
function F_mark(f, t, p, v, kind) {
var k = t + ":" + p[0] + "," + p[1];
if (!f.c[k] || f.c[k].v < v) f.c[k] = { v: v, k: kind };
}
function F_danger(f, t, p) {
return f.c[t + ":" + p[0] + "," + p[1]] || { v: 0, k: "" };
}
function F_fireAt(plan) {
for (var i = 0; i < plan.actions.length; i++) if (plan.actions[i].type === "fire") return i;
return null;
}
function F_killFrame(w, plan, sim, firstSkill) {
if (!w.enemy.tank) return null;
var fireAt = F_fireAt(plan);
if (fireAt == null) return null;
var pos = sim.pos[Math.min(fireAt, sim.pos.length - 1)];
var dir = sim.dir[Math.min(fireAt, sim.dir.length - 1)];
if (!F_shotHits(w, pos, dir, w.enemy.tank.p, firstSkill === "overload" || (w.me.status && w.me.status.overloaded))) return null;
return fireAt + Math.floor((F_man(pos, w.enemy.tank.p) - 1) / 2);
}
function F_shotHits(w, p, d, target, overload) {
if (!target || !F_VEC[d]) return false;
if (overload) {
var q = d === "up" || d === "down" ? [p[0] + 1, p[1]] : [p[0], p[1] + 1];
if (F_shotHits(w, q, d, target, false)) return true;
}
var v = F_VEC[d], x = F_cp(p);
while (true) {
x = F_add(x, v);
if (!F_inside(w, x) || F_blocksBullet(w, x)) return false;
if (F_same(x, target)) return true;
}
}
function F_fireHitsMound(w, p, d) {
return !!F_firstMound(w, p, d);
}
function F_fireHitsStarMound(w, p, d, over) {
var m = F_firstMound(w, p, d);
if (m && F_starMoundUseful(w, m)) return true;
if (!over) return false;
var q = d === "up" || d === "down" ? [p[0] + 1, p[1]] : [p[0], p[1] + 1];
m = F_firstMound(w, q, d);
return !!(m && F_starMoundUseful(w, m));
}
function F_firstMound(w, p, d) {
var v = F_VEC[d], x = F_cp(p);
while (true) {
x = F_add(x, v);
if (!F_inside(w, x) || F_tile(w, x) === "x") return null;
if (F_tile(w, x) === "m") return x;
}
}
function F_starMoundUseful(w, m) {
return w.star && (m[0] === w.star[0] || m[1] === w.star[1] || F_man(m, w.star) <= 3);
}
function F_fireBreaksStarMound(w) {
if (!w.star) return false;
return F_fireHitsMound(w, w.me.p, w.me.d) && (w.me.p[0] === w.star[0] || w.me.p[1] === w.star[1]);
}
function F_lineBlock(w, a, b) {
if (!a || !b) return true;
if (a[0] !== b[0] && a[1] !== b[1]) return true;
var ck = "L" + a[0] + "," + a[1] + ">" + b[0] + "," + b[1];
if (w.cache && w.cache[ck] != null) return w.cache[ck];
var d = a[0] === b[0] ? (a[1] < b[1] ? "down" : "up") : (a[0] < b[0] ? "right" : "left");
var p = F_cp(a), v = F_VEC[d];
while (true) {
p = F_add(p, v);
if (F_same(p, b)) return w.cache ? (w.cache[ck] = false) : false;
if (!F_inside(w, p) || F_blocksBullet(w, p)) return w.cache ? (w.cache[ck] = true) : true;
}
}
function F_enemyMayWinStar(w) {
if (!w.star || !w.enemy.tank) return false;
return F_dist(w, w.enemy.tank.p, w.star) <= F_dist(w, w.me.p, w.star) + 1;
}
function F_nearEnemyStar(w, sim) {
if (!w.star || !w.enemy.tank) return false;
var p = sim.pos[sim.pos.length - 1];
return F_man(w.enemy.tank.p, w.star) <= 7 || F_man(w.enemy.tank.p, p) <= 6;
}
function F_dist(w, start, goal) {
if (!start || !goal) return 99;
if (F_same(start, goal)) return 0;
var ck = start[0] + "," + start[1] + ">" + goal[0] + "," + goal[1];
if (w.cache && w.cache[ck] != null) return w.cache[ck];
var q = [start], seen = {}, head = 0;
seen[start[0] + "," + start[1]] = 0;
while (head < q.length && head < 220) {
var p = q[head++], d = seen[p[0] + "," + p[1]];
for (var i = 0; i < 4; i++) {
var n = F_add(p, F_VEC[F_DIRS[i]]), k = n[0] + "," + n[1];
if (seen[k] != null || !F_stand(w, n)) continue;
if (F_same(n, goal)) return w.cache ? (w.cache[ck] = d + 1) : d + 1;
seen[k] = d + 1;
q.push(n);
}
}
return w.cache ? (w.cache[ck] = 99) : 99;
}
function F_routeStep(w, start, goal) {
var best = null, bestD = 99;
for (var i = 0; i < 4; i++) {
var d = F_DIRS[i], n = F_add(start, F_VEC[d]);
if (!F_stand(w, n)) continue;
var dist = F_dist(w, n, goal);
if (dist < bestD) {
bestD = dist;
best = d;
}
}
if (!best) return null;
if (best === w.me.d) return { type: "go" };
return { type: "turn", side: F_turn(w.me.d, "left") === best ? "left" : "right" };
}
function F_teleports(w) {
var out = [], seen = {};
function add(p) {
var k = p[0] + "," + p[1];
if (F_stand(w, p) && !seen[k]) {
seen[k] = true;
out.push(p);
}
}
if (w.star) {
add(w.star);
for (var i = 0; i < 4; i++) add(F_add(w.star, F_VEC[F_DIRS[i]]));
}
if (w.enemy.tank) add(w.enemy.tank.p);
return out.slice(0, w.star ? 2 : 1);
}
function F_guardGo(w, a) { var n = F_add(w.me.p, F_VEC[w.me.d]); if (!F_liveBulletDanger(w, n)) return a; for (var i = 0; i < 2; i++) { var side = i ? "right" : "left"; if (!F_liveBulletDanger(w, w.me.p)) return { type: "turn", side: side }; } return { type: "wait" }; }
function F_liveBulletDanger(w, p) { var bs = w.enemy.bullet ? [w.enemy.bullet] : []; for (var i = 0; i < (F_MEM.bullets || []).length; i++) bs.push(F_MEM.bullets[i]); for (var j = 0; j < bs.length; j++) { var b = bs[j], q = F_cp(b.p); for (var s = 0; s < 3; s++) { if (F_same(q, p)) return true; if (s < 2) { q = F_add(q, F_VEC[b.d]); if (!F_inside(w, q) || F_blocksBullet(w, q)) break; } } } return false; }
function F_act(me, a, w) {
if (w && a && a.type === "go") a = F_guardGo(w, a);
if (!a || a.type === "wait") return;
if (a.type === "go") me.go();
else if (a.type === "turn") me.turn(a.side);
else if (a.type === "fire") me.fire();
else if (a.type === "teleport") me.teleport(a.x, a.y);
else if (a.type === "skill" && a.skill && typeof me[a.skill] === "function") me[a.skill]();
}
function F_turn(d, side) {
var i = F_DIRS.indexOf(d);
return F_DIRS[(i + (side === "left" ? 3 : 1)) % 4];
}
function F_sideTo(d, target) { return F_turn(d, "left") === target ? "left" : "right"; }
function F_stand(w, p) {
var t = F_tile(w, p);
return t !== "x" && t !== "m";
}
function F_blocksBullet(w, p) {
var t = F_tile(w, p);
return t === "x" || t === "m";
}
function F_tile(w, p) {
if (!F_inside(w, p)) return "x";
return w.map[p[0]][p[1]];
}
function F_inside(w, p) {
return p && p[0] >= 0 && p[1] >= 0 && p[0] < w.w && p[1] < w.h;
}
function F_add(a, b) { return [a[0] + b[0], a[1] + b[1]]; }
function F_cp(p) { return p ? [p[0], p[1]] : null; }
function F_same(a, b) {
return !!(a && b && a[0] === b[0] && a[1] === b[1]);
}
function F_man(a, b) { return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]); }
@tylearymf

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment