// ==UserScript== // @name 艾德尔阵图AI规划器(智能布局版) // @namespace https://idlexiuxianzhuan.cn/ // @version 7.0 // @description AI根据阵盘形状和阵纹效果,智能规划并放置阵盘阵纹,主动寻找激活布局 // @author 最中幻想 // @match https://idlexiuxianzhuan.cn/* // @grant GM_xmlhttpRequest // @grant GM_download // @grant GM_notification // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function() { 'use strict'; // ========================== 配置 ========================== let DEEPSEEK_API_KEY = GM_getValue('deepseek_key', ''); const DEEPSEEK_API_URL = 'https://api.deepseek.com/v1/chat/completions'; let allRunes = []; let recordedLayouts = []; let currentShapeFilter = '全部'; let runningAutoTest = false; let stopAutoTestFlag = false; // ========================== 日志 ========================== let logPanel; function addLog(msg, type = 'info') { const logDiv = document.createElement('div'); logDiv.style.cssText = `padding: 4px 8px; border-bottom: 1px solid #3a3a5a; font-size: 12px; color: ${type === 'error' ? '#ff8888' : (type === 'success' ? '#88ff88' : '#ddd')};`; logDiv.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`; if (logPanel) logPanel.appendChild(logDiv); logPanel.scrollTop = logPanel.scrollHeight; console.log(msg); } function createLogPanel() { const panel = document.createElement('div'); panel.id = 'array-tester-log'; panel.style.cssText = `position: fixed; top: 10px; left: 10px; right: 10px; max-height: 180px; background: rgba(0,0,0,0.85); backdrop-filter: blur(10px); border-radius: 12px; padding: 8px; overflow-y: auto; z-index: 1000000; font-family: monospace; font-size: 12px; color: #ddd; border: 1px solid #5a5a7a; pointer-events: none;`; document.body.appendChild(panel); logPanel = panel; addLog('日志面板已启动', 'success'); } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function waitForElement(selector, timeout = 10000) { const start = Date.now(); while (Date.now() - start < timeout) { const el = document.querySelector(selector); if (el) return el; await sleep(300); } return null; } async function safeClick(element, description) { if (!element) { addLog(`❌ 点击失败: ${description} - 元素不存在`, 'error'); return false; } try { element.scrollIntoView({ block: 'center' }); await sleep(100); element.click(); addLog(`✅ 点击: ${description}`, 'success'); await sleep(600); return true; } catch (e) { addLog(`❌ 点击异常: ${description} - ${e.message}`, 'error'); return false; } } function getButtonByText(parent, text) { const btns = parent.querySelectorAll('button'); return Array.from(btns).find(btn => btn.innerText.includes(text)); } // ========================== 数据抓取(增强版,提供更多信息给AI) ========================== async function getAllRunes() { let all = []; let page = 1; while (true) { await waitForElement('.cave-pool-card:last-child .cave-pool-item.rune', 5000); const items = document.querySelectorAll('.cave-pool-card:last-child .cave-pool-item.rune'); for (let item of items) { const nameEl = item.querySelector('.cave-pool-item-name'); if (!nameEl) continue; const name = nameEl.innerText.trim(); const metaEl = item.querySelector('.cave-pool-item-meta'); const meta = metaEl ? metaEl.innerText : ''; // 从meta中提取核心效果、指向、品质浮动 let core = '聚灵', direction = '↓', qualityBonus = 0; const coreMatch = meta.match(/核心效果:([^。]+)/); if (coreMatch) core = coreMatch[1]; const dirMatch = meta.match(/指向:([←↑→↓]+)/); if (dirMatch) direction = dirMatch[1]; const qualMatch = meta.match(/品质浮动加成 \+(\d+)%/); if (qualMatch) qualityBonus = parseInt(qualMatch[1]); const placeBtn = getButtonByText(item, '选中放置'); all.push({ name, core, direction, qualityBonus, meta, element: item, placeBtn }); } addLog(`阵纹第 ${page} 页获取 ${items.length} 个,累计 ${all.length}`, 'info'); const pageNav = document.querySelector('.cave-pool-card:last-child .page-nav'); if (!pageNav) break; const nextBtn = pageNav.querySelector('button:last-child'); if (!nextBtn || nextBtn.disabled || nextBtn.innerText !== '下一页') break; if (!await safeClick(nextBtn, `翻页 ${page}`)) break; page++; await sleep(1000); } addLog(`总共获取 ${all.length} 个阵纹`, 'success'); return all; } async function applyShapeFilter(shape) { if (shape === '全部') return; const filterRow = document.querySelector('.cave-pool-card:first-child .cave-rune-filter-row, .cave-pool-card:first-child label'); if (!filterRow) { addLog('未找到阵盘形状筛选下拉框', 'error'); return; } const select = filterRow.querySelector('select'); if (!select) { addLog('未找到形状选择器', 'error'); return; } select.value = shape; select.dispatchEvent(new Event('change', { bubbles: true })); await sleep(800); addLog(`已应用阵盘筛选: ${shape}`, 'success'); } async function getPlatesWithFilter(shape) { await applyShapeFilter(shape); await waitForElement('.cave-pool-card:first-child .cave-pool-item.plate', 5000); const items = document.querySelectorAll('.cave-pool-card:first-child .cave-pool-item.plate'); const plates = []; for (let item of items) { const nameEl = item.querySelector('.cave-pool-item-name'); if (!nameEl) continue; const name = nameEl.innerText.trim(); const metaEl = item.querySelector('.cave-pool-item-meta'); const meta = metaEl ? metaEl.innerText : ''; let shape = '未知', power = 0, bonuses = {}; if (meta) { const shapeMatch = meta.match(/形状:([^,]+)/); if (shapeMatch) shape = shapeMatch[1]; const powerMatch = meta.match(/每回合提供流量 (\d+)/); if (powerMatch) power = parseInt(powerMatch[1]); // 提取词条例如 "铸骨效果 +11%" "所有被指向的阵纹效果 +5%" const specificBonus = meta.match(/(聚灵|铸骨|灵锐|引流|攻伐|守御|迅行|平衡|聚气|锋芒)效果 \+(\d+)%/); if (specificBonus) bonuses[specificBonus[1]] = parseInt(specificBonus[2]); const generalMatch = meta.match(/所有被指向的阵纹效果 \+(\d+)%/); if (generalMatch) bonuses['所有'] = parseInt(generalMatch[1]); } const placeBtn = getButtonByText(item, '选中放置'); const rotateBtn = getButtonByText(item, '旋转'); plates.push({ name, shape, power, bonuses, meta, element: item, placeBtn, rotateBtn }); } addLog(`筛选后得到 ${plates.length} 个阵盘`, 'info'); return plates; } // 获取当前阵图详细状态(包括连通纹数量、触发值、属性加成等) async function getLayoutState() { const cells = document.querySelectorAll('.cave-formation-grid .cave-formation-cell'); const grid = []; for (let i = 0; i < cells.length; i++) { const cell = cells[i]; const piece = cell.querySelector('.cave-piece'); if (piece) { const type = piece.querySelector('.cave-piece-type')?.innerText; const name = piece.querySelector('.cave-piece-name')?.innerText; const meta = piece.querySelector('.cave-piece-meta')?.innerText; grid.push({ index: i, type, name, meta }); } else { grid.push(null); } } // 解析各种文本信息 let connectedCount = 0, triggerValue = 0, flowSupply = 0, flowConsume = 0, attrBonus = ''; const hints = document.querySelectorAll('.form-hint'); for (let hint of hints) { const text = hint.innerText; if (text.includes('连通阵纹')) { const match = text.match(/连通阵纹 (\d+)\/(\d+)/); if (match) connectedCount = parseInt(match[1]); const triggerMatch = text.match(/主阵触发值总计 (\d+)/); if (triggerMatch) triggerValue = parseInt(triggerMatch[1]); } if (text.includes('阵法流量')) { const supplyMatch = text.match(/供给 (\d+)/); if (supplyMatch) flowSupply = parseInt(supplyMatch[1]); const consumeMatch = text.match(/连通消耗 (\d+)/); if (consumeMatch) flowConsume = parseInt(consumeMatch[1]); } if (text.includes('总体属性加成')) attrBonus = text; } const isActivated = Array.from(document.querySelectorAll('.form-hint, .cave-formation-head, .pending-job')) .some(el => el.innerText.includes('已激活')); return { grid, connectedCount, triggerValue, flowSupply, flowConsume, attrBonus, isActivated }; } async function clearGrid() { const clearBtn = document.querySelector('.cave-formation-main .btn-xs'); if (clearBtn) return await safeClick(clearBtn, '一键清空阵图'); return false; } function getEmptyCells() { const cells = document.querySelectorAll('.cave-formation-grid .cave-formation-cell'); const empty = []; cells.forEach((cell, idx) => { if (!cell.querySelector('.cave-piece')) empty.push(idx); }); return empty; } async function placePlate(plate, cellIndex, rotations = 0) { if (!plate.placeBtn) { addLog(`❌ 阵盘 ${plate.name} 缺少放置按钮`, 'error'); return false; } if (!await safeClick(plate.placeBtn, `选中阵盘 ${plate.name}`)) return false; // 执行旋转 for (let i = 0; i < rotations; i++) { if (plate.rotateBtn) await safeClick(plate.rotateBtn, `旋转阵盘 ${plate.name}`); else break; } const cells = document.querySelectorAll('.cave-formation-grid .cave-formation-cell'); if (!cells[cellIndex]) return false; return await safeClick(cells[cellIndex], `放置到格子 ${cellIndex}`); } async function placeRune(rune, cellIndex) { if (!rune.placeBtn) { addLog(`❌ 阵纹 ${rune.name} 缺少放置按钮`, 'error'); return false; } if (!await safeClick(rune.placeBtn, `选中阵纹 ${rune.name}`)) return false; const cells = document.querySelectorAll('.cave-formation-grid .cave-formation-cell'); if (!cells[cellIndex]) return false; return await safeClick(cells[cellIndex], `放置到格子 ${cellIndex}`); } async function waitForActivation(timeout = 8000) { const start = Date.now(); while (Date.now() - start < timeout) { const toast = document.querySelector('.toast'); if (toast && toast.innerText.includes('主阵已激活')) return true; const activeHint = Array.from(document.querySelectorAll('.form-hint, .cave-formation-head, .pending-job')) .some(el => el.innerText.includes('已激活')); if (activeHint) return true; await sleep(300); } return false; } function recordSuccess() { const layout = captureCurrentLayout(); recordedLayouts.push(layout); addLog(`🎉 记录激活布局 #${recordedLayouts.length}`, 'success'); } function captureCurrentLayout() { const cells = document.querySelectorAll('.cave-formation-grid .cave-formation-cell'); const grid = []; cells.forEach(cell => { const piece = cell.querySelector('.cave-piece'); if (piece) { const type = piece.querySelector('.cave-piece-type')?.innerText; const name = piece.querySelector('.cave-piece-name')?.innerText; const meta = piece.querySelector('.cave-piece-meta')?.innerText; grid.push({ type, name, meta }); } else { grid.push(null); } }); let attrText = '', statusText = ''; document.querySelectorAll('.form-hint').forEach(hint => { if (hint.innerText.includes('总体属性加成')) attrText = hint.innerText; if (hint.innerText.includes('连通阵纹')) statusText = hint.innerText; }); return { grid, attrText, statusText, timestamp: new Date().toISOString() }; } // ========================== AI 智能规划核心 ========================== const toolDefinitions = [ { name: 'clear_grid', description: '清空阵图', parameters: {} }, { name: 'filter_plates', description: '筛选阵盘形状', parameters: { shape: { type: 'string', enum: ['全部','长二','T五','X五'] } } }, { name: 'get_plates', description: '获取当前可用阵盘列表(含形状、提供流量、词条)', parameters: {} }, { name: 'get_runes', description: '获取全部阵纹列表(含核心效果、指向、品质浮动)', parameters: {} }, { name: 'place_plate', description: '放置阵盘到指定格子(0-24),可指定旋转次数(0-3)', parameters: { plateIndex: { type: 'integer' }, cellIndex: { type: 'integer' }, rotations: { type: 'integer', default: 0 } } }, { name: 'place_rune', description: '放置阵纹到指定格子', parameters: { runeIndex: { type: 'integer' }, cellIndex: { type: 'integer' } } }, { name: 'get_layout_state', description: '获取当前阵图详细状态(连通数、触发值、是否激活等)', parameters: {} }, { name: 'record_current_layout', description: '将当前布局记录为成功激活布局', parameters: {} }, { name: 'export_records', description: '导出所有记录', parameters: {} }, { name: 'start_auto_test', description: '运行随机自动测试(备用方式)', parameters: {} }, { name: 'stop_auto_test', description: '停止随机测试', parameters: {} }, { name: 'wait_for_activation', description: '等待主阵激活(最多8秒)', parameters: {} } ]; const toolsForAPI = toolDefinitions.map(tool => ({ type: 'function', function: { name: tool.name, description: tool.description, parameters: { type: 'object', properties: Object.fromEntries(Object.entries(tool.parameters || {}).map(([k, v]) => [k, { type: v.type, enum: v.enum }])), required: [] } } })); async function callTool(toolName, args) { addLog(`🔧 执行工具: ${toolName} ${JSON.stringify(args)}`, 'info'); try { switch (toolName) { case 'clear_grid': await clearGrid(); return { ok: true }; case 'filter_plates': currentShapeFilter = args.shape; await applyShapeFilter(currentShapeFilter); return { ok: true, shape: currentShapeFilter }; case 'get_plates': { const plates = await getPlatesWithFilter(currentShapeFilter); return { ok: true, plates: plates.map((p,i) => ({ index: i, name: p.name, shape: p.shape, power: p.power, bonuses: p.bonuses })) }; } case 'get_runes': { if (allRunes.length === 0) allRunes = await getAllRunes(); return { ok: true, runes: allRunes.map((r,i) => ({ index: i, name: r.name, core: r.core, direction: r.direction, qualityBonus: r.qualityBonus })) }; } case 'place_plate': { const plates = await getPlatesWithFilter(currentShapeFilter); const plate = plates[args.plateIndex]; if (!plate) return { ok: false, error: '无效的阵盘索引' }; const success = await placePlate(plate, args.cellIndex, args.rotations || 0); return { ok: success }; } case 'place_rune': { if (allRunes.length === 0) allRunes = await getAllRunes(); const rune = allRunes[args.runeIndex]; if (!rune) return { ok: false, error: '无效的阵纹索引' }; const success = await placeRune(rune, args.cellIndex); return { ok: success }; } case 'get_layout_state': { const state = await getLayoutState(); return { ok: true, state }; } case 'record_current_layout': recordSuccess(); return { ok: true }; case 'export_records': exportRecords(); return { ok: true }; case 'start_auto_test': startAutoTest(); return { ok: true }; case 'stop_auto_test': stopAutoTest(); return { ok: true }; case 'wait_for_activation': { const activated = await waitForActivation(8000); return { ok: true, activated }; } default: return { ok: false, error: '未知工具' }; } } catch (e) { addLog(`工具异常: ${e.message}`, 'error'); return { ok: false, error: e.message }; } } async function askAIWithLoop(userMessage) { if (!DEEPSEEK_API_KEY) { addLog('请先设置DeepSeek API密钥', 'error'); return 'API密钥未配置'; } let conversation = [ { role: 'system', content: `你是艾德尔修仙传的阵图布局专家。你的目标是:通过智能分析阵盘形状、阵纹效果、连通规则,主动规划并放置阵盘和阵纹,以达到“主阵已激活”状态,并记录成功的布局。你拥有以下工具可以使用。每次任务执行中,你可以连续调用多个工具,并根据结果调整策略。当任务完成时,你必须返回一个JSON对象,格式为 { "done": true, "message": "总结信息" }。不要在未完成时返回done。 具体规划思路: 1. 首先调用 get_plates 和 get_runes 获取所有可用部件。 2. 分析阵盘的形状(长二、T五、X五等)和提供的流量,以及词条加成。 3. 分析阵纹的核心效果(聚灵、铸骨等)、指向和品质浮动。 4. 规划一个5x5网格的布局:首先放置一个阵盘(选择合适的位置和旋转),然后在周围空格子放置阵纹,使得阵纹的指向能够连接到阵盘,并且尽可能提高触发值。 5. 放置过程中可以多次调用 get_layout_state 检查连通数和触发值。 6. 如果触发了激活,立即调用 record_current_layout 记录,然后可以 clear_grid 开始下一轮尝试,或结束任务。 7. 尽量使用较少的尝试次数找到激活方案,避免随机穷举。 8. 你可以自主决定尝试不同的阵盘和阵纹组合。` } ]; conversation.push({ role: 'user', content: userMessage }); let finalAnswer = ''; let loopCount = 0; const MAX_LOOPS = 30; while (loopCount++ < MAX_LOOPS) { const resp = await new Promise((resolve) => { GM_xmlhttpRequest({ method: 'POST', url: DEEPSEEK_API_URL, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${DEEPSEEK_API_KEY}` }, data: JSON.stringify({ model: 'deepseek-chat', messages: conversation, tools: toolsForAPI, tool_choice: 'auto', temperature: 0.3 }), onload: (r) => resolve(JSON.parse(r.responseText)), onerror: (err) => resolve({ error: true, message: err }) }); }); if (resp.error) { addLog('API请求失败', 'error'); break; } const choice = resp.choices[0]; const message = choice.message; conversation.push(message); if (message.tool_calls && message.tool_calls.length) { const toolResults = []; for (let tc of message.tool_calls) { const toolName = tc.function.name; const args = JSON.parse(tc.function.arguments); const result = await callTool(toolName, args); toolResults.push({ tool_call_id: tc.id, role: 'tool', content: JSON.stringify(result) }); } conversation.push(...toolResults); continue; } else { const content = message.content; try { const jsonMatch = content.match(/\{[\s\S]*"done"\s*:\s*true[\s\S]*\}/); if (jsonMatch) { const parsed = JSON.parse(jsonMatch[0]); if (parsed.done === true) { finalAnswer = parsed.message || '任务完成'; addLog(`AI 完成任务: ${finalAnswer}`, 'success'); break; } } } catch(e) {} finalAnswer = content || '无明确完成标志'; break; } } if (loopCount >= MAX_LOOPS) finalAnswer = '达到最大循环次数,任务可能未完成。'; return finalAnswer; } // ========================== UI 组件(与之前类似) ========================== let chatPanel, chatLogDiv, chatInput; function appendChatMessage(role, text) { const div = document.createElement('div'); div.style.cssText = `padding: 6px 10px; border-radius: 12px; max-width: 85%; margin: 4px 0; ${role === 'user' ? 'align-self: flex-end; background: #2c6e2c;' : 'align-self: flex-start; background: #2a2a3a;'}`; div.textContent = text; chatLogDiv.appendChild(div); chatLogDiv.scrollTop = chatLogDiv.scrollHeight; } async function onSendMessage() { const msg = chatInput.value.trim(); if (!msg) return; appendChatMessage('user', msg); chatInput.value = ''; const reply = await askAIWithLoop(msg); appendChatMessage('ai', reply); } function createChatPanel() { const panel = document.createElement('div'); panel.id = 'ai-chat-panel'; panel.style.cssText = ` position: fixed; bottom: 20px; right: 20px; width: 360px; max-width: 90vw; background: #1e1e2fe6; backdrop-filter: blur(12px); border-radius: 20px; border: 1px solid #6a6a8a; display: flex; flex-direction: column; z-index: 1000001; font-family: system-ui; box-shadow: 0 4px 20px black; `; const header = document.createElement('div'); header.style.cssText = 'padding: 8px 12px; background: #2a2a3a; border-radius: 20px 20px 0 0; cursor: move; display: flex; justify-content: space-between;'; header.innerHTML = '🧠 AI规划器 (DeepSeek)'; const logDiv = document.createElement('div'); logDiv.style.cssText = 'height: 300px; overflow-y: auto; padding: 8px; display: flex; flex-direction: column; gap: 6px; font-size: 13px;'; const inputDiv = document.createElement('div'); inputDiv.style.cssText = 'display: flex; padding: 8px; gap: 8px; border-top: 1px solid #4a4a6a;'; chatInput = document.createElement('input'); chatInput.style.cssText = 'flex:1; background:#2a2a3a; border:none; border-radius:20px; padding:8px 12px; color:white;'; chatInput.placeholder = '例如:请规划一个能激活主阵的布局,找到后记录并导出。'; const sendBtn = document.createElement('button'); sendBtn.textContent = '发送'; sendBtn.style.cssText = 'background:#2c6e2c; border:none; border-radius:20px; padding:8px 16px; color:white; cursor:pointer;'; sendBtn.onclick = onSendMessage; inputDiv.appendChild(chatInput); inputDiv.appendChild(sendBtn); panel.appendChild(header); panel.appendChild(logDiv); panel.appendChild(inputDiv); document.body.appendChild(panel); chatLogDiv = logDiv; header.querySelector('#close-chat').onclick = () => panel.style.display = 'none'; // 拖拽 let offsetX, offsetY, isDragging = false; header.addEventListener('mousedown', (e) => { if (e.target === header.querySelector('#close-chat')) return; isDragging = true; offsetX = e.clientX - panel.offsetLeft; offsetY = e.clientY - panel.offsetTop; panel.style.position = 'fixed'; panel.style.bottom = 'auto'; panel.style.top = panel.offsetTop + 'px'; panel.style.right = 'auto'; panel.style.left = panel.offsetLeft + 'px'; e.preventDefault(); }); window.addEventListener('mousemove', (e) => { if (!isDragging) return; panel.style.left = (e.clientX - offsetX) + 'px'; panel.style.top = (e.clientY - offsetY) + 'px'; }); window.addEventListener('mouseup', () => { isDragging = false; }); appendChatMessage('ai', '你好!我是AI阵图规划助手。我会智能分析阵盘和阵纹,主动尝试布局以激活主阵。你可以给我一个目标,例如:“找到一种激活布局并记录”。'); } // 辅助手动测试功能(保留但不必须) async function startAutoTest() { if (runningAutoTest) return; runningAutoTest = true; stopAutoTestFlag = false; if (allRunes.length === 0) allRunes = await getAllRunes(); let attempt = 0; while (!stopAutoTestFlag && attempt < 100) { attempt++; const plates = await getPlatesWithFilter(currentShapeFilter); if (plates.length === 0) break; await clearGrid(); // 随机尝试(纯机械,备用) const plate = plates[Math.floor(Math.random() * plates.length)]; const empty = getEmptyCells(); if (empty.length) await placePlate(plate, empty[Math.floor(Math.random() * empty.length)]); for (let i = 0; i < 12; i++) { if (await waitForActivation(1500)) { recordSuccess(); break; } const emptyCells = getEmptyCells(); if (emptyCells.length === 0) break; const rune = allRunes[Math.floor(Math.random() * allRunes.length)]; await placeRune(rune, emptyCells[Math.floor(Math.random() * emptyCells.length)]); } await sleep(800); } runningAutoTest = false; } function stopAutoTest() { stopAutoTestFlag = true; } function exportRecords() { if (!recordedLayouts.length) { alert('暂无记录'); return; } const data = JSON.stringify(recordedLayouts, null, 2); const blob = new Blob([data], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.download = `array_ai_layouts_${Date.now()}.json`; a.href = url; a.click(); URL.revokeObjectURL(url); addLog('已导出记录', 'success'); } function clearRecords() { recordedLayouts = []; addLog('记录已清空', 'info'); } function showApiKeyDialog() { const key = prompt('DeepSeek API Key:', DEEPSEEK_API_KEY); if (key !== null) { DEEPSEEK_API_KEY = key; GM_setValue('deepseek_key', key); addLog('API密钥已保存', 'success'); } } function createMainPanel() { const panel = document.createElement('div'); panel.id = 'auto-tester-controls'; panel.style.cssText = ` position: fixed; top: 220px; left: 10px; background: #1e1e2fe6; backdrop-filter: blur(8px); border-radius: 16px; padding: 12px; display: flex; flex-direction: column; gap: 8px; z-index: 1000000; border: 1px solid #6a6a8a; font-family: system-ui; min-width: 170px; `; const shapeRow = document.createElement('div'); shapeRow.style.cssText = 'display:flex; gap:6px; align-items:center; color:white; font-size:13px;'; shapeRow.innerText = '阵盘形状: '; const shapeSelect = document.createElement('select'); shapeSelect.style.cssText = 'background:#2a2a3a; color:white; border-radius:16px; padding:4px 8px;'; ['全部','长二','T五','X五'].forEach(s => { const opt = document.createElement('option'); opt.value = s; opt.text = s; shapeSelect.appendChild(opt); }); shapeSelect.value = currentShapeFilter; shapeSelect.onchange = (e) => { currentShapeFilter = e.target.value; addLog(`形状改为 ${currentShapeFilter}`, 'info'); }; shapeRow.appendChild(shapeSelect); panel.appendChild(shapeRow); const btnStyle = 'background:#2c6e2c; border:none; color:white; padding:8px; border-radius:30px; cursor:pointer; margin:2px 0;'; const startBtn = document.createElement('button'); startBtn.textContent = '⏵ 随机测试(备用)'; startBtn.style.cssText = btnStyle; startBtn.onclick = startAutoTest; const stopBtn = document.createElement('button'); stopBtn.textContent = '⏹ 停止随机'; stopBtn.style.cssText = btnStyle.replace('#2c6e2c','#8b3c3c'); stopBtn.onclick = stopAutoTest; const exportBtn = document.createElement('button'); exportBtn.textContent = '💾 导出记录'; exportBtn.style.cssText = btnStyle.replace('#2c6e2c','#2980b9'); exportBtn.onclick = exportRecords; const clearBtn = document.createElement('button'); clearBtn.textContent = '🗑️ 清空记录'; clearBtn.style.cssText = btnStyle.replace('#2c6e2c','#7f8c8d'); clearBtn.onclick = clearRecords; const apiBtn = document.createElement('button'); apiBtn.textContent = '🔑 设置API密钥'; apiBtn.style.cssText = btnStyle.replace('#2c6e2c','#9b59b6'); apiBtn.onclick = showApiKeyDialog; const chatToggle = document.createElement('button'); chatToggle.textContent = '💬 显示/隐藏AI'; chatToggle.style.cssText = btnStyle.replace('#2c6e2c','#34495e'); chatToggle.onclick = () => { if(chatPanel) chatPanel.style.display = chatPanel.style.display === 'none' ? 'flex' : 'none'; }; panel.append(startBtn, stopBtn, exportBtn, clearBtn, apiBtn, chatToggle); document.body.appendChild(panel); } function init() { createLogPanel(); createMainPanel(); createChatPanel(); addLog('AI智能阵图规划器已加载,设置DeepSeek API密钥后即可使用。', 'success'); } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init(); })();