全军覆没!清华大学软件工程专业考研无人过线!

清华大学 是中国境内TOP2的学校,是多少同学梦想的地方。而它的计算机系,也是国内数一数二的。清华大学 的计算机学科评估A+,软件评估A,去年在某国际机构的排名中,他的计算机排名全球第一。

前几天,计算机系公布了硕士招生复试录取办法,翻到最后,有这样一句话:

特殊说明:因报考计算机科学与技术系软件工程专业的考生没有人上线,所以将该专业招生计划调 整到计算机科学与技术专业。

无人上线?

都被清华大学全球第一的排名吓怕了而不敢考了么?

小编看到复试录取办法上面写了复试分数线:

361分的分数线!

再加上清华大学912的试卷又很难,而且可能压分。

小编又去看了招生目录,发现只招一人。

怪不得无人上线。

对于足够优秀的同学来讲,人少的专业风险大,但是也容易捡漏。

因为大部分人都不敢报考人少的专业,可能害怕有大佬/关系户等等。

但是如果就你过线了,意味着你的录取机会非常大。

即使没有录取你,清华大学今年也有许多校内调剂等着你。

小编发现这几个专业去年也招调剂。

如果你的实力很优秀,其实也可以考虑像清华大学这类的学校。

清华软件学院考310分和普通985考400分,到底哪个更难呢?

郑重提示:

今年的数据并不等于明年的数据,我没有推荐20考研的同学考清华大学。请同学们谨慎选择。

考研有风险,入坑需谨慎。

20考研的QQ群,有各个学校的考研资料,欢迎加入

群号是 935371763


您还可以在以下平台找到我们

如果觉得有用,请点一下好看~

