{"repo": "abiscuitttt/fdty-auto", "files": {"fdty_top.js": {"c31f67ff234e6311d40534fa3c376b6183f6e917": {"t": 1785746006.1875212, "n": 10116, "h": "066ec9da1810e041", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n var SYSTEM_PROMPT = [\n '# 角色',\n '你是复旦大学体育理论考试判题员。运用运动科学常识与体育规则作答。',\n '',\n '# 知识领域',\n '题目涵盖十个领域:体育概论、健康体能、体质评价、运动保健、田径、球类、游泳与救生、形体运动、民族传统体育与武术、击剑。',\n '',\n '# 答题原则',\n '- 注意题干中的否定词(不/非/无/没有)和双重否定——切勿答反',\n '- 含\"完全\"\"绝不\"\"所有\"\"从不\"等绝对化用词的判断题通常为错,但公认事实和规则条文除外',\n '- 必须为每道题输出答案,不确定时基于常识推理给出最佳猜测,不得空题',\n '',\n '# 输出',\n '输出纯JSON对象,键为题号,值为答案。',\n '判断:\"对\"/\"错\" 单选:\"A\"/\"B\"/\"C\"/\"D\"'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY, '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n var unresolved = [];\n var phase2Ok = 0;\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i]));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n return concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n })\n .catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n startVoting();\n})();\n// test\n"}, "9f3f3ad61955beb63c69e9ab80ccb61da57b6058": {"t": 1785758719.147477, "n": 10116, "h": "066ec9da1810e041", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n var SYSTEM_PROMPT = [\n '# 角色',\n '你是复旦大学体育理论考试判题员。运用运动科学常识与体育规则作答。',\n '',\n '# 知识领域',\n '题目涵盖十个领域:体育概论、健康体能、体质评价、运动保健、田径、球类、游泳与救生、形体运动、民族传统体育与武术、击剑。',\n '',\n '# 答题原则',\n '- 注意题干中的否定词(不/非/无/没有)和双重否定——切勿答反',\n '- 含\"完全\"\"绝不\"\"所有\"\"从不\"等绝对化用词的判断题通常为错,但公认事实和规则条文除外',\n '- 必须为每道题输出答案,不确定时基于常识推理给出最佳猜测,不得空题',\n '',\n '# 输出',\n '输出纯JSON对象,键为题号,值为答案。',\n '判断:\"对\"/\"错\" 单选:\"A\"/\"B\"/\"C\"/\"D\"'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY, '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n var unresolved = [];\n var phase2Ok = 0;\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i]));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n return concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n })\n .catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n startVoting();\n})();\n// test\n"}, "7005851742fd4a7fa46252a384ddc95035508da0": {"t": 1785758720.5512323, "n": 10108, "h": "d4bbf9196fc3eae0", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n var SYSTEM_PROMPT = [\n '# 角色',\n '你是复旦大学体育理论考试判题员。运用运动科学常识与体育规则作答。',\n '',\n '# 知识领域',\n '题目涵盖十个领域:体育概论、健康体能、体质评价、运动保健、田径、球类、游泳与救生、形体运动、民族传统体育与武术、击剑。',\n '',\n '# 答题原则',\n '- 注意题干中的否定词(不/非/无/没有)和双重否定——切勿答反',\n '- 含\"完全\"\"绝不\"\"所有\"\"从不\"等绝对化用词的判断题通常为错,但公认事实和规则条文除外',\n '- 必须为每道题输出答案,不确定时基于常识推理给出最佳猜测,不得空题',\n '',\n '# 输出',\n '输出纯JSON对象,键为题号,值为答案。',\n '判断:\"对\"/\"错\" 单选:\"A\"/\"B\"/\"C\"/\"D\"'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY, '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n var unresolved = [];\n var phase2Ok = 0;\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i]));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n return concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n })\n .catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n startVoting();\n})();\n"}, "b73733d47dbd73de18116ca44b04ba9197b57f22": {"t": 1785758722.0091326, "n": 10112, "h": "efcc20ad9049a851", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n var SYSTEM_PROMPT = [\n '# 角色',\n '你是复旦大学体育理论考试判题员。运用运动科学常识与体育规则作答。',\n '',\n '# 知识领域',\n '题目涵盖十个领域:体育概论、健康体能、体质评价、运动保健、田径、球类、游泳与救生、形体运动、民族传统体育与武术、击剑。',\n '',\n '# 答题原则',\n '- 注意题干中的否定词(不/非/无/没有)和双重否定——切勿答反',\n '- 含\"完全\"\"绝不\"\"所有\"\"从不\"等绝对化用词的判断题通常为错,但公认事实和规则条文除外',\n '- 必须为每道题输出答案,不确定时基于常识推理给出最佳猜测,不得空题',\n '',\n '# 输出',\n '输出纯JSON对象,键为题号,值为答案。',\n '判断:\"对\"/\"错\" 单选:\"A\"/\"B\"/\"C\"/\"D\"'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY, '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i]));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n return concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n })\n .catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n startVoting();\n})();\n"}, "1f12866ca65f55c111a5281fa1019867f6d3b352": {"t": 1785758723.2814238, "n": 10941, "h": "03a66299c220852a", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n var SYSTEM_PROMPT = [\n '# 角色',\n '复旦大学体育理论考试专业判题员。根据运动科学常识与体育规则,为每道题目给出最准确的答案。',\n '',\n '# 知识覆盖领域',\n '- 体育概论:体育概念与分类、体育功能、奥林匹克精神、高等学校体育、体育锻炼与心理健康/社会适应',\n '- 健康体能:体能要素(心肺耐力/肌肉力量与耐力/柔韧性/身体成分)、健康体能锻炼原则',\n '- 体质健康:健康与体质概念、亚健康状态、体质评价方法(BMI/体脂率/最大吸氧量/12分钟跑)、营养膳食、运动性疲劳与恢复',\n '- 运动保健:运动生理反应(肌肉酸痛/极点与第二次呼吸/心血管适应)、运动损伤预防与处理(扭挫伤/肌肉拉伤/痉挛/低血糖/晕厥)、运动处方',\n '- 田径:走跑跳投项目群、短跑与中长跑技术、立定跳远、体质健康测试',\n '- 球类:篮球/排球/足球/羽毛球/网球/乒乓球/棒垒球/手球/气排球/高尔夫——各项目规则、基本技术、基本战术',\n '- 游泳与救生:游泳技术(蛙泳/自由泳/仰泳/蝶泳)、专项素质训练、水中救生方法',\n '- 形体运动:健美操/艺术体操/体育舞蹈/健美——基本技术、裁判规则、形体评价标准',\n '- 民族传统体育:长拳/太极拳/咏春拳/剑术/木兰拳(扇)/龙舞/八段锦/防身术/射艺——技术特点与功法',\n '- 击剑:击剑起源与发展、基本技术(花剑/重剑/佩剑)、战术类型、比赛规则与场地器材',\n '',\n '# 判题原则',\n '1. 常识推理:运用运动科学常识与体育规则进行推理判断;涉及具体数字、年份、人名的题目基于最合理的常识推理作答',\n '2. 绝对化判断:题干中出现\"完全\"\"绝不\"\"一定\"\"所有\"\"从不\"等绝对化用词时,通常为错误表述。但公认事实类绝对陈述(如\"奥林匹克格言是更快更高更强\")属于正确——需结合语境判断',\n '3. 否定识别:识别题干中的否定词(\"不\"\"非\"\"无\"\"没有\")和双重否定结构,避免因误读否定而答反',\n '4. 规则标准:运动规则类题目以最新国际通用比赛规则和中国现行规则为准',\n '5. 必出答案:遇到不确定的题目,基于最合理推理给出答案,不得输出空值、\"不确定\"或跳过任何题目',\n '',\n '# 输出格式',\n '仅输出JSON对象,键为题号(数字字符串),值为答案字符串。',\n '判断题:\"对\"或\"错\";单选题:大写字母A/B/C/D。',\n '示例: {\"1\":\"对\",\"2\":\"错\",\"3\":\"C\"}',\n '错误示例(禁止):{\"1\":\"正确\"} ← 必须用\"对\"而非\"正确\";{\"1\":\"True\"} ← 必须用中文\"对\";{\"1\":\"c\"} ← 必须用大写字母'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY, '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i]));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n return concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n })\n .catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n startVoting();\n})();\n"}, "4c47a868d5e7691bb62622884182c7fdcfc043b9": {"t": 1785758724.6890626, "n": 14628, "h": "30434213d09e9355", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n var SYSTEM_PROMPT = [\n '你是一位复旦大学体育理论考试专业答题助手。',\n '',\n '## 知识范围',\n '运动生理学、体能训练理论、运动规则(篮球/排球/足球/田径/游泳/击剑等)、健康体能与体质评价、运动损伤预防、民族传统体育。',\n '',\n '## 答题步骤',\n '1. 仔细阅读题目,识别考点属于哪个知识领域',\n '2. 结合运动科学常识和体育规则进行推理判断',\n '3. 检查题干中是否有绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),该类表述通常为错误',\n '4. 涉及具体年份、数据、百分比的事实题,严格对照已知知识点',\n '5. 注意否定词和双重否定,避免被题干迷惑',\n '6. 给出最终答案',\n '',\n '## 输出格式',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,不要markdown包裹,不要解释。',\n '示例: {\"1\":\"对\",\"2\":\"错\",\"3\":\"C\"}'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '\"\"\"章节大纲\"\"\"\\n' + buildChapterIndex() +\n '\\n\\n\"\"\"题目列表\"\"\"\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: [\n '你是大学体育理论考试出题分析师。任务:根据题目内容,判断每道题出自教材的哪个章节。',\n '',\n '## 步骤',\n '1. 阅读题目,识别其考查的知识点',\n '2. 在章节大纲中匹配最相关的1-2个章节',\n '3. 输出关联结果',\n '',\n '## 规则',\n '- 每道题标记1-2个最相关的章节文件名',\n '- 只输出JSON,不要任何解释或markdown',\n '- 格式: {\"题号\":[\"文件名1.md\",\"文件名2.md\"]}',\n '- 示例: {\"1\":[\"第一章_体育概论与体育锻炼.md\"],\"2\":[\"第六章_球类运动.md\",\"第五章_田径.md\"]}'\n ].join('\\n') },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n var fullChapters = {};\n\n function getKnowledge(q) {\n if (!relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var text = fullChapters[id];\n if (text) parts.push(text.substring(0, 4000));\n });\n return parts.length ? '\"\"\"教材参考\"\"\"\\n' + parts.join('\\n\\n---\\n\\n') + '\\n\"\"\"教材参考结束\"\"\"\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n return concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n var ctx = getKnowledge(q);\n\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n })\n .catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n function loadFullChapters(map) {\n if (!map) return Promise.resolve();\n var ids = {};\n Object.keys(map).forEach(function(k) {\n (map[k] || []).forEach(function(id) { ids[id] = true; });\n });\n var needed = Object.keys(ids);\n if (!needed.length) return Promise.resolve();\n progress('加载教材章节...');\n return Promise.all(needed.map(function(id) {\n var url = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters/' + id;\n return fetch(url).then(function(r) { return r.text(); }).then(function(t) {\n fullChapters[id] = t;\n }).catch(function() {});\n })).then(function() {\n log('加载 ' + Object.keys(fullChapters).length + ' 章教材内容');\n });\n }\n\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n return loadFullChapters(map);\n }).then(function() {\n clearProgress();\n startVoting();\n });\n})();\n"}, "6bc35071159a1700bb29fa7ca77659d55db67a05": {"t": 1785758726.0289295, "n": 14627, "h": "e7f9ac9df82e96e3", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n var SYSTEM_PROMPT = [\n '你是一位复旦大学体育理论考试专业答题助手。',\n '',\n '## 知识范围',\n '运动生理学、体能训练理论、运动规则(篮球/排球/足球/田径/游泳/击剑等)、健康体能与体质评价、运动损伤预防、民族传统体育。',\n '',\n '## 答题步骤',\n '1. 仔细阅读题目,识别考点属于哪个知识领域',\n '2. 结合运动科学常识和体育规则进行推理判断',\n '3. 检查题干中是否有绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),该类表述通常为错误',\n '4. 涉及具体年份、数据、百分比的事实题,严格对照已知知识点',\n '5. 注意否定词和双重否定,避免被题干迷惑',\n '6. 给出最终答案',\n '',\n '## 输出格式',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,不要markdown包裹,不要解释。',\n '示例: {\"1\":\"对\",\"2\":\"错\",\"3\":\"C\"}'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '\"\"\"章节大纲\"\"\"\\n' + buildChapterIndex() +\n '\\n\\n\"\"\"题目列表\"\"\"\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: [\n '你是大学体育理论考试出题分析师。任务:根据题目内容,判断每道题出自教材的哪个章节。',\n '',\n '## 步骤',\n '1. 阅读题目,识别其考查的知识点',\n '2. 在章节大纲中匹配最相关的1-2个章节',\n '3. 输出关联结果',\n '',\n '## 规则',\n '- 每道题标记1-2个最相关的章节文件名',\n '- 只输出JSON,不要任何解释或markdown',\n '- 格式: {\"题号\":[\"文件名1.md\",\"文件名2.md\"]}',\n '- 示例: {\"1\":[\"第一章_体育概论与体育锻炼.md\"],\"2\":[\"第六章_球类运动.md\",\"第五章_田径.md\"]}'\n ].join('\\n') },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n var fullChapters = {};\n\n function getKnowledge(q) {\n if (!relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var text = fullChapters[id];\n if (text) parts.push(text.substring(0, 4000));\n });\n return parts.length ? '\"\"\"教材参考\"\"\"\\n' + parts.join('\\n\\n---\\n\\n') + '\\n\"\"\"教材参考结束\"\"\"\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n var ctx = getKnowledge(q);\n\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n function loadFullChapters(map) {\n if (!map) return Promise.resolve();\n var ids = {};\n Object.keys(map).forEach(function(k) {\n (map[k] || []).forEach(function(id) { ids[id] = true; });\n });\n var needed = Object.keys(ids);\n if (!needed.length) return Promise.resolve();\n progress('加载教材章节...');\n return Promise.all(needed.map(function(id) {\n var url = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters/' + id;\n return fetch(url).then(function(r) { return r.text(); }).then(function(t) {\n fullChapters[id] = t;\n }).catch(function() {});\n })).then(function() {\n log('加载 ' + Object.keys(fullChapters).length + ' 章教材内容');\n });\n }\n\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n return loadFullChapters(map);\n }).then(function() {\n clearProgress();\n startVoting();\n });\n})();\n"}, "cac66caefde6cd89666ff25d01a3950301e316f3": {"t": 1785758727.331913, "n": 15677, "h": "d72dea3013cd31d7", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var ZHIPU_KEY = window.__ZHIPU_KEY || '';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n function webSearch(query) {\n if (!ZHIPU_KEY) return Promise.resolve([]);\n return fetch('https://open.bigmodel.cn/api/paas/v4/web_search', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + ZHIPU_KEY },\n body: JSON.stringify({ search_query: query, search_engine: 'search_pro_quark', search_intent: false, count: 3, content_size: 'medium' })\n }).then(function(r) { return r.json(); }).then(function(d) {\n return (d.search_result || []).map(function(r) { return r.title + '\\n ' + r.content; });\n }).catch(function() { return []; });\n }\n\n var SYSTEM_PROMPT = [\n '你是一位复旦大学体育理论考试专业答题助手。',\n '',\n '## 知识范围',\n '运动生理学、体能训练理论、运动规则(篮球/排球/足球/田径/游泳/击剑等)、健康体能与体质评价、运动损伤预防、民族传统体育。',\n '',\n '## 答题步骤',\n '1. 仔细阅读题目,识别考点属于哪个知识领域',\n '2. 结合运动科学常识和体育规则进行推理判断',\n '3. 检查题干中是否有绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),该类表述通常为错误',\n '4. 涉及具体年份、数据、百分比的事实题,严格对照已知知识点',\n '5. 注意否定词和双重否定,避免被题干迷惑',\n '6. 给出最终答案',\n '',\n '## 输出格式',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,不要markdown包裹,不要解释。',\n '示例: {\"1\":\"对\",\"2\":\"错\",\"3\":\"C\"}'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '\"\"\"章节大纲\"\"\"\\n' + buildChapterIndex() +\n '\\n\\n\"\"\"题目列表\"\"\"\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: [\n '你是大学体育理论考试出题分析师。任务:根据题目内容,判断每道题出自教材的哪个章节。',\n '',\n '## 步骤',\n '1. 阅读题目,识别其考查的知识点',\n '2. 在章节大纲中匹配最相关的1-2个章节',\n '3. 输出关联结果',\n '',\n '## 规则',\n '- 每道题标记1-2个最相关的章节文件名',\n '- 只输出JSON,不要任何解释或markdown',\n '- 格式: {\"题号\":[\"文件名1.md\",\"文件名2.md\"]}',\n '- 示例: {\"1\":[\"第一章_体育概论与体育锻炼.md\"],\"2\":[\"第六章_球类运动.md\",\"第五章_田径.md\"]}'\n ].join('\\n') },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n var fullChapters = {};\n\n function getKnowledge(q) {\n if (!relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var text = fullChapters[id];\n if (text) parts.push(text.substring(0, 4000));\n });\n return parts.length ? '\"\"\"教材参考\"\"\"\\n' + parts.join('\\n\\n---\\n\\n') + '\\n\"\"\"教材参考结束\"\"\"\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题搜索+仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (搜索+每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n // 先搜索,拿到结果后再投票\n var searchQuery = q.text.replace(/[^一-龥a-zA-Z0-9]/g, ' ').replace(/\\s+/g, ' ').trim().slice(0, 60);\n return webSearch(searchQuery).then(function(searchResults) {\n var searchCtx = searchResults.length ? '\"\"\"网络搜索参考\"\"\"\\n' + searchResults.join('\\n\\n') + '\\n\"\"\"搜索结束\"\"\"\\n\\n' : '';\n var ctx = getKnowledge(q) + searchCtx;\n\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n });\n\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n function loadFullChapters(map) {\n if (!map) return Promise.resolve();\n var ids = {};\n Object.keys(map).forEach(function(k) {\n (map[k] || []).forEach(function(id) { ids[id] = true; });\n });\n var needed = Object.keys(ids);\n if (!needed.length) return Promise.resolve();\n progress('加载教材章节...');\n return Promise.all(needed.map(function(id) {\n var url = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters/' + id;\n return fetch(url).then(function(r) { return r.text(); }).then(function(t) {\n fullChapters[id] = t;\n }).catch(function() {});\n })).then(function() {\n log('加载 ' + Object.keys(fullChapters).length + ' 章教材内容');\n });\n }\n\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n return loadFullChapters(map);\n }).then(function() {\n clearProgress();\n startVoting();\n });\n})();\n"}, "931dd2759bfeac56b630f7be01d1b54869d8721e": {"t": 1785758728.6646147, "n": 15032, "h": "1ede4c2151c95d97", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var ZHIPU_KEY = window.__ZHIPU_KEY || '';\n\n function webSearch(query) {\n if (!ZHIPU_KEY) return Promise.resolve([]);\n return fetch('https://open.bigmodel.cn/api/paas/v4/web_search', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + ZHIPU_KEY },\n body: JSON.stringify({ search_query: query, search_engine: 'search_pro_quark', search_intent: false, count: 3, content_size: 'medium' })\n }).then(function(r) { return r.json(); }).then(function(d) {\n return (d.search_result || []).map(function(r) { return r.title + '\\n ' + r.content; });\n }).catch(function() { return []; });\n }\n\n var SYSTEM_PROMPT = [\n '你是一位复旦大学体育理论考试专业答题助手。',\n '',\n '## 知识范围',\n '运动生理学、体能训练理论、运动规则(篮球/排球/足球/田径/游泳/击剑等)、健康体能与体质评价、运动损伤预防、民族传统体育。',\n '',\n '## 答题步骤',\n '1. 仔细阅读题目,识别考点属于哪个知识领域',\n '2. 结合运动科学常识和体育规则进行推理判断',\n '3. 检查题干中是否有绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),该类表述通常为错误',\n '4. 涉及具体年份、数据、百分比的事实题,严格对照已知知识点',\n '5. 注意否定词和双重否定,避免被题干迷惑',\n '6. 给出最终答案',\n '',\n '## 输出格式',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,不要markdown包裹,不要解释。',\n '示例: {\"1\":\"对\",\"2\":\"错\",\"3\":\"C\"}'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '\"\"\"章节大纲\"\"\"\\n' + buildChapterIndex() +\n '\\n\\n\"\"\"题目列表\"\"\"\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: [\n '你是大学体育理论考试出题分析师。任务:根据题目内容,判断每道题出自教材的哪个章节。',\n '',\n '## 步骤',\n '1. 阅读题目,识别其考查的知识点',\n '2. 在章节大纲中匹配最相关的1-2个章节',\n '3. 输出关联结果',\n '',\n '## 规则',\n '- 每道题标记1-2个最相关的章节文件名',\n '- 只输出JSON,不要任何解释或markdown',\n '- 格式: {\"题号\":[\"文件名1.md\",\"文件名2.md\"]}',\n '- 示例: {\"1\":[\"第一章_体育概论与体育锻炼.md\"],\"2\":[\"第六章_球类运动.md\",\"第五章_田径.md\"]}'\n ].join('\\n') },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n var fullChapters = {};\n\n function getKnowledge(q) {\n if (!relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var text = fullChapters[id];\n if (text) parts.push(text.substring(0, 4000));\n });\n return parts.length ? '\"\"\"教材参考\"\"\"\\n' + parts.join('\\n\\n---\\n\\n') + '\\n\"\"\"教材参考结束\"\"\"\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 共' + (questions.length*3) + '次并发' +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n var phase1Jobs = questions.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n Promise.all(phase1Jobs).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题搜索+仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (搜索+每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n // 先搜索,拿到结果后再投票\n var searchQuery = q.text.replace(/[^一-龥a-zA-Z0-9]/g, ' ').replace(/\\s+/g, ' ').trim().slice(0, 60);\n return webSearch(searchQuery).then(function(searchResults) {\n var searchCtx = searchResults.length ? '\"\"\"网络搜索参考\"\"\"\\n' + searchResults.join('\\n\\n') + '\\n\"\"\"搜索结束\"\"\"\\n\\n' : '';\n var ctx = getKnowledge(q) + searchCtx;\n\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n function loadFullChapters(map) {\n if (!map) return Promise.resolve();\n var ids = {};\n Object.keys(map).forEach(function(k) {\n (map[k] || []).forEach(function(id) { ids[id] = true; });\n });\n var needed = Object.keys(ids);\n if (!needed.length) return Promise.resolve();\n progress('加载教材章节...');\n return Promise.all(needed.map(function(id) {\n var url = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters/' + id;\n return fetch(url).then(function(r) { return r.text(); }).then(function(t) {\n fullChapters[id] = t;\n }).catch(function() {});\n })).then(function() {\n log('加载 ' + Object.keys(fullChapters).length + ' 章教材内容');\n });\n }\n\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n return loadFullChapters(map);\n }).then(function() {\n clearProgress();\n startVoting();\n });\n})();\n"}, "8f8fcfc4101c295518ce59b190caa4acbb013ec4": {"t": 1785758730.342658, "n": 13986, "h": "ca9d78f8efd7174a", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是一位复旦大学体育理论考试专业答题助手。',\n '',\n '## 知识范围',\n '运动生理学、体能训练理论、运动规则(篮球/排球/足球/田径/游泳/击剑等)、健康体能与体质评价、运动损伤预防、民族传统体育。',\n '',\n '## 答题步骤',\n '1. 仔细阅读题目,识别考点属于哪个知识领域',\n '2. 结合运动科学常识和体育规则进行推理判断',\n '3. 检查题干中是否有绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),该类表述通常为错误',\n '4. 涉及具体年份、数据、百分比的事实题,严格对照已知知识点',\n '5. 注意否定词和双重否定,避免被题干迷惑',\n '6. 给出最终答案',\n '',\n '## 输出格式',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,不要markdown包裹,不要解释。',\n '示例: {\"1\":\"对\",\"2\":\"错\",\"3\":\"C\"}'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '\"\"\"章节大纲\"\"\"\\n' + buildChapterIndex() +\n '\\n\\n\"\"\"题目列表\"\"\"\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: [\n '你是大学体育理论考试出题分析师。任务:根据题目内容,判断每道题出自教材的哪个章节。',\n '',\n '## 步骤',\n '1. 阅读题目,识别其考查的知识点',\n '2. 在章节大纲中匹配最相关的1-2个章节',\n '3. 输出关联结果',\n '',\n '## 规则',\n '- 每道题标记1-2个最相关的章节文件名',\n '- 只输出JSON,不要任何解释或markdown',\n '- 格式: {\"题号\":[\"文件名1.md\",\"文件名2.md\"]}',\n '- 示例: {\"1\":[\"第一章_体育概论与体育锻炼.md\"],\"2\":[\"第六章_球类运动.md\",\"第五章_田径.md\"]}'\n ].join('\\n') },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n var fullChapters = {};\n\n function getKnowledge(q) {\n if (!relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var text = fullChapters[id];\n if (text) parts.push(text.substring(0, 4000));\n });\n return parts.length ? '\"\"\"教材参考\"\"\"\\n' + parts.join('\\n\\n---\\n\\n') + '\\n\"\"\"教材参考结束\"\"\"\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 共' + (questions.length*3) + '次并发' +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n var phase1Jobs = questions.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n Promise.all(phase1Jobs).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题逐题10次仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n function loadFullChapters(map) {\n if (!map) return Promise.resolve();\n var ids = {};\n Object.keys(map).forEach(function(k) {\n (map[k] || []).forEach(function(id) { ids[id] = true; });\n });\n var needed = Object.keys(ids);\n if (!needed.length) return Promise.resolve();\n progress('加载教材章节...');\n return Promise.all(needed.map(function(id) {\n var url = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters/' + id;\n return fetch(url).then(function(r) { return r.text(); }).then(function(t) {\n fullChapters[id] = t;\n }).catch(function() {});\n })).then(function() {\n log('加载 ' + Object.keys(fullChapters).length + ' 章教材内容');\n });\n }\n\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n return loadFullChapters(map);\n }).then(function() {\n clearProgress();\n startVoting();\n });\n})();\n"}, "680ea98f332ae5b95d36b7289d227d78e426a08d": {"t": 1785758731.8363116, "n": 13475, "h": "cbf2e9ba42216bc1", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '【章节大纲】\\n' + buildChapterIndex() +\n '\\n\\n【题目列表】\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: '你是大学体育理论考试出题分析师。根据每道题的内容,判断其出自哪个章节。每道题标记1-2个最相关的章节文件名。只输出JSON: {\"题号\":[\"文件名1\"]},不要解释。' },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n var fullChapters = {};\n\n function getKnowledge(q) {\n if (!relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var text = fullChapters[id];\n if (text) parts.push(text.substring(0, 4000));\n });\n return parts.length ? '【教材参考】\\n\\n' + parts.join('\\n\\n---\\n\\n') + '\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 共' + (questions.length*3) + '次并发' +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n var phase1Jobs = questions.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n Promise.all(phase1Jobs).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题逐题10次仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n function loadFullChapters(map) {\n if (!map) return Promise.resolve();\n var ids = {};\n Object.keys(map).forEach(function(k) {\n (map[k] || []).forEach(function(id) { ids[id] = true; });\n });\n var needed = Object.keys(ids);\n if (!needed.length) return Promise.resolve();\n progress('加载教材章节...');\n return Promise.all(needed.map(function(id) {\n var url = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters/' + id;\n return fetch(url).then(function(r) { return r.text(); }).then(function(t) {\n fullChapters[id] = t;\n }).catch(function() {});\n })).then(function() {\n log('加载 ' + Object.keys(fullChapters).length + ' 章教材内容');\n });\n }\n\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n return loadFullChapters(map);\n }).then(function() {\n clearProgress();\n startVoting();\n });\n})();\n"}, "163bf1633fb3e6739bd00472056e33a31659c6da": {"t": 1785758734.2570071, "n": 12802, "h": "a300bf24f593f605", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '【章节大纲】\\n' + buildChapterIndex() +\n '\\n\\n【题目列表】\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: '你是大学体育理论考试出题分析师。根据每道题的内容,判断其出自哪个章节。每道题标记1-2个最相关的章节文件名。只输出JSON: {\"题号\":[\"文件名1\"]},不要解释。' },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n function getKnowledge(q) {\n if (!chaptersData || !relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var ch = chaptersData.find(function(c) { return c.id === id; });\n if (ch) {\n parts.push('【知识点 - ' + ch.title + '】\\n' + ch.summary + '\\n包含: ' + ch.sections.join('、'));\n }\n });\n return parts.length ? parts.join('\\n\\n') + '\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 共' + (questions.length*3) + '次并发' +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n var phase1Jobs = questions.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n Promise.all(phase1Jobs).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题逐题10次仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n clearProgress();\n startVoting();\n });\n})();\n"}, "26df350083a0cc214d638dc083e58c75e1ee0408": {"t": 1785758735.43631, "n": 12818, "h": "f417d04de6a3846d", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '【章节大纲】\\n' + buildChapterIndex() +\n '\\n\\n【题目列表】\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: '你是大学体育理论考试出题分析师。根据每道题的内容,判断其出自哪个章节。每道题标记1-2个最相关的章节文件名。只输出JSON: {\"题号\":[\"文件名1\"]},不要解释。' },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify(body)\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n function getKnowledge(q) {\n if (!chaptersData || !relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var ch = chaptersData.find(function(c) { return c.id === id; });\n if (ch) {\n parts.push('【知识点 - ' + ch.title + '】\\n' + ch.summary + '\\n包含: ' + ch.sections.join('、'));\n }\n });\n return parts.length ? parts.join('\\n\\n') + '\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 共' + (questions.length*3) + '次并发' +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n var phase1Jobs = questions.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n Promise.all(phase1Jobs).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题逐题10次仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n clearProgress();\n startVoting();\n });\n})();\n"}, "2a5ed1eaad754f3ec6a12979a758a2d41b1cb0e2": {"t": 1785758736.6128995, "n": 9288, "h": "9552e085c69bd246", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 逐题投票,每题3票并发 ============\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n log(questions.length + ' 题 每题3票 共' + (questions.length*3) + '次并发...', '#1a73e8');\n\n var phase1Jobs = questions.map(function(q) {\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i]));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n Promise.all(phase1Jobs).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题逐题10次仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "d0a4569e5fa90e8b580f60fe3f3ec573d9a667fe": {"t": 1785758738.0460753, "n": 9354, "h": "f61dd6bb091a64c3", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 逐题投票,每题3票并发 ============\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n log(questions.length + ' 题 每题3票 共' + (questions.length*3) + '次并发...', '#1a73e8');\n\n var phase1Jobs = questions.map(function(q) {\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i]));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n Promise.all(phase1Jobs).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题逐题10次仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "f7d64d70ab31e884ab87d8ae9d1b458c804164a9": {"t": 1785758739.311823, "n": 9313, "h": "205391148c7038fb", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 三次投票 ============\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n var doneVotes = 0;\n var phase1Calls = temps.map(function(temp) {\n return callAPI(questions, temp).then(function(r) {\n doneVotes++;\n progress('投票 ' + doneVotes + '/3 完成');\n return r;\n });\n });\n\n Promise.all(phase1Calls).then(function(results) {\n clearProgress();\n streamClear();\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n questions.forEach(function(q) {\n var key = String(q.num);\n results.forEach(function(r) {\n votes[key].push(String(r[key] || '').trim());\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#1a73e8');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Done = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n progress('仲裁 ' + phase2Done + '/' + disputed.length + ' 题完成...');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "eb9f62b13197dad6975a543cb4631a6e01e75760": {"t": 1785758740.6806946, "n": 10517, "h": "872e5ae4b7998953", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n stream: true,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n\n var reader = r.body.getReader();\n var decoder = new TextDecoder();\n var content = '', buf = '';\n\n return reader.read().then(function pump(result) {\n if (result.done) {\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }\n\n buf += decoder.decode(result.value, { stream: true });\n var lines = buf.split('\\n');\n buf = lines.pop();\n\n for (var i = 0; i < lines.length; i++) {\n var line = lines[i].trim();\n if (!line || line === 'data: [DONE]') continue;\n if (line.indexOf('data: ') !== 0) continue;\n try {\n var chunk = JSON.parse(line.slice(6));\n var delta = chunk.choices[0].delta;\n if (delta.content) {\n content += delta.content;\n streamShow(content);\n }\n } catch(e) {}\n }\n return reader.read().then(pump);\n });\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var totalCalls = chunks.length * temps.length;\n var doneCalls = 0;\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) {\n var p = callAPI(chunk, temp).then(function(r) {\n doneCalls++;\n progress('投票 ' + doneCalls + '/' + totalCalls + ' 完成...');\n return r;\n });\n phase1Calls.push(p);\n });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n clearProgress();\n streamClear();\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#1a73e8');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Done = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n progress('仲裁 ' + phase2Done + '/' + disputed.length + ' 题完成...');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "11a327e365f0fa927acc3e92e4efb7009a85415b": {"t": 1785758741.868947, "n": 9203, "h": "a6da056c8ac25f8a", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var totalCalls = chunks.length * temps.length;\n var doneCalls = 0;\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) {\n var p = callAPI(chunk, temp).then(function(r) {\n doneCalls++;\n progress('投票 ' + doneCalls + '/' + totalCalls + ' 完成...');\n return r;\n });\n phase1Calls.push(p);\n });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n clearProgress();\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#1a73e8');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Done = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n progress('仲裁 ' + phase2Done + '/' + disputed.length + ' 题完成...');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "d268720620ce31afadfcfe879d4a30ae5704fe6e": {"t": 1785758743.1119199, "n": 9171, "h": "44aabf99c78dddb2", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'disabled' },\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var totalCalls = chunks.length * temps.length;\n var doneCalls = 0;\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) {\n var p = callAPI(chunk, temp).then(function(r) {\n doneCalls++;\n progress('投票 ' + doneCalls + '/' + totalCalls + ' 完成...');\n return r;\n });\n phase1Calls.push(p);\n });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n clearProgress();\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#1a73e8');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Done = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n progress('仲裁 ' + phase2Done + '/' + disputed.length + ' 题完成...');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "6fc8bdc251c33c5796ef48c0b4b96ea8e914b836": {"t": 1785758744.4896598, "n": 9108, "h": "bfc9a2bf4b1d0765", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var totalCalls = chunks.length * temps.length;\n var doneCalls = 0;\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) {\n var p = callAPI(chunk, temp).then(function(r) {\n doneCalls++;\n progress('投票 ' + doneCalls + '/' + totalCalls + ' 完成...');\n return r;\n });\n phase1Calls.push(p);\n });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n clearProgress();\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#1a73e8');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Done = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n progress('仲裁 ' + phase2Done + '/' + disputed.length + ' 题完成...');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "01920735ec3e8d43154cfceecfa0fae8a2a9059e": {"t": 1785758746.062085, "n": 8423, "h": "56f2883d03b7e194", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) { phase1Calls.push(callAPI(chunk, temp)); });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#1a73e8');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "ebb391b3ee47f3850df302bda610713cee1280fd": {"t": 1785758747.4245703, "n": 8374, "h": "0b5b2e9aa3bd5f0f", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:200px;pointer-events:none';\n panel_el.innerHTML = '
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) { phase1Calls.push(callAPI(chunk, temp)); });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#1a73e8');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n log(unresolved.length + ' 题无法确定 (看控制台)');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n console.warn('[fdty] [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "1d51374bb570b05d07d4cfba4764e81d96342e43": {"t": 1785758748.6907973, "n": 8634, "h": "4d2a053b16f8382c", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;left:8px;z-index:99999;background:rgba(0,0,0,0.82);color:#fff;padding:10px 14px;border-radius:8px;font:12px/1.6 monospace;min-width:220px;max-width:320px;box-shadow:0 2px 12px rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.15);pointer-events:none';\n panel_el.innerHTML = '
fdty 答题中...
';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#ccc';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found', '#ff5252'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#4fc3f7');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) { phase1Calls.push(callAPI(chunk, temp)); });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), agreed.length > disputed.length ? '#69f0ae' : '#ffab40');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#69f0ae');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#ffab40');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n log(unresolved.length + ' 题无法确定 (看控制台)', '#ff5252');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n console.warn('[fdty] [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#69f0ae');\n });\n }).catch(function(e) {\n log('错误: ' + e.message, '#ff5252');\n console.error('[fdty]', e);\n });\n})();\n"}, "d6566d96b7c99962f35e163a7435fcb8448271cc": {"t": 1785758749.9739895, "n": 8410, "h": "a3448c64ebc0dfed", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 悬浮面板 ============\n var panel_el = document.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;left:8px;z-index:99999;background:rgba(0,0,0,0.82);color:#fff;padding:10px 14px;border-radius:8px;font:12px/1.6 monospace;min-width:220px;max-width:320px;box-shadow:0 2px 12px rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.15);pointer-events:none';\n panel_el.innerHTML = '
fdty 答题中...
';\n document.body.appendChild(panel_el);\n var msg_el = document.getElementById('fdty-msg');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#ccc';\n var line = document.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found', '#ff5252'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#4fc3f7');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) { phase1Calls.push(callAPI(chunk, temp)); });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), agreed.length > disputed.length ? '#69f0ae' : '#ffab40');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#69f0ae');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#ffab40');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n log(unresolved.length + ' 题无法确定 (看控制台)', '#ff5252');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n console.warn('[fdty] [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#69f0ae');\n });\n }).catch(function(e) {\n log('错误: ' + e.message, '#ff5252');\n console.error('[fdty]', e);\n });\n})();\n"}, "7fc5b9949f1c4a50730b0714ddf1a35ac478ebec": {"t": 1785758751.6193368, "n": 7481, "h": "d53c0b8d05a772cf", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) return console.error('[fdty] Panel3 not found');\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n console.log('[fdty] ' + questions.length + ' 题 Phase 1 投票中...');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) { phase1Calls.push(callAPI(chunk, temp)); });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n console.log('[fdty] 一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts());\n\n if (disputed.length === 0) {\n console.log('[fdty] 完成 ' + totalOk + '/' + questions.length + ' 题 | ' + ts());\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n console.log('[fdty] Phase 2 仲裁 ' + disputed.length + ' 题...');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n console.warn('[fdty] ' + unresolved.length + ' 题无法确定,需人工判断:');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n console.warn(' [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n console.log('[fdty] 完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts());\n });\n }).catch(function(e) {\n console.error('[fdty]', e);\n });\n})();\n"}, "807ad0d81d2ba1b3260ccc990aeb581b0f326128": {"t": 1785758753.2175405, "n": 10013, "h": "8ba3539eeac099fe", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) return console.error('[fdty] Panel3 not found');\n\n // ============ 提取题目 (DOM 遍历 + 正则兜底) ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n // Method 1: DOM 遍历 — 找所有 group 元素,向前找兄弟文本节点\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue; // 过滤子元素 (radio inputs)\n\n // 向前找包含数字的文本节点\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break; // TEXT_NODE\n node = node.previousSibling;\n }\n if (!node) continue;\n\n // 从文本节点末尾提取 \"题号 . 题目文本\"\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n // Method 2: 正则兜底 — innerHTML 匹配 (处理 DOM 遍历遗漏)\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n var numTF = questions.filter(function(q) { return q.type === 'tf'; }).length;\n var numChoice = questions.filter(function(q) { return q.type === 'choice'; }).length;\n questions.sort(function(a, b) { return a.num - b.num; });\n console.log('[fdty] ' + ts() + ' 提取 ' + questions.length + ' 题 (判断' + numTF + ' + 单选' + numChoice + ', DOM:' + (questions.length > 0 && isGroupId(questions[0].id) ? 'yes' : 'regex') + ')');\n console.log('[fdty] 模型: deepseek-v4-flash | Key: ' + API_KEY.slice(0,8) + '***');\n\n // ============ API 调用 ============\n var apiCallCount = 0;\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var t0 = Date.now();\n apiCallCount++;\n var label = 'API#' + apiCallCount + ' (' + qs.length + '题, temp=' + temp.toFixed(2) + ')';\n console.log('[fdty] ' + ts() + ' ' + label + ' 发送...');\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status + ': ' + t.slice(0,200)); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n var elapsed = ((Date.now() - t0) / 1000).toFixed(1);\n var tokenInfo = d.usage ? ' | tokens: ' + d.usage.prompt_tokens + '+' + d.usage.completion_tokens + '=' + d.usage.total_tokens : '';\n if (!content) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 空响应!' + tokenInfo);\n return {};\n }\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try {\n var obj = JSON.parse(content);\n var keys = Object.keys(obj).length;\n console.log('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 解析 ' + keys + ' 个答案' + tokenInfo);\n return obj;\n } catch(e) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, JSON解析失败: ' + content.slice(0,80) + tokenInfo);\n return {};\n }\n }).catch(function(e) {\n console.error('[fdty] ' + ts() + ' ' + label + ' 失败: ' + e.message);\n return {};\n });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n console.log('[fdty] ' + ts() + ' Phase 1 ' + questions.length + '题 → ' + chunks.length + '组×3票, 共' + (chunks.length*3) + '次并发 (temp=' + temps.map(function(t) { return t.toFixed(2); }).join(',') + ')');\n\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) {\n phase1Calls.push(callAPI(chunk, temp));\n });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n // allResults: [chunk0_temp0, chunk0_temp1, chunk0_temp2, chunk1_temp0, ...]\n // 合并为每题的 3 票\n var votes = {};\n questions.forEach(function(q) {\n votes[String(q.num)] = [];\n });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n var ans = String(result[String(q.num)] || '').trim();\n votes[String(q.num)].push(ans);\n });\n });\n });\n\n var agreed = [], disputed = [];\n var emptyCount = 0;\n questions.forEach(function(q) {\n var key = String(q.num);\n var v = votes[key];\n if (!v[0] && !v[1] && !v[2]) {\n emptyCount++;\n }\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n // 全票一致的直接选\n var totalOk = 0;\n agreed.forEach(function(item) {\n if (selectAnswer(item.q, item.ans)) totalOk++;\n });\n console.log('[fdty] ' + ts() + ' Phase 1 一致:' + agreed.length + ' 已选 | 分歧:' + disputed.length + ' | 全空:' + emptyCount);\n\n if (disputed.length === 0) {\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length);\n return;\n }\n\n // ============ Phase 2: 逐题仲裁,流式输出 ============\n var unresolved = [];\n var phase2Done = 0, phase2Ok = 0;\n console.log('[fdty] Phase 2 开始 (' + disputed.length + '题, 每题10次请求)...');\n\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n console.log('[fdty] ✅ #' + q.num + ' → ' + best + ' (' + max + '/10票) [' + phase2Done + '/' + disputed.length + ']');\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n console.log('[fdty] ❓ #' + q.num + ' ' + (best||'?') + '(' + max + '/10) [' + phase2Done + '/' + disputed.length + ']');\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n console.warn('[fdty] === 以下 ' + unresolved.length + ' 题无法确定,需人工判断 ===');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n console.warn(' [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n console.warn('[fdty] ==========================================');\n }\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ', 共' + apiCallCount + '次API)');\n });\n }).catch(function(e) {\n console.error('[fdty]', e);\n });\n})();\n"}, "c20d5d943c0c8c28974b7c4d427cf6a35cc6aba5": {"t": 1785758754.4748611, "n": 9422, "h": "59e7c9cfe39030de", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) return console.error('[fdty] Panel3 not found');\n\n // ============ 提取题目 (DOM 遍历 + 正则兜底) ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n // Method 1: DOM 遍历 — 找所有 group 元素,向前找兄弟文本节点\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue; // 过滤子元素 (radio inputs)\n\n // 向前找包含数字的文本节点\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break; // TEXT_NODE\n node = node.previousSibling;\n }\n if (!node) continue;\n\n // 从文本节点末尾提取 \"题号 . 题目文本\"\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n // Method 2: 正则兜底 — innerHTML 匹配 (处理 DOM 遍历遗漏)\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n var numTF = questions.filter(function(q) { return q.type === 'tf'; }).length;\n var numChoice = questions.filter(function(q) { return q.type === 'choice'; }).length;\n questions.sort(function(a, b) { return a.num - b.num; });\n console.log('[fdty] ' + ts() + ' 提取 ' + questions.length + ' 题 (判断' + numTF + ' + 单选' + numChoice + ', DOM:' + (questions.length > 0 && isGroupId(questions[0].id) ? 'yes' : 'regex') + ')');\n console.log('[fdty] 模型: deepseek-v4-flash | Key: ' + API_KEY.slice(0,8) + '***');\n\n // ============ API 调用 ============\n var apiCallCount = 0;\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var t0 = Date.now();\n apiCallCount++;\n var label = 'API#' + apiCallCount + ' (' + qs.length + '题, temp=' + temp.toFixed(2) + ')';\n console.log('[fdty] ' + ts() + ' ' + label + ' 发送...');\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status + ': ' + t.slice(0,200)); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n var elapsed = ((Date.now() - t0) / 1000).toFixed(1);\n var tokenInfo = d.usage ? ' | tokens: ' + d.usage.prompt_tokens + '+' + d.usage.completion_tokens + '=' + d.usage.total_tokens : '';\n if (!content) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 空响应!' + tokenInfo);\n return {};\n }\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try {\n var obj = JSON.parse(content);\n var keys = Object.keys(obj).length;\n console.log('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 解析 ' + keys + ' 个答案' + tokenInfo);\n return obj;\n } catch(e) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, JSON解析失败: ' + content.slice(0,80) + tokenInfo);\n return {};\n }\n }).catch(function(e) {\n console.error('[fdty] ' + ts() + ' ' + label + ' 失败: ' + e.message);\n return {};\n });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 三次随机温度投票 ============\n var temps = [\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4\n ];\n console.log('[fdty] ' + ts() + ' Phase 1 三次投票 (temp=' + temps.map(function(t) { return t.toFixed(2); }).join(',') + ')...');\n\n Promise.all([\n callAPI(questions, temps[0]),\n callAPI(questions, temps[1]),\n callAPI(questions, temps[2])\n ]).then(function(results) {\n // 统计每题的 3 票\n var votes = {};\n questions.forEach(function(q) {\n var key = String(q.num);\n votes[key] = results.map(function(r) { return String(r[key] || '').trim(); });\n });\n\n var agreed = [], disputed = [];\n var emptyCount = 0;\n questions.forEach(function(q) {\n var key = String(q.num);\n var v = votes[key];\n if (!v[0] && !v[1] && !v[2]) {\n emptyCount++;\n }\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n // 全票一致的直接选\n var totalOk = 0;\n agreed.forEach(function(item) {\n if (selectAnswer(item.q, item.ans)) totalOk++;\n });\n console.log('[fdty] ' + ts() + ' Phase 1 一致:' + agreed.length + ' 已选 | 分歧:' + disputed.length + ' | 全空:' + emptyCount);\n\n if (disputed.length === 0) {\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length);\n return;\n }\n\n // ============ Phase 2: 逐题仲裁,流式输出 ============\n var unresolved = [];\n var phase2Done = 0, phase2Ok = 0;\n console.log('[fdty] Phase 2 开始 (' + disputed.length + '题, 每题10次请求)...');\n\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n console.log('[fdty] ✅ #' + q.num + ' → ' + best + ' (' + max + '/10票) [' + phase2Done + '/' + disputed.length + ']');\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n console.log('[fdty] ❓ #' + q.num + ' ' + (best||'?') + '(' + max + '/10) [' + phase2Done + '/' + disputed.length + ']');\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n console.warn('[fdty] === 以下 ' + unresolved.length + ' 题无法确定,需人工判断 ===');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n console.warn(' [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n console.warn('[fdty] ==========================================');\n }\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ', 共' + apiCallCount + '次API)');\n });\n }).catch(function(e) {\n console.error('[fdty]', e);\n });\n})();\n"}, "1819b5103d2a07ba9b07df80b97a587bf4a71fcb": {"t": 1785758756.0573802, "n": 9372, "h": "7f922c66648bcaef", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) return console.error('[fdty] Panel3 not found');\n\n // ============ 提取题目 (DOM 遍历 + 正则兜底) ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n // Method 1: DOM 遍历 — 找所有 group 元素,向前找兄弟文本节点\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue; // 过滤子元素 (radio inputs)\n\n // 向前找包含数字的文本节点\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break; // TEXT_NODE\n node = node.previousSibling;\n }\n if (!node) continue;\n\n // 从文本节点末尾提取 \"题号 . 题目文本\"\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n // Method 2: 正则兜底 — innerHTML 匹配 (处理 DOM 遍历遗漏)\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n var numTF = questions.filter(function(q) { return q.type === 'tf'; }).length;\n var numChoice = questions.filter(function(q) { return q.type === 'choice'; }).length;\n questions.sort(function(a, b) { return a.num - b.num; });\n console.log('[fdty] ' + ts() + ' 提取 ' + questions.length + ' 题 (判断' + numTF + ' + 单选' + numChoice + ', DOM:' + (questions.length > 0 && isGroupId(questions[0].id) ? 'yes' : 'regex') + ')');\n console.log('[fdty] 模型: deepseek-v4-flash | Key: ' + API_KEY.slice(0,8) + '***');\n\n // ============ API 调用 ============\n var apiCallCount = 0;\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var t0 = Date.now();\n apiCallCount++;\n var label = 'API#' + apiCallCount + ' (' + qs.length + '题, temp=' + temp.toFixed(2) + ')';\n console.log('[fdty] ' + ts() + ' ' + label + ' 发送...');\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status + ': ' + t.slice(0,200)); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n var elapsed = ((Date.now() - t0) / 1000).toFixed(1);\n var tokenInfo = d.usage ? ' | tokens: ' + d.usage.prompt_tokens + '+' + d.usage.completion_tokens + '=' + d.usage.total_tokens : '';\n if (!content) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 空响应!' + tokenInfo);\n return {};\n }\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try {\n var obj = JSON.parse(content);\n var keys = Object.keys(obj).length;\n console.log('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 解析 ' + keys + ' 个答案' + tokenInfo);\n return obj;\n } catch(e) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, JSON解析失败: ' + content.slice(0,80) + tokenInfo);\n return {};\n }\n }).catch(function(e) {\n console.error('[fdty] ' + ts() + ' ' + label + ' 失败: ' + e.message);\n return {};\n });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 三次随机温度投票 ============\n var temps = [\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4\n ];\n console.log('[fdty] ' + ts() + ' Phase 1 三次投票 (temp=' + temps.map(function(t) { return t.toFixed(2); }).join(',') + ')...');\n\n Promise.all([\n callAPI(questions, temps[0]),\n callAPI(questions, temps[1]),\n callAPI(questions, temps[2])\n ]).then(function(results) {\n // 统计每题的 3 票\n var votes = {};\n questions.forEach(function(q) {\n var key = String(q.num);\n votes[key] = results.map(function(r) { return String(r[key] || '').trim(); });\n });\n\n var agreed = [], disputed = [];\n var emptyCount = 0;\n questions.forEach(function(q) {\n var key = String(q.num);\n var v = votes[key];\n if (!v[0] && !v[1] && !v[2]) {\n emptyCount++;\n }\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n // 全票一致的直接选\n var totalOk = 0;\n agreed.forEach(function(item) {\n if (selectAnswer(item.q, item.ans)) totalOk++;\n });\n console.log('[fdty] ' + ts() + ' Phase 1 一致:' + agreed.length + ' 已选 | 分歧:' + disputed.length + ' | 全空:' + emptyCount);\n\n if (disputed.length === 0) {\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length);\n return;\n }\n\n // ============ Phase 2: 逐题仲裁,流式输出 ============\n var unresolved = [];\n var phase2Done = 0, phase2Ok = 0;\n console.log('[fdty] Phase 2 开始 (' + disputed.length + '题, 每题10次请求)...');\n\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n console.log('[fdty] ✅ #' + q.num + ' → ' + best + ' (' + max + '/10票) [' + phase2Done + '/' + disputed.length + ']');\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n console.log('[fdty] ❓ #' + q.num + ' ' + (best||'?') + '(' + max + '/10) [' + phase2Done + '/' + disputed.length + ']');\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n console.warn('[fdty] === 以下 ' + unresolved.length + ' 题无法确定,需人工判断 ===');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n console.warn(' [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n console.warn('[fdty] ==========================================');\n }\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ', 共' + apiCallCount + '次API)');\n });\n }).catch(function(e) {\n console.error('[fdty]', e);\n });\n})();\n"}, "9112c12f88927b5992c097d26b779d34d6b89577": {"t": 1785758758.1313272, "n": 9928, "h": "db8dc423428c50db", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) return console.error('[fdty] Panel3 not found');\n\n // ============ 提取题目 (DOM 遍历 + 正则兜底) ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n // Method 1: DOM 遍历 — 找所有 group 元素,向前找兄弟文本节点\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue; // 过滤子元素 (radio inputs)\n\n // 向前找包含数字的文本节点\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break; // TEXT_NODE\n node = node.previousSibling;\n }\n if (!node) continue;\n\n // 从文本节点末尾提取 \"题号 . 题目文本\"\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n // Method 2: 正则兜底 — innerHTML 匹配 (处理 DOM 遍历遗漏)\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n var numTF = questions.filter(function(q) { return q.type === 'tf'; }).length;\n var numChoice = questions.filter(function(q) { return q.type === 'choice'; }).length;\n questions.sort(function(a, b) { return a.num - b.num; });\n console.log('[fdty] ' + ts() + ' 提取 ' + questions.length + ' 题 (判断' + numTF + ' + 单选' + numChoice + ', DOM:' + (questions.length > 0 && isGroupId(questions[0].id) ? 'yes' : 'regex') + ')');\n console.log('[fdty] 模型: deepseek-v4-flash | Key: ' + API_KEY.slice(0,8) + '***');\n\n // ============ API 调用 ============\n var apiCallCount = 0;\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var t0 = Date.now();\n apiCallCount++;\n var label = 'API#' + apiCallCount + ' (' + qs.length + '题, temp=' + temp.toFixed(2) + ')';\n console.log('[fdty] ' + ts() + ' ' + label + ' 发送...');\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status + ': ' + t.slice(0,200)); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n var elapsed = ((Date.now() - t0) / 1000).toFixed(1);\n var tokenInfo = d.usage ? ' | tokens: ' + d.usage.prompt_tokens + '+' + d.usage.completion_tokens + '=' + d.usage.total_tokens : '';\n if (!content) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 空响应!' + tokenInfo);\n return {};\n }\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try {\n var obj = JSON.parse(content);\n var keys = Object.keys(obj).length;\n console.log('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 解析 ' + keys + ' 个答案' + tokenInfo);\n return obj;\n } catch(e) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, JSON解析失败: ' + content.slice(0,80) + tokenInfo);\n return {};\n }\n }).catch(function(e) {\n console.error('[fdty] ' + ts() + ' ' + label + ' 失败: ' + e.message);\n return {};\n });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 三次随机温度投票 ============\n var temps = [\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4\n ];\n console.log('[fdty] ' + ts() + ' Phase 1 三次投票 (temp=' + temps.map(function(t) { return t.toFixed(2); }).join(',') + ')...');\n\n Promise.all([\n callAPI(questions, temps[0]),\n callAPI(questions, temps[1]),\n callAPI(questions, temps[2])\n ]).then(function(results) {\n // 统计每题的 3 票\n var votes = {};\n questions.forEach(function(q) {\n var key = String(q.num);\n votes[key] = results.map(function(r) { return String(r[key] || '').trim(); });\n });\n\n var agreed = [], disputed = [];\n var emptyCount = 0;\n questions.forEach(function(q) {\n var key = String(q.num);\n var v = votes[key];\n if (!v[0] && !v[1] && !v[2]) {\n emptyCount++;\n }\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n // 全票一致的直接选\n var totalOk = 0;\n agreed.forEach(function(item) {\n if (selectAnswer(item.q, item.ans)) totalOk++;\n });\n console.log('[fdty] ' + ts() + ' Phase 1 一致:' + agreed.length + ' 已选 | 分歧:' + disputed.length + ' | 全空:' + emptyCount);\n\n if (disputed.length === 0) {\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length);\n return;\n }\n\n // ============ Phase 2: 逐题 10 次仲裁 ============\n console.log('[fdty] ' + ts() + ' Phase 2 逐题仲裁 ' + disputed.length + ' 题 (每题 10 次请求, 共' + (disputed.length*10) + '次)...');\n\n var phase2Done = 0;\n var phase2Jobs = disputed.map(function(q, qi) {\n // 同一题发 10 次请求\n var batch = [];\n for (var i = 0; i < 10; i++) {\n batch.push(callAPI([q], 0.7));\n }\n return Promise.all(batch).then(function(answers) {\n phase2Done++;\n // 统计 10 次投票\n var counts = {}, validAnswers = 0;\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) { counts[ans] = (counts[ans] || 0) + 1; validAnswers++; }\n });\n\n var best = '', max = 0;\n Object.keys(counts).forEach(function(ans) {\n if (counts[ans] > max) { max = counts[ans]; best = ans; }\n });\n\n var result = { q: q, best: best, count: max, counts: counts };\n console.log('[fdty] ' + ts() + ' Phase2 [' + phase2Done + '/' + disputed.length +\n '] 题#' + q.num + ' (' + (q.type==='tf'?'判':'选') + ') ' +\n q.text.slice(0, 40) + '... ' + validAnswers + '/10 有效, 最佳:' + best + '(' + max + '票)');\n return result;\n });\n });\n\n return Promise.all(phase2Jobs).then(function(results) {\n var unresolved = [];\n\n results.forEach(function(r) {\n if (r.best && r.count >= 8) {\n if (selectAnswer(r.q, r.best)) totalOk++;\n } else {\n unresolved.push(r);\n }\n });\n\n var phase2ok = results.length - unresolved.length;\n console.log('[fdty] ' + ts() + ' Phase 2 确定 ' + phase2ok + ' 题, 剩余 ' + unresolved.length + ' 题');\n\n // ============ Phase 3: 输出无法确定题目 ============\n if (unresolved.length > 0) {\n console.warn('[fdty] ========== 以下 ' + unresolved.length + ' 题无法确定,需人工判断 ==========');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) {\n parts.push(ans + ':' + r.counts[ans]);\n });\n console.warn(\n ' [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' +\n r.q.text + '\\n 票数: ' + parts.join(', ')\n );\n });\n console.warn('[fdty] ================================================');\n }\n\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length +\n ' (一致:' + agreed.length + ' + 仲裁:' + phase2ok + ', 未定:' + unresolved.length + ', API调用:' + apiCallCount + ')');\n });\n }).catch(function(e) {\n console.error('[fdty]', e);\n });\n})();\n"}, "9f5931fc29e45423b8707e9f943921dfd7bb2149": {"t": 1785758759.3000329, "n": 8135, "h": "7204bf4330e4f19e", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) return console.error('[fdty] Panel3 not found');\n\n // ============ 提取题目 (DOM 遍历 + 正则兜底) ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n // Method 1: DOM 遍历 — 找所有 group 元素,向前找兄弟文本节点\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue; // 过滤子元素 (radio inputs)\n\n // 向前找包含数字的文本节点\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break; // TEXT_NODE\n node = node.previousSibling;\n }\n if (!node) continue;\n\n // 从文本节点末尾提取 \"题号 . 题目文本\"\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n // Method 2: 正则兜底 — innerHTML 匹配 (处理 DOM 遍历遗漏)\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n console.log('[fdty] ' + ts() + ' 提取 ' + questions.length + ' 题 (DOM: ' + (questions.length > 0 && isGroupId(questions[0].id) ? 'yes' : 'regex') + ')');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status + ': ' + t.slice(0,200)); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (!content) throw new Error('empty response');\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 三次随机温度投票 ============\n var temps = [\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4\n ];\n console.log('[fdty] ' + ts() + ' Phase 1 三次投票 (temp=' + temps.map(function(t) { return t.toFixed(2); }).join(',') + ')...');\n\n Promise.all([\n callAPI(questions, temps[0]),\n callAPI(questions, temps[1]),\n callAPI(questions, temps[2])\n ]).then(function(results) {\n // 统计每题的 3 票\n var votes = {};\n questions.forEach(function(q) {\n var key = String(q.num);\n votes[key] = results.map(function(r) { return String(r[key] || '').trim(); });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var key = String(q.num);\n var v = votes[key];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n // 全票一致的直接选\n var totalOk = 0;\n agreed.forEach(function(item) {\n if (selectAnswer(item.q, item.ans)) totalOk++;\n });\n console.log('[fdty] ' + ts() + ' Phase 1 一致 ' + agreed.length + ' 题已选, 分歧 ' + disputed.length + ' 题');\n\n if (disputed.length === 0) {\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length);\n return;\n }\n\n // ============ Phase 2: 逐题 10 次仲裁 ============\n console.log('[fdty] ' + ts() + ' Phase 2 逐题仲裁 ' + disputed.length + ' 题 (每题 10 次请求)...');\n\n var phase2Jobs = disputed.map(function(q) {\n // 同一题发 10 次请求\n var batch = [];\n for (var i = 0; i < 10; i++) {\n batch.push(callAPI([q], 0.7));\n }\n return Promise.all(batch).then(function(answers) {\n // 统计 10 次投票\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n\n var best = '', max = 0;\n Object.keys(counts).forEach(function(ans) {\n if (counts[ans] > max) { max = counts[ans]; best = ans; }\n });\n\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n return Promise.all(phase2Jobs).then(function(results) {\n var unresolved = [];\n\n results.forEach(function(r) {\n if (r.best && r.count >= 8) {\n if (selectAnswer(r.q, r.best)) totalOk++;\n } else {\n unresolved.push(r);\n }\n });\n\n var phase2ok = results.length - unresolved.length;\n console.log('[fdty] ' + ts() + ' Phase 2 确定 ' + phase2ok + ' 题, 剩余 ' + unresolved.length + ' 题');\n\n // ============ Phase 3: 输出无法确定题目 ============\n if (unresolved.length > 0) {\n console.warn('[fdty] ========== 以下 ' + unresolved.length + ' 题无法确定,需人工判断 ==========');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) {\n parts.push(ans + ':' + r.counts[ans]);\n });\n console.warn(\n ' [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' +\n r.q.text + '\\n 票数: ' + parts.join(', ')\n );\n });\n console.warn('[fdty] ================================================');\n }\n\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length +\n ' (一致 ' + agreed.length + ' + 仲裁 ' + phase2ok + ', 未定 ' + unresolved.length + ')');\n });\n }).catch(function(e) {\n console.error('[fdty]', e);\n });\n})();\n"}, "ee741843ae4815e4056204cb5f7d1200ef94a578": {"t": 1785758760.6310122, "n": 8133, "h": "0fc6bcee3ca478cb", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) return console.error('[fdty] Panel3 not found');\n\n // ============ 提取题目 (DOM 遍历 + 正则兜底) ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n // Method 1: DOM 遍历 — 找所有 group 元素,向前找兄弟文本节点\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue; // 过滤子元素 (radio inputs)\n\n // 向前找包含数字的文本节点\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break; // TEXT_NODE\n node = node.previousSibling;\n }\n if (!node) continue;\n\n // 从文本节点末尾提取 \"题号 . 题目文本\"\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n // Method 2: 正则兜底 — innerHTML 匹配 (处理 DOM 遍历遗漏)\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n console.log('[fdty] ' + ts() + ' 提取 ' + questions.length + ' 题 (DOM: ' + (questions.length > 0 && isGroupId(questions[0].id) ? 'yes' : 'regex') + ')');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-pro',\n temperature: temp,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status + ': ' + t.slice(0,200)); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (!content) throw new Error('empty response');\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 三次随机温度投票 ============\n var temps = [\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4\n ];\n console.log('[fdty] ' + ts() + ' Phase 1 三次投票 (temp=' + temps.map(function(t) { return t.toFixed(2); }).join(',') + ')...');\n\n Promise.all([\n callAPI(questions, temps[0]),\n callAPI(questions, temps[1]),\n callAPI(questions, temps[2])\n ]).then(function(results) {\n // 统计每题的 3 票\n var votes = {};\n questions.forEach(function(q) {\n var key = String(q.num);\n votes[key] = results.map(function(r) { return String(r[key] || '').trim(); });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var key = String(q.num);\n var v = votes[key];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n // 全票一致的直接选\n var totalOk = 0;\n agreed.forEach(function(item) {\n if (selectAnswer(item.q, item.ans)) totalOk++;\n });\n console.log('[fdty] ' + ts() + ' Phase 1 一致 ' + agreed.length + ' 题已选, 分歧 ' + disputed.length + ' 题');\n\n if (disputed.length === 0) {\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length);\n return;\n }\n\n // ============ Phase 2: 逐题 10 次仲裁 ============\n console.log('[fdty] ' + ts() + ' Phase 2 逐题仲裁 ' + disputed.length + ' 题 (每题 10 次请求)...');\n\n var phase2Jobs = disputed.map(function(q) {\n // 同一题发 10 次请求\n var batch = [];\n for (var i = 0; i < 10; i++) {\n batch.push(callAPI([q], 0.7));\n }\n return Promise.all(batch).then(function(answers) {\n // 统计 10 次投票\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n\n var best = '', max = 0;\n Object.keys(counts).forEach(function(ans) {\n if (counts[ans] > max) { max = counts[ans]; best = ans; }\n });\n\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n return Promise.all(phase2Jobs).then(function(results) {\n var unresolved = [];\n\n results.forEach(function(r) {\n if (r.best && r.count >= 8) {\n if (selectAnswer(r.q, r.best)) totalOk++;\n } else {\n unresolved.push(r);\n }\n });\n\n var phase2ok = results.length - unresolved.length;\n console.log('[fdty] ' + ts() + ' Phase 2 确定 ' + phase2ok + ' 题, 剩余 ' + unresolved.length + ' 题');\n\n // ============ Phase 3: 输出无法确定题目 ============\n if (unresolved.length > 0) {\n console.warn('[fdty] ========== 以下 ' + unresolved.length + ' 题无法确定,需人工判断 ==========');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) {\n parts.push(ans + ':' + r.counts[ans]);\n });\n console.warn(\n ' [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' +\n r.q.text + '\\n 票数: ' + parts.join(', ')\n );\n });\n console.warn('[fdty] ================================================');\n }\n\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length +\n ' (一致 ' + agreed.length + ' + 仲裁 ' + phase2ok + ', 未定 ' + unresolved.length + ')');\n });\n }).catch(function(e) {\n console.error('[fdty]', e);\n });\n})();\n"}}}}