Files
yangxiangyuan 6dc79a2c12 feat: 新增多项功能并添加新工具
- 私人剪贴板新增限时直通URL生成功能
- doc_cloud_keeper新增标题复制按钮
- 新增贪蛇出动游戏工具
- 新增OSS中转直链工具
- 添加批量抽帧测试脚本
2026-08-11 10:22:31 +08:00

102 lines
3.0 KiB
JavaScript

/* 贪蛇出动 - AI 蛇控制器:觅食/避障/边界回避/随机加速 */
(function () {
'use strict';
window.GS = window.GS || {};
const U = GS.U;
class AI {
constructor(snake) {
this.s = snake;
this.decideTimer = U.rand(0, 0.3);
this.wanderAngle = snake.angle;
this.boostTimer = 0;
}
update(dt, world, snakes) {
const s = this.s;
if (s.dead) return;
this.decideTimer -= dt;
this.boostTimer -= dt;
if (this.decideTimer > 0) {
s.boosting = this.boostTimer > 0;
return;
}
this.decideTimer = 0.15;
const h = s.head;
const look = 90 + s.radius * 3;
const aheadX = h.x + Math.cos(s.angle) * look;
const aheadY = h.y + Math.sin(s.angle) * look;
// 1) 边界回避
const margin = 220;
let avoid = null;
if (aheadX < margin || aheadY < margin || aheadX > world.size - margin || aheadY > world.size - margin) {
avoid = Math.atan2(world.size / 2 - h.y, world.size / 2 - h.x);
}
// 2) 蛇身回避:检查前方采样点是否撞上其他蛇
if (avoid == null) {
for (const o of snakes) {
if (o === s || o.dead) continue;
const segs = o.segments();
const rr = (s.radius + o.radius + 26);
for (let i = 0; i < segs.length; i += 2) {
if (U.dist2(aheadX, aheadY, segs[i].x, segs[i].y) < rr * rr) {
// 向左或向右转,选更开阔的一侧
const left = s.angle - Math.PI / 2, right = s.angle + Math.PI / 2;
avoid = this._clearer(left, right, h, snakes, s);
break;
}
}
if (avoid != null) break;
}
}
if (avoid != null) {
s.targetAngle = avoid;
s.boosting = false;
return;
}
// 3) 觅食:找最近金币
let best = null, bestD = 520 * 520;
const near = world.grid.query(h.x, h.y, 520);
for (const c of near) {
const d2 = U.dist2(h.x, h.y, c.x, c.y);
if (d2 < bestD) { bestD = d2; best = c; }
}
if (best) {
s.targetAngle = Math.atan2(best.y - h.y, best.x - h.x);
} else {
// 漫游:小幅随机偏转
this.wanderAngle += U.rand(-0.6, 0.6);
s.targetAngle = this.wanderAngle;
}
// 4) 随机加速:追食或逃跑时小概率冲刺
if (Math.random() < 0.06 && s.score > 30) this.boostTimer = U.rand(0.4, 1.0);
s.boosting = this.boostTimer > 0;
}
_clearer(a1, a2, h, snakes, s) {
const score = a => {
const px = h.x + Math.cos(a) * 160, py = h.y + Math.sin(a) * 160;
let d = 1e9;
for (const o of snakes) {
if (o === s || o.dead) continue;
const segs = o.segments();
for (let i = 0; i < segs.length; i += 3) {
const dd = U.dist2(px, py, segs[i].x, segs[i].y);
if (dd < d) d = dd;
}
}
return d;
};
return score(a1) >= score(a2) ? a1 : a2;
}
}
GS.AI = AI;
})();