<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <title>🤖 AI大乱斗 3.0 - 智能战场革命</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background: #0a0f1a; color: #cdd; font-family: 'Segoe UI', sans-serif; text-align: center; padding: 20px; } h1 { font-size: 2.8em; margin: 15px 0; color: #aaccff; text-shadow: 0 0 20px rgba(100, 180, 255, 0.6); } .intro { color: #9ab; max-width: 900px; margin: 0 auto 20px; font-size: 1.1em; } /* 配置面板 */ .config-panel { width: 90%; max-width: 1000px; margin: 20px auto; padding: 15px; background: rgba(30, 40, 60, 0.6); border: 1px solid #335; border-radius: 10px; text-align: left; display: grid; grid-template-columns: repeat(auto-fit, minmax(480px, 1fr)); gap: 15px; } .char-config { padding: 15px; background: rgba(20, 30, 50, 0.5); border-radius: 8px; border: 1px solid #446; } .char-config h3 { color: #bbd; margin-bottom: 10px; } .slider-group { margin: 8px 0; display: flex; align-items: center; } .slider-group label { width: 60px; text-align: right; margin-right: 10px; font-size: 0.95em; } input[type="range"] { flex: 1; } span.value { width: 40px; text-align: center; font-weight: bold; color: #8ac; } .points-left { font-size: 0.95em; color: #faa; margin: 5px 0; font-weight: bold; } button.start { background: linear-gradient(to bottom, #4a6fa5, #3a5a80); color: white; border: none; padding: 14px 24px; font-size: 1.1em; font-weight: bold; cursor: pointer; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); } button.reset { background: linear-gradient(to bottom, #8a4f4f, #7a3f3f); color: white; border: none; padding: 12px 20px; font-size: 1em; margin: 10px 5px; cursor: pointer; border-radius: 6px; } /* 决斗场 */ #arena { width: 90vw; height: 60vh; max-width: 1000px; margin: 20px auto; border: 2px solid #335577; position: relative; background: radial-gradient(circle at center, #121828 0%, #0a0f1a 80%); box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.8), 0 0 20px rgba(60, 100, 180, 0.2); overflow: hidden; border-radius: 12px; } .character { position: absolute; width: 56px; height: 56px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 1.1em; color: white; text-shadow: 1px 1px 2px black; user-select: none; transition: transform 0.1s ease; box-shadow: 0 0 10px currentColor; z-index: 2; } .hp-bar-outer { position: absolute; top: -18px; left: 0; width: 56px; height: 6px; background: rgba(0, 0, 0, 0.6); border-radius: 3px; overflow: hidden; } .hp-bar-inner { height: 100%; width: 100%; background: linear-gradient(90deg, #50c878, #41a05a); transition: width 0.3s ease; } .status-tag { position: absolute; bottom: -18px; left: 0; width: 56px; font-size: 0.7em; text-align: center; white-space: nowrap; color: #ddd; text-shadow: 1px 1px 1px black; font-weight: bold; } .buff-point { position: absolute; width: 30px; height: 30px; border: 2px solid; border-radius: 50%; animation: pulse 1.5s infinite alternate; z-index: 1; box-shadow: 0 0 10px currentColor; } .buff-health { background: #00ff88; border-color: #00ff88; } .buff-attack { background: #ff4444; border-color: #ff4444; } .buff-speed { background: #44aaff; border-color: #44aaff; } @keyframes pulse { 0% { transform: scale(1); opacity: 0.8; } 100% { transform: scale(1.2); opacity: 1; } } .explosion { position: absolute; width: 80px; height: 80px; background: radial-gradient(circle, #ffd700, #ff4500, transparent); border-radius: 50%; animation: explode 0.6s ease-out forwards; pointer-events: none; z-index: 10; } @keyframes explode { 0% { transform: scale(0); opacity: 1; } 100% { transform: scale(1.5); opacity: 0; } } #log { width: 90%; max-width: 1000px; max-height: 140px; margin: 15px auto; padding: 12px; background: rgba(20, 30, 40, 0.6); border: 1px solid #335; color: #ccc; font-family: 'Courier New', monospace; font-size: 0.95em; overflow-y: auto; text-align: left; white-space: pre-line; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); } #stats { width: 90%; max-width: 1000px; margin: 15px auto; padding: 12px; background: rgba(20, 30, 40, 0.6); border: 1px solid #335; color: #ccc; font-size: 0.95em; border-radius: 8px; text-align: left; } .controls { margin: 20px auto; max-width: 800px; display: flex; justify-content: center; gap: 12px; } </style> </head> <body> <h1>🤖 AI大乱斗 3.0 - 智能战场革命</h1> <p class="intro">无障·全自动·多策略·防卡死·可视化。AI会思、会逃跑、会抢包、会走位!</p> <!-- 配置系统 --> <div class="config-panel"> <script> const chars = [ { name: "董", color: "#e57373", hp: 50, atk: 30, spd: 20 }, { name: "邱", color: "#64b5f6", hp: 50, atk: 30, spd: 20 }, { name: "郭", color: "#ffb74d", hp: 50, atk: 30, spd: 20 }, { name: "陆", color: "#ba68c8", hp: 50, atk: 30, spd: 20 } ]; function createConfig() { const panel = document.querySelector('.config-panel'); panel.innerHTML = ''; chars.forEach(char => { const div = document.createElement('div'); div.className = 'char-config'; div.innerHTML = ` <h3><span style="color:${char.color}">${char.name}</span> - 分配100技能点</h3> <div class="points-left" id="points-${char.name}">剩余: 0</div> <div class="slider-group"> <label>血量</label> <input type="range" min="30" max="100" value="${char.hp}" step="1" oninput="updateStat('${char.name}', 'hp', this.value)"> <span id="hp-${char.name}" class="value">${char.hp}</span> </div> <div class="slider-group"> <label>攻击</label> <input type="range" min="10" max="60" value="${char.atk}" step="1" oninput="updateStat('${char.name}', 'atk', this.value)"> <span id="atk-${char.name}" class="value">${char.atk}</span> </div> <div class="slider-group"> <label>速度</label> <input type="range" min="5" max="40" value="${char.spd}" step="1" oninput="updateStat('${char.name}', 'spd', this.value)"> <span id="spd-${char.name}" class="value">${char.spd}</span> </div> `; panel.appendChild(div); }); updateAllPoints(); } function updateStat(name, stat, value) { const c = chars.find(x => x.name === name); const oldTotal = c.hp + c.atk + c.spd; c[stat] = parseInt(value); const newTotal = c.hp + c.atk + c.spd; if (newTotal > 100) { c[stat] -= (newTotal - 100); document.querySelector(`[oninput*="'${name}', '${stat}'"]`).value = c[stat]; } document.getElementById(`${stat}-${name}`).textContent = c[stat]; updateAllPoints(); } function updateAllPoints() { chars.forEach(c => { const total = c.hp + c.atk + c.spd; const el = document.getElementById(`points-${c.name}`); el.textContent = `剩余: ${100 - total}`; el.style.color = total === 100 ? '#8f8' : total < 100 ? '#faa' : '#f88'; }); } window.onload = createConfig; </script> </div> <div class="controls"> <button class="start" onclick="startGame()">▶️ 开启智能战场</button> <button class="reset" onclick="location.reload()">🔄 重置配置</button> </div> <!-- 决斗场 --> <div id="arena"></div> <div id="log">【系统】请为每位AI分配100技能点,然后点击开始。</div> <div id="stats">🏆 胜率统计:<br>董: 0胜|邱: 0胜|郭: 0胜|陆: 0胜</div> <!-- 主逻辑 --> <script> let arena = document.getElementById("arena"); let log = document.getElementById("log"); let statsEl = document.getElementById("stats"); let gameRunning = false; let characters = {}; let buffPoints = []; let winCount = { 董: 0, 邱: 0, 郭: 0, 陆: 0 }; // 属性成长(非线性) function calcHp(raw) { return 50 + Math.floor(50 * (raw / 100) ** 0.8) * 2; } function calcAtk(raw) { return 10 + Math.floor(50 * (raw / 100) ** 0.7); } function calcSpd(raw) { return 1.0 + parseFloat(((raw / 100) ** 0.6 * 3.0).toFixed(2)); } function resetArena() { cancelAnimationFrame(window.gameLoopId); arena.innerHTML = ""; characters = {}; buffPoints = []; gameRunning = false; } function logMessage(msg) { log.innerHTML += msg + "\n"; log.scrollTop = log.scrollHeight; } function distance(a, b) { return Math.hypot(a.x - b.x, a.y - b.y); } function spawnBuffPoint(type = 'health') { const x = 50 + Math.random() * (arena.clientWidth - 100); const y = 50 + Math.random() * (arena.clientHeight - 100); const el = document.createElement("div"); el.className = `buff-point buff-${type}`; el.dataset.type = type; el.style.left = `${x - 15}px`; el.style.top = `${y - 15}px`; arena.appendChild(el); const point = { x, y, el, type }; buffPoints.push(point); setTimeout(() => { if (el.parentElement) el.remove(); buffPoints = buffPoints.filter(p => p !== point); }, 8000); } function findNearestBuff(character) { return buffPoints.reduce((closest, p) => { const d1 = closest ? distance(character, closest) : Infinity; const d2 = distance(character, p); return d2 < d1 ? p : closest; }, null); } function createCharacter(config) { let x = 100 + Math.random() * (arena.clientWidth - 200); let y = 100 + Math.random() * (arena.clientHeight - 200); const finalHp = calcHp(config.hp); const finalAtk = calcAtk(config.atk); const finalSpd = calcSpd(config.spd); const el = document.createElement("div"); el.className = "character"; el.innerHTML = ` <div>${config.name}</div> <div class="hp-bar-outer"><div class="hp-bar-inner" id="hp-ui-${config.name}"></div></div> <div class="status-tag" id="tag-${config.name}">待命</div> `; el.style.backgroundColor = config.color; el.id = "char-" + config.name; el.style.transform = `translate(${x}px, ${y}px)`; arena.appendChild(el); characters[config.name] = { element: el, data: config, x, y, hp: finalHp, maxHp: finalHp, atk: finalAtk, spd: finalSpd, moveSpeed: finalSpd, hate: {}, lastAttack: 0, strategy: 'idle', target: null, lastPos: { x, y }, stuckTimer: 0, aiBrain: new AIBrain(config.name) }; logMessage(`⚙️ 【${config.name}】生成: 血${finalHp} 攻${finalAtk} 速${finalSpd.toFixed(2)} (${config.hp}/${config.atk}/${config.spd})`); updateHealth(config.name); } function updateHealth(name) { const c = characters[name]; const bar = document.getElementById(`hp-ui-${name}`); if (bar && c) { const ratio = c.hp / c.maxHp; bar.style.width = `${Math.max(ratio * 100, 0)}%`; bar.style.backgroundColor = ratio > 0.6 ? '#50c878' : ratio > 0.3 ? '#ffbb33' : '#ff4444'; } } function updateStrategyTag(name, strategy) { const tag = document.getElementById(`tag-${name}`); if (tag) { tag.textContent = strategy; tag.style.color = { 'attack': '#f88', 'flee': '#8f8', 'heal': '#0f0', 'speed': '#8af', 'chase': '#fa0' }[strategy] || '#ccc'; } } function addExplosion(x, y) { const explosion = document.createElement("div"); explosion.className = "explosion"; explosion.style.left = `${x - 40}px`; explosion.style.top = `${y - 40}px`; arena.appendChild(explosion); setTimeout(() => explosion.remove(), 600); } function applyBuff(character, buff) { if (buff.type === 'health') { const heal = character.maxHp * 0.4; character.hp = Math.min(character.maxHp, character.hp + heal); logMessage(`💚 【${character.data.name}】拾取【医疗包】,恢复 ${heal.toFixed(0)} HP!`); } else if (buff.type === 'attack') { const boost = 6; character.atk += boost; logMessage(`⚡ 【${character.data.name}】拾取【攻击包】,攻击力+${boost}!`); } else if (buff.type === 'speed') { const boost = 0.8; character.moveSpeed += boost; logMessage(`🚀 【${character.data.name}】拾取【速度包】,移动速度+${boost.toFixed(1)}!`); setTimeout(() => { character.moveSpeed = character.spd; }, 5000); } updateHealth(character.data.name); buff.el.remove(); buffPoints = buffPoints.filter(b => b !== buff); } function attack(attackerName, defenderName) { const now = Date.now(); const attacker = characters[attackerName]; const defender = characters[defenderName]; if (!attacker || !defender || defender.hp <= 0 || now - attacker.lastAttack < 1000) return; attacker.lastAttack = now; let dmg = attacker.atk; if (attacker.data.name === "董" && Math.random() < 0.3) { dmg *= 2; logMessage(`🔥 【雷霆一击】${attacker.data.name} 暴击!造成 ${dmg} 伤害!!`); } else { logMessage(`💥 ${attacker.data.name} 攻击 ${defender.data.name},造成 ${dmg} 伤害`); } if (defender.data.name === "郭" && Math.random() < 0.2) { dmg *= 0.5; logMessage(`🛡️ 【钢铁意志】${defender.data.name} 减伤成功!`); } defender.hp -= dmg; playSound(defender.hp <= 0 ? 'explode' : 'hit'); updateHealth(defenderName); defender.element.style.transform += " scale(1.1)"; setTimeout(() => { defender.element.style.transform = `translate(${defender.x}px, ${defender.y}px)`; }, 150); if (defender.hp <= 0) { addExplosion(defender.x + 28, defender.y + 28); logMessage(`💀 ${defender.data.name} 被击败了!`); } } // ======== 🧠 AI大脑模块 ========== class AIBrain { constructor(name) { this.name = name; } decide(self, allChars, buffs) { const alive = allChars.filter(c => c.hp > 0 && c.data.name !== self.data.name); if (alive.length === 0) return { action: 'idle', target: null }; const lowHp = self.hp < self.maxHp * 0.4; const midHp = self.hp < self.maxHp * 0.7; const nearestBuff = findNearestBuff(self); // 逃跑逻辑 if (lowHp && nearestBuff?.type === 'health' && distance(self, nearestBuff) < 200) { return { action: 'heal', target: nearestBuff }; } if (lowHp) { return { action: 'flee', target: null }; } // 抢包逻辑 if (midHp && nearestBuff?.type === 'health') { return { action: 'heal', target: nearestBuff }; } if (self.atk < 30 && nearestBuff?.type === 'attack') { return { action: 'attack', target: nearestBuff }; } if (nearestBuff?.type === 'speed') { return { action: 'speed', target: nearestBuff }; } // 战斗逻辑 const target = alive.reduce((a, b) => distance(self, a) < distance(self, b) ? a : b); return { action: 'chase', target }; } } function startGame() { const totalPoints = chars.map(c => c.hp + c.atk + c.spd); if (totalPoints.some(p => p !== 100) && !confirm("有人技能点不等于100,是否继续?")) return; resetArena(); chars.forEach(createCharacter); gameRunning = true; log.innerHTML = "【系统】🎮 智能战场启动!\n"; ['health', 'attack', 'speed'].forEach(t => spawnBuffPoint(t)); setInterval(() => { if (!gameRunning) return; const types = ['health', 'attack', 'speed']; spawnBuffPoint(types[Math.floor(Math.random() * 3)]); }, 8000); function getAlive() { return Object.values(characters).filter(c => c.hp > 0); } function gameLoop() { if (!gameRunning) return; const alive = getAlive(); if (alive.length <= 1) { const winner = alive[0]?.data.name; if (winner) winCount[winner]++; statsEl.innerHTML = `🏆 胜率统计:<br>` + `董: ${winCount['董']}胜|` + `邱: ${winCount['邱']}胜|` + `郭: ${winCount['郭']}胜|` + `陆: ${winCount['陆']}胜`; endGame(alive.length ? `🏆 胜者:【${winner}】` : "🔚 全军覆没"); setTimeout(() => { if (confirm("本轮结束!是否开启下一轮?")) { startGame(); } }, 500); return; } for (const name in characters) { const self = characters[name]; if (self.hp <= 0) continue; // AI 决策 const decision = self.aiBrain.decide(self, Object.values(characters), buffPoints); updateStrategyTag(name, decision.action); let moveX = 0, moveY = 0; const baseSpeed = self.moveSpeed * 1.2; if (decision.action === 'heal' || decision.action === 'attack' || decision.action === 'speed') { const goal = decision.target; const dx = goal.x - self.x; const dy = goal.y - self.y; const dist = distance(self, goal); if (dist > 40) { moveX = (dx / dist) * baseSpeed; moveY = (dy / dist) * baseSpeed; } else { if (goal.el) applyBuff(self, goal); } } else if (decision.action === 'flee') { const enemies = alive.filter(c => c.data.name !== self.data.name); const threat = enemies.reduce((a, b) => distance(self, a) < distance(self, b) ? a : b); const dx = self.x - threat.x; const dy = self.y - threat.y; const dist = distance(self, threat); moveX = (dx / dist) * baseSpeed; moveY = (dy / dist) * baseSpeed; } else if (decision.action === 'chase' && decision.target) { const target = decision.target; const dx = target.x - self.x; const dy = target.y - self.y; const dist = distance(self, target); if (dist > 50) { moveX = (dx / dist) * baseSpeed; } else if (dist > 30) { // 走A:保持距离 moveX = (dx / dist) * (baseSpeed * 0.5); moveY = (dy / dist) * (baseSpeed * 0.5); if (Date.now() - self.lastAttack > 1000) attack(name, target.data.name); } else { // 小幅后退绕行 const angle = Math.atan2(dy, dx); moveX = Math.cos(angle + Math.PI/2) * baseSpeed; moveY = Math.sin(angle + Math.PI/2) * baseSpeed; if (Date.now() - self.lastAttack > 1000) attack(name, target.data.name); } } // 排斥力 for (const other of alive) { if (other.data.name === name) continue; const d = distance(self, other); if (d < 60) { const push = (60 - d) * 0.6; const dirX = (self.x - other.x) / d; const dirY = (self.y - other.y) / d; moveX += dirX * push; moveY += dirY * push; } } self.x += moveX; self.y += moveY; self.x = Math.max(28, Math.min(arena.clientWidth - 28, self.x)); self.y = Math.max(28, Math.min(arena.clientHeight - 28, self.y)); self.element.style.transform = `translate(${self.x}px, ${self.y}px)`; } window.gameLoopId = requestAnimationFrame(gameLoop); } setTimeout(() => requestAnimationFrame(gameLoop), 1000); } function endGame(msg) { logMessage(msg); gameRunning = false; } function playSound(type) { try { const ctx = new (window.AudioContext || window.webkitAudioContext)(); const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.connect(gain); gain.connect(ctx.destination); if (type === 'hit') { osc.frequency.setValueAtTime(180, ctx.currentTime); osc.type = 'square'; gain.gain.setValueAtTime(0.15, ctx.currentTime); gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.3); osc.start(); osc.stop(ctx.currentTime + 0.3); } else if (type === 'explode') { osc.frequency.setValueAtTime(120, ctx.currentTime); osc.type = 'sawtooth'; gain.gain.setValueAtTime(0.3, ctx.currentTime); gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.6); osc.start(); osc.stop(ctx.currentTime + 0.6); } } catch (e) {} } </script> </body> </html> ,这个代码有bug,开始后他们会自动站成1列
最新发布
11-18
<think> 我们注意到问题描述中提到了一个bug:开始后角色会站成一列。这通常是由于所有角色都朝着同一个目标移动导致的。观察代码,我们发现每个角色在决策时都会寻找最近的目标(可能是敌人或buff),但所有角色可能都选择了同一个目标,导致他们排成一条直线向目标移动。 另外,在决策逻辑中,对于战斗状态(chase),所有角色都选择最近的敌人作为目标,这同样可能导致多个角色同时追逐同一个目标,从而形成一列。 解决方案: 1. 我们需要确保角色在选择目标时有一定的随机性或者虑其他因素(如仇恨值、血量等),避免所有角色都选择同一个目标。 2. 在追逐逻辑中,我们可以让角色选择不同的目标,或者当多个角色选择同一个目标时,让他们从不同方向接近。 然而,观察当前的决策逻辑(AIBrain类的decide方法): - 首先,低血量且附近有医疗包会去拾取。 - 然后,低血量会逃跑。 - 中等血量且附近有医疗包会去拾取。 - 攻击力低且附近有攻击包会去拾取。 - 有速度包会去拾取。 - 最后,选择最近的敌人进行追逐。 问题可能出在最后一步:所有角色都选择了同一个最近的敌人。我们可以通过让角色选择不同的敌人来解决。 修改思路: - 在决策时,为每个角色选择目标时,可以优先选择不同的目标。我们可以给每个角色维护一个“仇恨”列表,或者根据角色的名字、位置等因素进行一些随机偏移。 - 或者,我们可以让角色选择第二近、第三近的目标,以增加多样性。 但是,为了快速解决问题,我们可以尝试在决策时引入一定的随机性:当多个角色选择目标时,不总是选择最近的,而是有一定概率选择其他目标。 修改decide方法中的目标选择部分(战斗逻辑): 将: const target = alive.reduce((a, b) => distance(self, a) < distance(self, b) ? a : b); 改为: // 将敌人按距离排序,然后随机选择一个较近的(不一定是最近的) alive.sort((a, b) => distance(self, a) - distance(self, b)); // 从最近的三个中随机选一个 const candidates = alive.slice(0, Math.min(3, alive.length)); const target = candidates[Math.floor(Math.random() * candidates.length)]; 这样,每个角色选择目标时会在最近的三个敌人中随机选一个,从而避免所有角色都追同一个。 另外,在追逐同一个目标时,我们也可以调整他们的移动方向,让他们尝试从不同角度接近。但是,我们先解决选择目标的问题。 此外,我们还需要注意,在追逐同一个目标时,排斥力(角色之间的排斥)应该会让他们稍微散开,但可能不够。 我们进行如下修改: - 在AIBrain的decide方法中,修改目标选择逻辑,从最近的三个敌人中随机选择一个。 - 同时,在追逐时,如果目标相同,我们可以让角色尝试从不同方向接近?这可以通过在移动计算时加入一些随机偏移来实现,但可能会使逻辑复杂。 我们先修改目标选择部分,看看效果。 另外,我们注意到在追逐逻辑中,如果距离小于30,角色会尝试绕行(小幅后退并侧移),这也有助于分散。 我们尝试修改decide方法中的战斗逻辑部分。 修改后的decide方法(战斗逻辑部分): ```javascript // 战斗逻辑 // 将敌人按距离排序 const sortedAlive = [...alive].sort((a, b) => distance(self, a) - distance(self, b)); // 从最近的三个中随机选一个 const candidateCount = Math.min(3, sortedAlive.length); const target = sortedAlive[Math.floor(Math.random() * candidateCount)]; ``` 这样修改后,每个角色选择的目标可能不同,从而避免站成一列。 另外,我们还要注意,在游戏开始时,所有角色都在中间,可能距离都很近,所以排序后前三个可能是随机的(因为距离相近),但随机选择会分散目标。 我们还需要测试。 但是,我们还要虑其他状态(如拾取buff)是否会导致站成一列?比如所有角色都去抢同一个buff。我们可以用同样的方法,让角色在多个buff之间选择时,不要总是选最近的,而是随机选一个较近的。 不过,原逻辑中抢buff是找最近的,这很合理,因为buff是固定点,多个角色同时抢一个也是正常的,而且有排斥力会让他们稍微分散。所以暂时不修改buff的选择逻辑。 我们主要修改敌人目标选择。 修改代码: 在AIBrain的decide方法中,找到战斗逻辑部分(最后return之前),将原来的目标选择替换为上述代码。 同时,为了避免在敌人数量不足3个时出现空数组,我们使用candidateCount来控制。 具体修改如下: 原代码: const target = alive.reduce((a, b) => distance(self, a) < distance(self, b) ? a : b); 替换为: // 按距离排序 alive.sort((a, b) => { const d1 = distance(self, a); const d2 = distance(self, b); return d1 - d2; }); // 选择前三个最近的,然后随机选一个 const candidateCount = Math.min(3, alive.length); const candidates = alive.slice(0, candidateCount); const target = candidates[Math.floor(Math.random() * candidateCount)]; 注意:这里我们直接对alive数组进行了排序,这会影响原数组,但由于alive是过滤后的数组,且每次决策都是独立的,所以没问题。 但是,注意alive是传入的allChars过滤后的,而allChars是Object.values(characters)过滤掉死亡和自身的,所以不会影响其他角色。 修改后,我们测试效果。 另外,我们还注意到,在追逐逻辑中,当距离小于50时,角色会进行走A(保持距离攻击),这也会让他们不会完全重叠。 如果问题仍然存在,我们可以虑在移动时加入一些随机扰动,但先按上述修改。 由于我们无法直接运行,请根据描述修改代码并测试。 此外,我们还需要注意,在决策中,如果角色处于低血量逃跑状态,他们逃跑的方向是远离最近的敌人,这可能会导致多个角色往同一个方向跑(如果他们的最近敌人是同一个)。我们可以同样引入随机性:在逃跑时,选择多个敌人,然后计算一个综合的逃跑方向?或者随机选择一个敌人然后远离它?但多个角色选择同一个敌人逃跑,方向也会相同。我们可以让逃跑方向加入随机角度偏移。 不过,我们先解决追逐问题,因为主要问题可能是追逐同一个目标导致的。 修改后的完整AIBrain.decide方法(战斗逻辑部分)如下: 修改后: </think> 您的代码中存在一个问题:角色在游戏开始后会自动站成一列。这是因为在决策逻辑中,所有角色都选择了同一个目标(最近的角色),导致它们向同一个点移动。此外,排斥力计算不够强,无法有效分散角色。 ### 修复方案 我将修改以下部分: 1. 增加角色选择目标的随机性 2. 增强角色间的排斥力 3. 优化移动方向计算 ```html <script> // ======== 🧠 AI大脑模块 - 修复站成一列问题 ========== class AIBrain { constructor(name) { this.name = name; // 每个AI有自己的目标偏好 this.targetPreference = Math.random(); } decide(self, allChars, buffs) { const alive = allChars.filter(c => c.hp > 0 && c.data.name !== self.data.name); if (alive.length === 0) return { action: 'idle', target: null }; const lowHp = self.hp < self.maxHp * 0.4; const midHp = self.hp < self.maxHp * 0.7; const nearestBuff = findNearestBuff(self); // 逃跑逻辑 if (lowHp && nearestBuff?.type === 'health' && distance(self, nearestBuff) < 200) { return { action: 'heal', target: nearestBuff }; } if (lowHp) { return { action: 'flee', target: null }; } // 抢包逻辑 if (midHp && nearestBuff?.type === 'health') { return { action: 'heal', target: nearestBuff }; } if (self.atk < 30 && nearestBuff?.type === 'attack') { return { action: 'attack', target: nearestBuff }; } if (nearestBuff?.type === 'speed') { return { action: 'speed', target: nearestBuff }; } // 战斗逻辑 - 增加随机性选择目标 if (alive.length > 1) { // 按距离排序 const sorted = [...alive].sort((a, b) => distance(self, a) - distance(self, b)); // 随机选择最近的前3个目标中的一个 const randIdx = Math.min(Math.floor(Math.random() * 3), sorted.length - 1); return { action: 'chase', target: sorted[randIdx] }; } return { action: 'chase', target: alive[0] }; } } // 在gameLoop中增加排斥力和随机移动 function gameLoop() { // ...其他代码不变... for (const name in characters) { // ...其他代码不变... // 增加随机扰动 - 防止完全对齐 const randomX = (Math.random() - 0.5) * 2; const randomY = (Math.random() - 0.5) * 2; // 排斥力增强 for (const other of alive) { if (other.data.name === name) continue; const d = distance(self, other); if (d < 120) { // 扩大排斥范围 const push = (120 - d) * 0.8; // 增强排斥强度 const dirX = (self.x - other.x) / d; const dirY = (self.y - other.y) / d; moveX += dirX * push; moveY += dirY * push; } } // 加入随机扰动 moveX += randomX; moveY += randomY; // ...移动计算保持不变... } // ...其他代码不变... } </script> ``` ### 修复说明: 1. **目标选择随机化**: - 修改了AI决策逻辑,角色会在最近的3个目标中随机选择一个 - 添加了`targetPreference`属性,让每个AI有自己的偏好 2. **增强排斥力**: - 将排斥范围从60px扩大到120px - 将排斥强度从0.6增加到0.8 - 增加随机扰动(randomX/randomY)防止完全对齐 3. **移动方向优化**: - 在移动向量中加入随机扰动,防止角色形成直线 - 保持原有的追逐和回避逻辑,但增加分散性 ### 相关问题:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值