1698 lines
58 KiB
JavaScript
1698 lines
58 KiB
JavaScript
/**
|
||
* AI智能游戏生成器
|
||
* 功能:自动出题、动态难度调整、个性化游戏内容
|
||
*/
|
||
class AIGameGenerator {
|
||
constructor() {
|
||
this.isInitialized = false;
|
||
this.currentDifficulty = 1; // 1-5级难度
|
||
this.studentProfile = null;
|
||
this.questionBank = new Map();
|
||
this.difficultyHistory = [];
|
||
this.performanceHistory = [];
|
||
|
||
// 题目类型配置
|
||
this.questionTypes = {
|
||
math: {
|
||
addition: { min: 1, max: 20, operators: ['+'] },
|
||
subtraction: { min: 1, max: 20, operators: ['-'] },
|
||
multiplication: { min: 1, max: 10, operators: ['×'] },
|
||
comparison: { min: 1, max: 20, operators: ['>', '<', '='] },
|
||
clock: { min: 1, max: 12, operators: ['hour', 'minute'] }
|
||
},
|
||
language: {
|
||
pinyin: { min: 1, max: 10, operators: ['声调', '拼音'] },
|
||
word: { min: 1, max: 20, operators: ['组词', '造句'] },
|
||
poem: { min: 1, max: 5, operators: ['背诵', '理解'] }
|
||
},
|
||
life: {
|
||
emotion: { min: 1, max: 8, operators: ['识别', '表达'] },
|
||
safety: { min: 1, max: 10, operators: ['判断', '选择'] },
|
||
daily: { min: 1, max: 15, operators: ['分类', '整理'] }
|
||
}
|
||
};
|
||
|
||
// 难度调整参数
|
||
this.difficultyParams = {
|
||
1: { successRate: 0.9, timeLimit: 30, hints: 3 },
|
||
2: { successRate: 0.8, timeLimit: 25, hints: 2 },
|
||
3: { successRate: 0.7, timeLimit: 20, hints: 1 },
|
||
4: { successRate: 0.6, timeLimit: 15, hints: 0 },
|
||
5: { successRate: 0.5, timeLimit: 10, hints: 0 }
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 初始化AI游戏生成器
|
||
*/
|
||
async init() {
|
||
try {
|
||
console.log('🤖 AI游戏生成器初始化中...');
|
||
|
||
// 加载题目库
|
||
await this.loadQuestionBank();
|
||
|
||
// 加载学生档案
|
||
await this.loadStudentProfile();
|
||
|
||
this.isInitialized = true;
|
||
console.log('✅ AI游戏生成器初始化完成');
|
||
|
||
return true;
|
||
} catch (error) {
|
||
console.error('❌ AI游戏生成器初始化失败:', error);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 加载学生档案
|
||
*/
|
||
async loadStudentProfile() {
|
||
try {
|
||
// 从后端获取学生数据
|
||
if (window.apiService) {
|
||
const userData = window.userManager?.getCurrentUser();
|
||
if (userData) {
|
||
const stats = await window.apiService.getStudentStats(userData.id, 30);
|
||
this.studentProfile = {
|
||
id: userData.id,
|
||
name: userData.name,
|
||
grade: userData.grade || 2,
|
||
strengths: stats.strengths || [],
|
||
weaknesses: stats.weaknesses || [],
|
||
learningStyle: stats.learningStyle || 'visual',
|
||
currentLevel: stats.currentLevel || 1,
|
||
totalQuestions: stats.totalQuestions || 0,
|
||
correctRate: stats.correctRate || 0.7
|
||
};
|
||
}
|
||
}
|
||
|
||
// 如果没有后端数据,使用默认配置
|
||
if (!this.studentProfile) {
|
||
this.studentProfile = {
|
||
id: 'default',
|
||
name: '学生',
|
||
grade: 2,
|
||
strengths: ['数学'],
|
||
weaknesses: ['语文'],
|
||
learningStyle: 'visual',
|
||
currentLevel: 1,
|
||
totalQuestions: 0,
|
||
correctRate: 0.7
|
||
};
|
||
}
|
||
|
||
console.log('📊 学生档案加载完成:', this.studentProfile);
|
||
} catch (error) {
|
||
console.error('❌ 加载学生档案失败:', error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 加载题目库
|
||
*/
|
||
async loadQuestionBank() {
|
||
try {
|
||
const response = await fetch('data/questionBank.json');
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
const questionBankData = await response.json();
|
||
|
||
// 将JSON数据转换为Map格式
|
||
for (const [subject, types] of Object.entries(questionBankData)) {
|
||
this.questionBank.set(subject, types);
|
||
}
|
||
|
||
console.log('📚 题目库加载完成:', this.questionBank);
|
||
} catch (error) {
|
||
console.error('❌ 加载题目库失败:', error);
|
||
// 如果加载失败,使用默认题目库
|
||
await this.initializeDefaultQuestionBank();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 初始化默认题目库(备用)
|
||
*/
|
||
async initializeDefaultQuestionBank() {
|
||
// 数学题目库
|
||
this.questionBank.set('math', {
|
||
addition: [
|
||
{ question: "2 + 3 = ?", options: ["4", "5", "6", "7"], correct: 1, difficulty: 1 },
|
||
{ question: "7 + 8 = ?", options: ["14", "15", "16", "17"], correct: 1, difficulty: 2 }
|
||
],
|
||
subtraction: [
|
||
{ question: "8 - 3 = ?", options: ["4", "5", "6", "7"], correct: 1, difficulty: 1 },
|
||
{ question: "15 - 7 = ?", options: ["7", "8", "9", "10"], correct: 1, difficulty: 2 }
|
||
]
|
||
});
|
||
|
||
// 语文题目库
|
||
this.questionBank.set('language', {
|
||
pinyin: [
|
||
{ question: "'猫'的拼音是?", options: ["māo", "máo", "mǎo", "mào"], correct: 0, difficulty: 1 }
|
||
],
|
||
word: [
|
||
{ question: "用'美丽'组词", options: ["美丽的花", "美丽的人", "美丽的风景", "以上都对"], correct: 3, difficulty: 2 }
|
||
]
|
||
});
|
||
|
||
// 生活题目库
|
||
this.questionBank.set('life', {
|
||
emotion: [
|
||
{ question: "看到好吃的食物,应该是什么表情?", options: ["😊", "😢", "😠", "😴"], correct: 0, difficulty: 1 }
|
||
],
|
||
safety: [
|
||
{ question: "过马路时应该?", options: ["看红绿灯", "直接跑过去", "闭着眼睛", "听音乐"], correct: 0, difficulty: 1 }
|
||
]
|
||
});
|
||
|
||
console.log('📚 默认题目库初始化完成');
|
||
}
|
||
|
||
/**
|
||
* 生成个性化游戏
|
||
*/
|
||
generatePersonalizedGame(subject = 'math', gameType = 'quiz', questionCount = 5) {
|
||
if (!this.isInitialized) {
|
||
console.error('❌ AI游戏生成器未初始化');
|
||
return null;
|
||
}
|
||
|
||
console.log(`🎮 生成个性化游戏: ${subject} - ${gameType}`);
|
||
|
||
const gameConfig = {
|
||
id: this.generateGameId(),
|
||
subject: subject,
|
||
type: gameType,
|
||
difficulty: this.currentDifficulty,
|
||
questions: [],
|
||
timeLimit: this.difficultyParams[this.currentDifficulty].timeLimit,
|
||
hints: this.difficultyParams[this.currentDifficulty].hints,
|
||
studentProfile: this.studentProfile
|
||
};
|
||
|
||
// 从题目库中获取题目
|
||
const questions = this.getQuestionsFromBank(subject, questionCount);
|
||
gameConfig.questions = questions;
|
||
|
||
// 个性化调整
|
||
this.personalizeGame(gameConfig);
|
||
|
||
console.log('✅ 个性化游戏生成完成:', gameConfig);
|
||
return gameConfig;
|
||
}
|
||
|
||
/**
|
||
* 生成完整的游戏页面
|
||
*/
|
||
generateGamePage(subject = 'math', gameType = 'quiz', questionCount = 5) {
|
||
if (!this.isInitialized) {
|
||
console.error('❌ AI游戏生成器未初始化');
|
||
return null;
|
||
}
|
||
|
||
console.log(`🎮 生成完整游戏页面: ${subject} - ${gameType}`);
|
||
|
||
// 生成游戏配置
|
||
const gameConfig = this.generatePersonalizedGame(subject, gameType, questionCount);
|
||
|
||
if (!gameConfig || !gameConfig.questions.length) {
|
||
console.error('❌ 无法生成游戏配置');
|
||
return null;
|
||
}
|
||
|
||
// 在当前页面内嵌入游戏
|
||
this.embedGameInCurrentPage(gameConfig);
|
||
|
||
console.log('✅ 游戏页面生成完成并已嵌入当前页面');
|
||
return gameConfig;
|
||
}
|
||
|
||
/**
|
||
* 在当前页面内嵌入游戏
|
||
*/
|
||
embedGameInCurrentPage(gameConfig) {
|
||
const subject = gameConfig.subject;
|
||
const gameType = gameConfig.type;
|
||
const questions = gameConfig.questions;
|
||
const difficulty = gameConfig.difficulty;
|
||
|
||
// 根据学科确定游戏标题和样式
|
||
let gameTitle, gameIntro, gameStyle;
|
||
|
||
switch (subject) {
|
||
case 'math':
|
||
if (gameType === 'quiz') {
|
||
gameTitle = 'AI数学游戏';
|
||
gameIntro = '欢迎来到AI数学游戏!我会帮你学习数学知识,准备好了吗?';
|
||
} else {
|
||
gameTitle = 'AI数学比大小游戏';
|
||
gameIntro = '欢迎来到AI数学比大小游戏!我会帮你学习数字比较,准备好了吗?';
|
||
}
|
||
gameStyle = this.getMathGameStyle();
|
||
break;
|
||
case 'language':
|
||
gameTitle = 'AI语文拼音游戏';
|
||
gameIntro = '欢迎来到AI语文拼音游戏!我会帮你学习拼音和声调,准备好了吗?';
|
||
gameStyle = this.getLanguageGameStyle();
|
||
break;
|
||
case 'life':
|
||
gameTitle = 'AI生活安全游戏';
|
||
gameIntro = '欢迎来到AI生活安全游戏!我会帮你学习生活中的安全知识,准备好了吗?';
|
||
gameStyle = this.getLifeGameStyle();
|
||
break;
|
||
default:
|
||
gameTitle = 'AI智能游戏';
|
||
gameIntro = '欢迎来到AI智能游戏!我会帮你学习知识,准备好了吗?';
|
||
gameStyle = this.getDefaultGameStyle();
|
||
}
|
||
|
||
// 创建游戏容器
|
||
const gameContainer = document.createElement('div');
|
||
gameContainer.id = 'ai-game-container';
|
||
gameContainer.innerHTML = `
|
||
<style>
|
||
${gameStyle}
|
||
#ai-game-container {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.8);
|
||
z-index: 9999;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
#ai-game-container .game-container {
|
||
width: 90%;
|
||
max-width: 800px;
|
||
max-height: 90%;
|
||
overflow-y: auto;
|
||
position: relative;
|
||
}
|
||
.virtual-teacher-sidebar {
|
||
position: absolute;
|
||
right: -200px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 180px;
|
||
height: 200px;
|
||
background: rgba(255, 255, 255, 0.9);
|
||
border-radius: 15px;
|
||
padding: 20px;
|
||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.virtual-teacher-avatar {
|
||
width: 80px;
|
||
height: 80px;
|
||
background: linear-gradient(135deg, #ff9aa2, #ffb7b2);
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 40px;
|
||
margin-bottom: 10px;
|
||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
|
||
}
|
||
.virtual-teacher-status {
|
||
font-size: 14px;
|
||
color: #666;
|
||
text-align: center;
|
||
margin-bottom: 10px;
|
||
}
|
||
.virtual-teacher-controls {
|
||
display: flex;
|
||
gap: 10px;
|
||
}
|
||
.control-btn {
|
||
width: 30px;
|
||
height: 30px;
|
||
border: none;
|
||
border-radius: 50%;
|
||
background: #f0f0f0;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 16px;
|
||
transition: all 0.2s ease;
|
||
}
|
||
.control-btn:hover {
|
||
background: #e0e0e0;
|
||
transform: scale(1.1);
|
||
}
|
||
.control-btn.speaking {
|
||
background: #4ecdc4;
|
||
color: white;
|
||
animation: pulse 1s infinite;
|
||
}
|
||
@keyframes pulse {
|
||
0% { transform: scale(1); }
|
||
50% { transform: scale(1.1); }
|
||
100% { transform: scale(1); }
|
||
}
|
||
/* 体感交互按钮样式 */
|
||
.box-btn {
|
||
position: absolute;
|
||
bottom: 20px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20px;
|
||
background: rgba(255, 255, 255, 0.9);
|
||
padding: 15px 30px;
|
||
border-radius: 50px;
|
||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2);
|
||
z-index: 1000;
|
||
}
|
||
.box-btn-left, .box-btn-right {
|
||
width: 60px;
|
||
height: 60px;
|
||
border: none;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, #4ecdc4, #44a08d);
|
||
color: white;
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.box-btn-left:hover, .box-btn-right:hover {
|
||
transform: scale(1.1);
|
||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
||
}
|
||
.box-btn-left:active, .box-btn-right:active {
|
||
transform: scale(0.95);
|
||
}
|
||
.box-btn-question {
|
||
font-size: 16px;
|
||
color: #333;
|
||
text-align: center;
|
||
min-width: 200px;
|
||
font-weight: 500;
|
||
}
|
||
.close-game-btn {
|
||
position: absolute;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: #ff6b6b;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 50%;
|
||
width: 40px;
|
||
height: 40px;
|
||
font-size: 20px;
|
||
cursor: pointer;
|
||
z-index: 10000;
|
||
}
|
||
.close-game-btn:hover {
|
||
background: #ff5252;
|
||
}
|
||
</style>
|
||
<button class="close-game-btn" onclick="closeAIGame()">×</button>
|
||
<div class="game-container">
|
||
<div class="score-area">
|
||
<div>得分: <span id="score">0</span></div>
|
||
<div>进度: <span id="progress">0</span>/<span id="total">${questions.length}</span></div>
|
||
</div>
|
||
|
||
<h1>${gameTitle}</h1>
|
||
|
||
<div class="progress-bar">
|
||
<div class="progress-fill" id="progressFill"></div>
|
||
</div>
|
||
|
||
<div class="question-area">
|
||
<div class="question" id="questionText">准备开始...</div>
|
||
<div class="options" id="optionsContainer">
|
||
<!-- 选项将通过JavaScript动态生成 -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 体感交互按钮容器 -->
|
||
<div class="box-btn" id="bodyInteractionBtns" style="display: none;">
|
||
<button class="box-btn-left" onclick="selectBodyAnswer('left')" aria-label="选择左侧选项"></button>
|
||
<div class="box-btn-question" id="bodyQuestionText">请举起左手或右手选择答案</div>
|
||
<button class="box-btn-right" onclick="selectBodyAnswer('right')" aria-label="选择右侧选项"></button>
|
||
</div>
|
||
|
||
<!-- 虚拟猫头鹰侧边栏 -->
|
||
<div class="virtual-teacher-sidebar">
|
||
<div class="virtual-teacher-avatar" id="virtualTeacherAvatar">🦉</div>
|
||
<div class="virtual-teacher-status" id="virtualTeacherStatus">准备就绪</div>
|
||
<div class="virtual-teacher-controls">
|
||
<button class="control-btn" id="speakBtn" onclick="toggleVirtualTeacher()" title="语音开关">🔊</button>
|
||
<button class="control-btn" id="muteBtn" onclick="muteVirtualTeacher()" title="静音">🔇</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="message" id="message">
|
||
<h2 id="messageTitle">标题</h2>
|
||
<p id="messageText">内容</p>
|
||
<button class="message-btn" id="messageBtn">继续</button>
|
||
</div>
|
||
`;
|
||
|
||
// 添加到页面
|
||
document.body.appendChild(gameContainer);
|
||
|
||
// 初始化游戏逻辑
|
||
this.initializeGameLogic(gameConfig, gameTitle, gameIntro);
|
||
}
|
||
|
||
/**
|
||
* 初始化游戏逻辑
|
||
*/
|
||
initializeGameLogic(gameConfig, gameTitle, gameIntro) {
|
||
const gameId = gameConfig.id;
|
||
const questions = gameConfig.questions;
|
||
|
||
// DOM元素
|
||
const questionText = document.getElementById("questionText");
|
||
const optionsContainer = document.getElementById("optionsContainer");
|
||
const message = document.getElementById("message");
|
||
const messageTitle = document.getElementById("messageTitle");
|
||
const messageText = document.getElementById("messageText");
|
||
const messageBtn = document.getElementById("messageBtn");
|
||
const scoreElement = document.getElementById("score");
|
||
const progressElement = document.getElementById("progress");
|
||
const totalElement = document.getElementById("total");
|
||
const progressFill = document.getElementById("progressFill");
|
||
|
||
// 游戏状态
|
||
let currentQuestionIndex = 0;
|
||
let score = 0;
|
||
let correctCount = 0;
|
||
let gameStarted = false;
|
||
let virtualTeacherMuted = false;
|
||
|
||
// 体感交互元素
|
||
const bodyInteractionBtns = document.getElementById("bodyInteractionBtns");
|
||
const bodyQuestionText = document.getElementById("bodyQuestionText");
|
||
|
||
// 虚拟猫头鹰控制函数
|
||
function updateVirtualTeacherStatus(status, isSpeaking = false) {
|
||
if (virtualTeacherStatus) {
|
||
virtualTeacherStatus.textContent = status;
|
||
}
|
||
if (speakBtn) {
|
||
if (isSpeaking) {
|
||
speakBtn.classList.add('speaking');
|
||
} else {
|
||
speakBtn.classList.remove('speaking');
|
||
}
|
||
}
|
||
}
|
||
|
||
function toggleVirtualTeacher() {
|
||
virtualTeacherMuted = !virtualTeacherMuted;
|
||
if (virtualTeacherMuted) {
|
||
updateVirtualTeacherStatus('已静音');
|
||
if (window.gameVirtualTeacher) {
|
||
window.gameVirtualTeacher.stopAllSpeech();
|
||
}
|
||
} else {
|
||
updateVirtualTeacherStatus('准备就绪');
|
||
}
|
||
}
|
||
|
||
function muteVirtualTeacher() {
|
||
virtualTeacherMuted = true;
|
||
updateVirtualTeacherStatus('已静音');
|
||
if (window.gameVirtualTeacher) {
|
||
window.gameVirtualTeacher.stopAllSpeech();
|
||
}
|
||
}
|
||
|
||
// 体感交互控制函数
|
||
function showBodyInteraction() {
|
||
if (bodyInteractionBtns) {
|
||
bodyInteractionBtns.style.display = 'flex';
|
||
}
|
||
}
|
||
|
||
function hideBodyInteraction() {
|
||
if (bodyInteractionBtns) {
|
||
bodyInteractionBtns.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
function updateBodyQuestionText(text) {
|
||
if (bodyQuestionText) {
|
||
bodyQuestionText.textContent = text;
|
||
}
|
||
}
|
||
|
||
// 体感交互选择答案
|
||
function selectBodyAnswer(hand) {
|
||
if (currentQuestionIndex >= questions.length) return;
|
||
|
||
const question = questions[currentQuestionIndex];
|
||
let selectedIndex = -1;
|
||
|
||
// 根据左右手选择对应的选项
|
||
if (question.options.length >= 2) {
|
||
selectedIndex = hand === 'left' ? 0 : 1;
|
||
} else if (question.options.length === 1) {
|
||
selectedIndex = 0;
|
||
}
|
||
|
||
if (selectedIndex >= 0) {
|
||
selectAnswer(selectedIndex, question);
|
||
}
|
||
}
|
||
|
||
// 显示当前题目
|
||
function showCurrentQuestion() {
|
||
if (currentQuestionIndex >= questions.length) {
|
||
endGame();
|
||
return;
|
||
}
|
||
|
||
const question = questions[currentQuestionIndex];
|
||
questionText.textContent = question.question;
|
||
|
||
// 清空选项容器
|
||
optionsContainer.innerHTML = "";
|
||
|
||
// 创建选项按钮
|
||
question.options.forEach((option, index) => {
|
||
const button = document.createElement("button");
|
||
button.className = "option-btn";
|
||
button.textContent = option;
|
||
button.onclick = () => selectAnswer(index, question);
|
||
optionsContainer.appendChild(button);
|
||
});
|
||
|
||
// 更新进度
|
||
progressElement.textContent = currentQuestionIndex + 1;
|
||
progressFill.style.width = `${((currentQuestionIndex + 1) / questions.length) * 100}%`;
|
||
|
||
// 虚拟老师读题目
|
||
if (window.gameVirtualTeacher && !virtualTeacherMuted) {
|
||
updateVirtualTeacherStatus('正在读题...', true);
|
||
setTimeout(() => {
|
||
window.gameVirtualTeacher.readQuestion(question.question);
|
||
setTimeout(() => {
|
||
updateVirtualTeacherStatus('准备就绪');
|
||
}, 2000);
|
||
}, 500);
|
||
}
|
||
|
||
// 显示体感交互按钮
|
||
showBodyInteraction();
|
||
|
||
// 更新体感交互提示文本
|
||
if (question.options.length >= 2) {
|
||
updateBodyQuestionText(`请举起${question.options[0]}或${question.options[1]}选择答案`);
|
||
} else if (question.options.length === 1) {
|
||
updateBodyQuestionText(`请举起任意一只手选择:${question.options[0]}`);
|
||
} else {
|
||
updateBodyQuestionText('请举起左手或右手选择答案');
|
||
}
|
||
}
|
||
|
||
// 选择答案
|
||
function selectAnswer(selectedIndex, question) {
|
||
if (message.classList.contains("show")) return;
|
||
|
||
// 隐藏体感交互按钮
|
||
hideBodyInteraction();
|
||
|
||
const isCorrect = selectedIndex === question.correct;
|
||
const selectedButton = optionsContainer.children[selectedIndex];
|
||
const correctButton = optionsContainer.children[question.correct];
|
||
|
||
// 显示答案效果
|
||
selectedButton.classList.add(isCorrect ? "correct" : "wrong");
|
||
if (!isCorrect) {
|
||
correctButton.classList.add("correct");
|
||
}
|
||
|
||
// 虚拟老师反馈
|
||
if (window.gameVirtualTeacher && !virtualTeacherMuted) {
|
||
window.gameVirtualTeacher.stopAllSpeech();
|
||
updateVirtualTeacherStatus(isCorrect ? '答对了!' : '再想想...', true);
|
||
setTimeout(() => {
|
||
if (isCorrect) {
|
||
window.gameVirtualTeacher.recordCorrect();
|
||
} else {
|
||
window.gameVirtualTeacher.recordIncorrect();
|
||
}
|
||
setTimeout(() => {
|
||
updateVirtualTeacherStatus('准备就绪');
|
||
}, 1500);
|
||
}, 300);
|
||
}
|
||
|
||
// 更新分数
|
||
if (isCorrect) {
|
||
correctCount++;
|
||
score += 10;
|
||
scoreElement.textContent = score;
|
||
}
|
||
|
||
// 记录游戏数据
|
||
if (gameStarted && window.gameTracker) {
|
||
if (isCorrect) {
|
||
window.gameTracker.recordCorrect(10, {
|
||
question: question.question,
|
||
selected: question.options[selectedIndex],
|
||
correct: question.options[question.correct],
|
||
questionNumber: currentQuestionIndex + 1,
|
||
});
|
||
} else {
|
||
window.gameTracker.recordIncorrect(0, {
|
||
question: question.question,
|
||
selected: question.options[selectedIndex],
|
||
correct: question.options[question.correct],
|
||
questionNumber: currentQuestionIndex + 1,
|
||
});
|
||
}
|
||
}
|
||
|
||
// 显示结果消息
|
||
const messageText = isCorrect
|
||
? `太棒了!${question.question} 的答案是 ${question.options[question.correct]}`
|
||
: `再想想!正确答案是 ${question.options[question.correct]}`;
|
||
|
||
showMessage(isCorrect, messageText);
|
||
|
||
if (isCorrect) {
|
||
createConfetti();
|
||
}
|
||
}
|
||
|
||
// 显示消息
|
||
function showMessage(isCorrect, text) {
|
||
message.className = "message " + (isCorrect ? "correct" : "wrong");
|
||
messageTitle.textContent = isCorrect ? "太棒了!" : "再试一次";
|
||
messageText.textContent = text;
|
||
message.classList.add("show");
|
||
|
||
// 自动消失定时器
|
||
const autoHideTimer = setTimeout(() => {
|
||
nextQuestion();
|
||
}, 3000); // 3秒后自动消失
|
||
|
||
// 点击消息区域也可以消失
|
||
message.onclick = () => {
|
||
clearTimeout(autoHideTimer);
|
||
nextQuestion();
|
||
};
|
||
}
|
||
|
||
// 创建彩色纸屑效果
|
||
function createConfetti() {
|
||
const colors = ["#FF9AA2", "#FFB7B2", "#FFDAC1", "#E2F0CB", "#B5EAD7", "#C7CEEA"];
|
||
|
||
for (let i = 0; i < 50; i++) {
|
||
const confetti = document.createElement("div");
|
||
confetti.className = "confetti";
|
||
confetti.style.left = `${Math.random() * 100}%`;
|
||
confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
|
||
confetti.style.width = `${Math.random() * 10 + 5}px`;
|
||
confetti.style.height = `${Math.random() * 10 + 5}px`;
|
||
confetti.style.animation = `confetti-fall ${Math.random() * 2 + 2}s linear forwards`;
|
||
confetti.style.animationDelay = `${Math.random() * 0.5}s`;
|
||
|
||
document.body.appendChild(confetti);
|
||
|
||
setTimeout(() => {
|
||
confetti.remove();
|
||
}, 3000);
|
||
}
|
||
}
|
||
|
||
// 下一题
|
||
function nextQuestion() {
|
||
message.classList.remove("show");
|
||
currentQuestionIndex++;
|
||
showCurrentQuestion();
|
||
}
|
||
|
||
// 结束游戏
|
||
function endGame() {
|
||
const successRate = (correctCount / questions.length) * 100;
|
||
|
||
message.className = "message correct";
|
||
messageTitle.textContent = "游戏完成!";
|
||
messageText.textContent = `恭喜你完成了游戏!\n得分: ${score}\n正确率: ${successRate.toFixed(1)}%`;
|
||
message.classList.add("show");
|
||
|
||
// 虚拟老师游戏结束
|
||
if (window.gameVirtualTeacher) {
|
||
updateVirtualTeacherStatus('游戏结束!', true);
|
||
window.gameVirtualTeacher.gameEnd();
|
||
setTimeout(() => {
|
||
updateVirtualTeacherStatus('等待重新开始');
|
||
}, 2000);
|
||
}
|
||
|
||
// 记录游戏结果
|
||
if (gameStarted && window.gameTracker) {
|
||
if (successRate >= 80) {
|
||
window.gameTracker.recordWin(score, {
|
||
totalQuestions: questions.length,
|
||
correctCount: correctCount,
|
||
successRate: successRate,
|
||
});
|
||
} else {
|
||
window.gameTracker.recordLose(score, {
|
||
totalQuestions: questions.length,
|
||
correctCount: correctCount,
|
||
successRate: successRate,
|
||
});
|
||
}
|
||
}
|
||
|
||
messageBtn.textContent = "重新开始";
|
||
messageBtn.onclick = restartGame;
|
||
}
|
||
|
||
// 重新开始游戏
|
||
function restartGame() {
|
||
currentQuestionIndex = 0;
|
||
score = 0;
|
||
correctCount = 0;
|
||
scoreElement.textContent = "0";
|
||
progressElement.textContent = "0";
|
||
progressFill.style.width = "0%";
|
||
|
||
message.classList.remove("show");
|
||
messageBtn.textContent = "继续";
|
||
messageBtn.onclick = nextQuestion;
|
||
|
||
showCurrentQuestion();
|
||
}
|
||
|
||
// 事件监听
|
||
messageBtn.addEventListener("click", nextQuestion);
|
||
|
||
// 初始化游戏
|
||
setTimeout(() => {
|
||
// 虚拟老师游戏开始
|
||
if (window.gameVirtualTeacher) {
|
||
updateVirtualTeacherStatus('游戏开始!', true);
|
||
window.gameVirtualTeacher.gameStart(gameTitle);
|
||
setTimeout(() => {
|
||
if (!virtualTeacherMuted) {
|
||
window.gameVirtualTeacher.speak(gameIntro);
|
||
setTimeout(() => {
|
||
updateVirtualTeacherStatus('准备就绪');
|
||
}, 3000);
|
||
}
|
||
}, 1000);
|
||
}
|
||
|
||
// 开始游戏跟踪
|
||
if (window.gameTracker) {
|
||
window.gameTracker.startGame(gameId, {
|
||
gameType: "ai_generated",
|
||
difficulty: "adaptive",
|
||
subject: gameConfig.subject,
|
||
});
|
||
}
|
||
|
||
gameStarted = true;
|
||
showCurrentQuestion();
|
||
}, 500);
|
||
}
|
||
|
||
/**
|
||
* 关闭AI游戏
|
||
*/
|
||
closeAIGame() {
|
||
const gameContainer = document.getElementById('ai-game-container');
|
||
if (gameContainer) {
|
||
gameContainer.remove();
|
||
}
|
||
|
||
// 停止虚拟老师
|
||
if (window.gameVirtualTeacher) {
|
||
window.gameVirtualTeacher.stopAllSpeech();
|
||
}
|
||
|
||
console.log('🎮 AI游戏已关闭');
|
||
}
|
||
|
||
/**
|
||
* 创建游戏页面HTML
|
||
*/
|
||
createGamePageHTML(gameConfig) {
|
||
const gameId = gameConfig.id;
|
||
const subject = gameConfig.subject;
|
||
const gameType = gameConfig.type;
|
||
const questions = gameConfig.questions;
|
||
const difficulty = gameConfig.difficulty;
|
||
|
||
// 根据学科确定游戏标题和样式
|
||
let gameTitle, gameIntro, gameStyle;
|
||
|
||
switch (subject) {
|
||
case 'math':
|
||
if (gameType === 'quiz') {
|
||
gameTitle = 'AI数学游戏';
|
||
gameIntro = '欢迎来到AI数学游戏!我会帮你学习数学知识,准备好了吗?';
|
||
} else {
|
||
gameTitle = 'AI数学比大小游戏';
|
||
gameIntro = '欢迎来到AI数学比大小游戏!我会帮你学习数字比较,准备好了吗?';
|
||
}
|
||
gameStyle = this.getMathGameStyle();
|
||
break;
|
||
case 'language':
|
||
gameTitle = 'AI语文拼音游戏';
|
||
gameIntro = '欢迎来到AI语文拼音游戏!我会帮你学习拼音和声调,准备好了吗?';
|
||
gameStyle = this.getLanguageGameStyle();
|
||
break;
|
||
case 'life':
|
||
gameTitle = 'AI生活安全游戏';
|
||
gameIntro = '欢迎来到AI生活安全游戏!我会帮你学习生活中的安全知识,准备好了吗?';
|
||
gameStyle = this.getLifeGameStyle();
|
||
break;
|
||
default:
|
||
gameTitle = 'AI智能游戏';
|
||
gameIntro = '欢迎来到AI智能游戏!我会帮你学习知识,准备好了吗?';
|
||
gameStyle = this.getDefaultGameStyle();
|
||
}
|
||
|
||
// 生成游戏HTML
|
||
const gameHTML = `
|
||
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>${gameTitle}</title>
|
||
|
||
<!-- 引入必要的脚本 -->
|
||
<script src="../../js/apiService.js"></script>
|
||
<script src="../../js/dataManager.js"></script>
|
||
<script src="../../js/userManager.js"></script>
|
||
<script src="../../js/accessTracker.js"></script>
|
||
<script src="../../js/gameTracker.js"></script>
|
||
<script src="../../js/virtualTeacher.js"></script>
|
||
<script src="../../js/gameVirtualTeacher.js"></script>
|
||
|
||
<style>
|
||
${gameStyle}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="game-container">
|
||
<div class="score-area">
|
||
<div>得分: <span id="score">0</span></div>
|
||
<div>进度: <span id="progress">0</span>/<span id="total">${questions.length}</span></div>
|
||
</div>
|
||
|
||
<h1>${gameTitle}</h1>
|
||
|
||
<div class="progress-bar">
|
||
<div class="progress-fill" id="progressFill"></div>
|
||
</div>
|
||
|
||
<div class="question-area">
|
||
<div class="question" id="questionText">准备开始...</div>
|
||
<div class="options" id="optionsContainer">
|
||
<!-- 选项将通过JavaScript动态生成 -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="message" id="message">
|
||
<h2 id="messageTitle">标题</h2>
|
||
<p id="messageText">内容</p>
|
||
<button class="message-btn" id="messageBtn">继续</button>
|
||
</div>
|
||
|
||
<script>
|
||
// 游戏配置
|
||
const gameConfig = ${JSON.stringify(gameConfig)};
|
||
|
||
// DOM元素
|
||
const questionText = document.getElementById("questionText");
|
||
const optionsContainer = document.getElementById("optionsContainer");
|
||
const message = document.getElementById("message");
|
||
const messageTitle = document.getElementById("messageTitle");
|
||
const messageText = document.getElementById("messageText");
|
||
const messageBtn = document.getElementById("messageBtn");
|
||
const scoreElement = document.getElementById("score");
|
||
const progressElement = document.getElementById("progress");
|
||
const totalElement = document.getElementById("total");
|
||
const progressFill = document.getElementById("progressFill");
|
||
|
||
// 游戏状态
|
||
let currentQuestionIndex = 0;
|
||
let score = 0;
|
||
let correctCount = 0;
|
||
let gameStarted = false;
|
||
let gameId = "${gameId}";
|
||
|
||
// 显示当前题目
|
||
function showCurrentQuestion() {
|
||
if (currentQuestionIndex >= gameConfig.questions.length) {
|
||
endGame();
|
||
return;
|
||
}
|
||
|
||
const question = gameConfig.questions[currentQuestionIndex];
|
||
questionText.textContent = question.question;
|
||
|
||
// 清空选项容器
|
||
optionsContainer.innerHTML = "";
|
||
|
||
// 创建选项按钮
|
||
question.options.forEach((option, index) => {
|
||
const button = document.createElement("button");
|
||
button.className = "option-btn";
|
||
button.textContent = option;
|
||
button.onclick = () => selectAnswer(index, question);
|
||
optionsContainer.appendChild(button);
|
||
});
|
||
|
||
// 更新进度
|
||
progressElement.textContent = currentQuestionIndex + 1;
|
||
progressFill.style.width = \`\${
|
||
((currentQuestionIndex + 1) / gameConfig.questions.length) * 100
|
||
}%\`;
|
||
}
|
||
|
||
// 选择答案
|
||
function selectAnswer(selectedIndex, question) {
|
||
if (message.classList.contains("show")) return;
|
||
|
||
// 隐藏体感交互按钮
|
||
hideBodyInteraction();
|
||
|
||
const isCorrect = selectedIndex === question.correct;
|
||
const selectedButton = optionsContainer.children[selectedIndex];
|
||
const correctButton = optionsContainer.children[question.correct];
|
||
|
||
// 显示答案效果
|
||
selectedButton.classList.add(isCorrect ? "correct" : "wrong");
|
||
if (!isCorrect) {
|
||
correctButton.classList.add("correct");
|
||
}
|
||
|
||
// 虚拟老师反馈
|
||
if (window.gameVirtualTeacher && !virtualTeacherMuted) {
|
||
window.gameVirtualTeacher.stopAllSpeech();
|
||
updateVirtualTeacherStatus(isCorrect ? '答对了!' : '再想想...', true);
|
||
setTimeout(() => {
|
||
if (isCorrect) {
|
||
window.gameVirtualTeacher.recordCorrect();
|
||
} else {
|
||
window.gameVirtualTeacher.recordIncorrect();
|
||
}
|
||
setTimeout(() => {
|
||
updateVirtualTeacherStatus('准备就绪');
|
||
}, 1500);
|
||
}, 300);
|
||
}
|
||
|
||
// 更新分数
|
||
if (isCorrect) {
|
||
correctCount++;
|
||
score += 10;
|
||
scoreElement.textContent = score;
|
||
}
|
||
|
||
// 记录游戏数据
|
||
if (gameStarted && window.gameTracker) {
|
||
if (isCorrect) {
|
||
window.gameTracker.recordCorrect(10, {
|
||
question: question.question,
|
||
selected: question.options[selectedIndex],
|
||
correct: question.options[question.correct],
|
||
questionNumber: currentQuestionIndex + 1,
|
||
});
|
||
} else {
|
||
window.gameTracker.recordIncorrect(0, {
|
||
question: question.question,
|
||
selected: question.options[selectedIndex],
|
||
correct: question.options[question.correct],
|
||
questionNumber: currentQuestionIndex + 1,
|
||
});
|
||
}
|
||
}
|
||
|
||
// 显示结果消息
|
||
const messageText = isCorrect
|
||
? \`太棒了!\${question.question} 的答案是 \${question.options[question.correct]}\`
|
||
: \`再想想!正确答案是 \${question.options[question.correct]}\`;
|
||
|
||
showMessage(isCorrect, messageText);
|
||
|
||
if (isCorrect) {
|
||
createConfetti();
|
||
}
|
||
}
|
||
|
||
// 下一题
|
||
function nextQuestion() {
|
||
message.classList.remove("show");
|
||
currentQuestionIndex++;
|
||
showCurrentQuestion();
|
||
}
|
||
|
||
// 结束游戏
|
||
function endGame() {
|
||
const successRate = (correctCount / gameConfig.questions.length) * 100;
|
||
|
||
message.className = "message correct";
|
||
messageTitle.textContent = "游戏完成!";
|
||
messageText.textContent = \`恭喜你完成了游戏!\\n得分: \${score}\\n正确率: \${successRate.toFixed(1)}%\`;
|
||
message.classList.add("show");
|
||
|
||
// 虚拟老师游戏结束
|
||
if (window.gameVirtualTeacher) {
|
||
updateVirtualTeacherStatus('游戏结束!', true);
|
||
window.gameVirtualTeacher.gameEnd();
|
||
setTimeout(() => {
|
||
updateVirtualTeacherStatus('等待重新开始');
|
||
}, 2000);
|
||
}
|
||
|
||
// 记录游戏结果
|
||
if (gameStarted && window.gameTracker) {
|
||
if (successRate >= 80) {
|
||
window.gameTracker.recordWin(score, {
|
||
totalQuestions: gameConfig.questions.length,
|
||
correctCount: correctCount,
|
||
successRate: successRate,
|
||
});
|
||
} else {
|
||
window.gameTracker.recordLose(score, {
|
||
totalQuestions: gameConfig.questions.length,
|
||
correctCount: correctCount,
|
||
successRate: successRate,
|
||
});
|
||
}
|
||
}
|
||
|
||
messageBtn.textContent = "重新开始";
|
||
messageBtn.onclick = restartGame;
|
||
|
||
// 游戏结束消息不自动消失,需要用户点击
|
||
// 清除之前的点击事件
|
||
message.onclick = null;
|
||
}
|
||
|
||
// 重新开始游戏
|
||
function restartGame() {
|
||
currentQuestionIndex = 0;
|
||
score = 0;
|
||
correctCount = 0;
|
||
scoreElement.textContent = "0";
|
||
progressElement.textContent = "0";
|
||
progressFill.style.width = "0%";
|
||
|
||
message.classList.remove("show");
|
||
messageBtn.textContent = "继续";
|
||
messageBtn.onclick = nextQuestion;
|
||
|
||
showCurrentQuestion();
|
||
}
|
||
|
||
// 事件监听
|
||
messageBtn.addEventListener("click", nextQuestion);
|
||
|
||
// 初始化游戏
|
||
setTimeout(() => {
|
||
// 虚拟老师游戏开始
|
||
if (window.gameVirtualTeacher) {
|
||
window.gameVirtualTeacher.gameStart("${gameTitle}");
|
||
setTimeout(() => {
|
||
window.gameVirtualTeacher.speak("${gameIntro}");
|
||
}, 1000);
|
||
}
|
||
|
||
// 开始游戏跟踪
|
||
if (window.gameTracker) {
|
||
window.gameTracker.startGame(gameId, {
|
||
gameType: "ai_generated",
|
||
difficulty: "adaptive",
|
||
subject: "${subject}",
|
||
});
|
||
}
|
||
|
||
gameStarted = true;
|
||
showCurrentQuestion();
|
||
}, 500);
|
||
</script>
|
||
</body>
|
||
</html>`;
|
||
|
||
return gameHTML;
|
||
}
|
||
|
||
/**
|
||
* 获取数学游戏样式
|
||
*/
|
||
getMathGameStyle() {
|
||
return `
|
||
:root {
|
||
--primary: #ff9aa2;
|
||
--secondary: #ffb7b2;
|
||
--accent: #ffdac1;
|
||
--correct: #b5ead7;
|
||
--wrong: #ff9aa2;
|
||
--button: #70a1ff;
|
||
--text: #5e5346;
|
||
}
|
||
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: "Comic Sans MS", "Marker Felt", "微软雅黑", sans-serif;
|
||
background-color: #f9f7f0;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
min-height: 100vh;
|
||
background-image: radial-gradient(circle, #f5f5f5 10%, transparent 10%);
|
||
background-size: 30px 30px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.game-container {
|
||
width: 80%;
|
||
background-color: white;
|
||
border-radius: 20px;
|
||
padding: 30px;
|
||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||
text-align: center;
|
||
position: relative;
|
||
overflow: hidden;
|
||
border: 5px solid #ffdac1;
|
||
}
|
||
|
||
h1 {
|
||
color: #ff6b6b;
|
||
margin-bottom: 30px;
|
||
font-size: 3rem;
|
||
text-shadow: 3px 3px 0 #ffdac1;
|
||
letter-spacing: 2px;
|
||
}
|
||
|
||
.question-area {
|
||
margin: 30px 0;
|
||
padding: 20px;
|
||
background-color: #f0f8ff;
|
||
border-radius: 15px;
|
||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.question {
|
||
font-size: 4rem;
|
||
margin: 20px 0;
|
||
color: #5e5346;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.options {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 20px;
|
||
margin: 30px 0;
|
||
}
|
||
|
||
.option-btn {
|
||
padding: 20px;
|
||
border-radius: 15px;
|
||
background-color: var(--button);
|
||
color: white;
|
||
border: none;
|
||
font-size: 2.5rem;
|
||
font-weight: bold;
|
||
cursor: pointer;
|
||
box-shadow: 0 5px 0 #4a6baf;
|
||
transition: all 0.2s ease;
|
||
min-height: 80px;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.option-btn:hover {
|
||
background-color: #5d8eff;
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.option-btn:active {
|
||
transform: translateY(5px);
|
||
box-shadow: 0 2px 0 #4a6baf;
|
||
}
|
||
|
||
.option-btn.correct {
|
||
background-color: var(--correct);
|
||
box-shadow: 0 5px 0 #8dd3c7;
|
||
}
|
||
|
||
.option-btn.wrong {
|
||
background-color: var(--wrong);
|
||
box-shadow: 0 5px 0 #e67e7e;
|
||
}
|
||
|
||
.score-area {
|
||
position: absolute;
|
||
top: 20px;
|
||
right: 20px;
|
||
background-color: #fff;
|
||
padding: 15px;
|
||
border-radius: 10px;
|
||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
|
||
font-size: 1.2rem;
|
||
font-weight: bold;
|
||
color: var(--text);
|
||
}
|
||
|
||
.progress-bar {
|
||
width: 100%;
|
||
height: 20px;
|
||
background-color: #e0e0e0;
|
||
border-radius: 10px;
|
||
margin: 20px 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.progress-fill {
|
||
height: 100%;
|
||
background-color: var(--button);
|
||
transition: width 0.3s ease;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.message {
|
||
position: fixed;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%) scale(0);
|
||
background-color: white;
|
||
padding: 30px;
|
||
border-radius: 15px;
|
||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||
z-index: 100;
|
||
text-align: center;
|
||
max-width: 80%;
|
||
transition: all 0.3s ease;
|
||
border: 5px solid;
|
||
}
|
||
|
||
.message.show {
|
||
transform: translate(-50%, -50%) scale(1);
|
||
}
|
||
|
||
.message.correct {
|
||
border-color: #b5ead7;
|
||
background-color: #e8f8f3;
|
||
}
|
||
|
||
.message.wrong {
|
||
border-color: #ff9aa2;
|
||
background-color: #ffeef0;
|
||
}
|
||
|
||
.message h2 {
|
||
font-size: 3rem;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.message p {
|
||
font-size: 2rem;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.message-btn {
|
||
padding: 10px 20px;
|
||
border-radius: 50px;
|
||
border: none;
|
||
background-color: var(--button);
|
||
color: white;
|
||
font-size: 2rem;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.message-btn:hover {
|
||
background-color: #5d8eff;
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.confetti {
|
||
position: absolute;
|
||
width: 15px;
|
||
height: 15px;
|
||
background-color: var(--primary);
|
||
opacity: 0;
|
||
z-index: 90;
|
||
}
|
||
|
||
@keyframes confetti-fall {
|
||
0% {
|
||
transform: translateY(-100vh) rotate(0deg);
|
||
opacity: 1;
|
||
}
|
||
100% {
|
||
transform: translateY(100vh) rotate(360deg);
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
.game-container {
|
||
width: 95%;
|
||
padding: 20px;
|
||
}
|
||
|
||
h1 {
|
||
font-size: 2rem;
|
||
}
|
||
|
||
.question {
|
||
font-size: 2.5rem;
|
||
}
|
||
|
||
.options {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.option-btn {
|
||
font-size: 2rem;
|
||
}
|
||
}`;
|
||
}
|
||
|
||
/**
|
||
* 获取语文游戏样式
|
||
*/
|
||
getLanguageGameStyle() {
|
||
return this.getMathGameStyle().replace('--button: #70a1ff;', '--button: #ff6b9d;');
|
||
}
|
||
|
||
/**
|
||
* 获取生活游戏样式
|
||
*/
|
||
getLifeGameStyle() {
|
||
return this.getMathGameStyle().replace('--button: #70a1ff;', '--button: #4ecdc4;');
|
||
}
|
||
|
||
/**
|
||
* 获取默认游戏样式
|
||
*/
|
||
getDefaultGameStyle() {
|
||
return this.getMathGameStyle();
|
||
}
|
||
|
||
/**
|
||
* 从题目库获取题目
|
||
*/
|
||
getQuestionsFromBank(subject, questionCount) {
|
||
const subjectQuestions = this.questionBank.get(subject);
|
||
if (!subjectQuestions) {
|
||
console.warn(`❌ 未找到学科 ${subject} 的题目库`);
|
||
return [];
|
||
}
|
||
|
||
const allQuestions = [];
|
||
|
||
// 收集所有题目
|
||
for (const [type, questions] of Object.entries(subjectQuestions)) {
|
||
allQuestions.push(...questions);
|
||
}
|
||
|
||
// 根据难度筛选题目
|
||
const suitableQuestions = allQuestions.filter(q =>
|
||
q.difficulty <= this.currentDifficulty
|
||
);
|
||
|
||
if (suitableQuestions.length === 0) {
|
||
console.warn(`❌ 未找到适合难度 ${this.currentDifficulty} 的题目`);
|
||
return [];
|
||
}
|
||
|
||
// 随机选择题目
|
||
const selectedQuestions = [];
|
||
const shuffled = [...suitableQuestions].sort(() => Math.random() - 0.5);
|
||
|
||
for (let i = 0; i < Math.min(questionCount, shuffled.length); i++) {
|
||
selectedQuestions.push({
|
||
...shuffled[i],
|
||
id: `q_${Date.now()}_${i}`
|
||
});
|
||
}
|
||
|
||
return selectedQuestions;
|
||
}
|
||
|
||
/**
|
||
* 选择题目类型
|
||
*/
|
||
selectQuestionTypes(subject) {
|
||
const availableTypes = Object.keys(this.questionTypes[subject] || {});
|
||
const studentStrengths = this.studentProfile.strengths;
|
||
const studentWeaknesses = this.studentProfile.weaknesses;
|
||
|
||
// 优先选择学生擅长的类型,但也包含需要练习的类型
|
||
let selectedTypes = [];
|
||
|
||
// 70%选择擅长的类型
|
||
const strengthCount = Math.ceil(availableTypes.length * 0.7);
|
||
for (let i = 0; i < strengthCount && i < availableTypes.length; i++) {
|
||
selectedTypes.push(availableTypes[i]);
|
||
}
|
||
|
||
// 30%选择需要练习的类型
|
||
const weaknessCount = availableTypes.length - strengthCount;
|
||
for (let i = 0; i < weaknessCount && i < availableTypes.length; i++) {
|
||
const type = availableTypes[(strengthCount + i) % availableTypes.length];
|
||
if (!selectedTypes.includes(type)) {
|
||
selectedTypes.push(type);
|
||
}
|
||
}
|
||
|
||
return selectedTypes;
|
||
}
|
||
|
||
/**
|
||
* 生成单个题目
|
||
*/
|
||
generateQuestion(subject, questionType) {
|
||
const subjectBank = this.questionBank.get(subject);
|
||
if (!subjectBank || !subjectBank[questionType]) {
|
||
return null;
|
||
}
|
||
|
||
const questions = subjectBank[questionType];
|
||
const suitableQuestions = questions.filter(q => q.difficulty <= this.currentDifficulty);
|
||
|
||
if (suitableQuestions.length === 0) {
|
||
return null;
|
||
}
|
||
|
||
// 随机选择一个题目
|
||
const selectedQuestion = suitableQuestions[Math.floor(Math.random() * suitableQuestions.length)];
|
||
|
||
// 根据当前难度调整题目
|
||
return this.adjustQuestionDifficulty(selectedQuestion);
|
||
}
|
||
|
||
/**
|
||
* 调整题目难度
|
||
*/
|
||
adjustQuestionDifficulty(question) {
|
||
const adjustedQuestion = { ...question };
|
||
|
||
// 根据当前难度调整选项数量
|
||
if (this.currentDifficulty <= 2) {
|
||
// 简单难度:减少选项数量
|
||
adjustedQuestion.options = adjustedQuestion.options.slice(0, 3);
|
||
} else if (this.currentDifficulty >= 4) {
|
||
// 困难难度:增加干扰选项
|
||
if (adjustedQuestion.options.length < 4) {
|
||
adjustedQuestion.options.push("不知道");
|
||
}
|
||
}
|
||
|
||
return adjustedQuestion;
|
||
}
|
||
|
||
/**
|
||
* 个性化游戏配置
|
||
*/
|
||
personalizeGame(gameConfig) {
|
||
const profile = this.studentProfile;
|
||
|
||
// 根据学习风格调整
|
||
if (profile.learningStyle === 'visual') {
|
||
gameConfig.visualAids = true;
|
||
gameConfig.images = true;
|
||
} else if (profile.learningStyle === 'auditory') {
|
||
gameConfig.audioHints = true;
|
||
gameConfig.voiceReading = true;
|
||
} else if (profile.learningStyle === 'kinesthetic') {
|
||
gameConfig.interactiveElements = true;
|
||
gameConfig.handOnActivities = true;
|
||
}
|
||
|
||
// 根据年级调整
|
||
if (profile.grade <= 1) {
|
||
gameConfig.simpleLanguage = true;
|
||
gameConfig.bigFonts = true;
|
||
gameConfig.moreHints = true;
|
||
} else if (profile.grade >= 3) {
|
||
gameConfig.complexQuestions = true;
|
||
gameConfig.multipleSteps = true;
|
||
}
|
||
|
||
// 根据正确率调整
|
||
if (profile.correctRate < 0.6) {
|
||
gameConfig.easierQuestions = true;
|
||
gameConfig.moreTime = true;
|
||
} else if (profile.correctRate > 0.8) {
|
||
gameConfig.challengingQuestions = true;
|
||
gameConfig.bonusPoints = true;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 动态调整难度
|
||
*/
|
||
adjustDifficulty(performance) {
|
||
const { correct, total, timeSpent, hintsUsed } = performance;
|
||
const successRate = correct / total;
|
||
const avgTimePerQuestion = timeSpent / total;
|
||
|
||
// 记录性能历史
|
||
this.performanceHistory.push({
|
||
timestamp: Date.now(),
|
||
successRate,
|
||
avgTimePerQuestion,
|
||
hintsUsed,
|
||
difficulty: this.currentDifficulty
|
||
});
|
||
|
||
// 根据成功率调整难度
|
||
if (successRate >= 0.8 && this.currentDifficulty < 5) {
|
||
this.currentDifficulty++;
|
||
console.log(`📈 难度提升到 ${this.currentDifficulty}`);
|
||
} else if (successRate < 0.6 && this.currentDifficulty > 1) {
|
||
this.currentDifficulty--;
|
||
console.log(`📉 难度降低到 ${this.currentDifficulty}`);
|
||
}
|
||
|
||
// 根据答题速度调整
|
||
const expectedTime = this.difficultyParams[this.currentDifficulty].timeLimit;
|
||
if (avgTimePerQuestion < expectedTime * 0.5) {
|
||
// 答题太快,可能太简单
|
||
if (this.currentDifficulty < 5) {
|
||
this.currentDifficulty++;
|
||
console.log(`⚡ 答题速度过快,难度提升到 ${this.currentDifficulty}`);
|
||
}
|
||
} else if (avgTimePerQuestion > expectedTime * 1.5) {
|
||
// 答题太慢,可能太难
|
||
if (this.currentDifficulty > 1) {
|
||
this.currentDifficulty--;
|
||
console.log(`🐌 答题速度过慢,难度降低到 ${this.currentDifficulty}`);
|
||
}
|
||
}
|
||
|
||
return this.currentDifficulty;
|
||
}
|
||
|
||
/**
|
||
* 生成游戏ID
|
||
*/
|
||
generateGameId() {
|
||
return `ai_game_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||
}
|
||
|
||
/**
|
||
* 获取游戏统计
|
||
*/
|
||
getGameStats() {
|
||
return {
|
||
currentDifficulty: this.currentDifficulty,
|
||
totalGames: this.performanceHistory.length,
|
||
averageSuccessRate: this.performanceHistory.reduce((sum, p) => sum + p.successRate, 0) / this.performanceHistory.length,
|
||
difficultyHistory: this.difficultyHistory,
|
||
performanceHistory: this.performanceHistory
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 重置游戏生成器
|
||
*/
|
||
reset() {
|
||
this.currentDifficulty = 1;
|
||
this.difficultyHistory = [];
|
||
this.performanceHistory = [];
|
||
console.log('🔄 AI游戏生成器已重置');
|
||
}
|
||
}
|
||
|
||
// 创建全局实例
|
||
window.aiGameGenerator = new AIGameGenerator();
|
||
|
||
// 全局关闭函数
|
||
window.closeAIGame = function() {
|
||
const gameContainer = document.getElementById('ai-game-container');
|
||
if (gameContainer) {
|
||
gameContainer.remove();
|
||
}
|
||
|
||
// 停止虚拟老师
|
||
if (window.gameVirtualTeacher) {
|
||
window.gameVirtualTeacher.stopAllSpeech();
|
||
}
|
||
|
||
console.log('🎮 AI游戏已关闭');
|
||
};
|
||
|
||
// 全局虚拟猫头鹰控制函数
|
||
window.toggleVirtualTeacher = function() {
|
||
// 这个函数会在游戏内部被定义
|
||
if (typeof toggleVirtualTeacher === 'function') {
|
||
toggleVirtualTeacher();
|
||
}
|
||
};
|
||
|
||
window.muteVirtualTeacher = function() {
|
||
// 这个函数会在游戏内部被定义
|
||
if (typeof muteVirtualTeacher === 'function') {
|
||
muteVirtualTeacher();
|
||
}
|
||
};
|
||
|
||
// 全局体感交互函数
|
||
window.selectBodyAnswer = function(hand) {
|
||
// 这个函数会在游戏内部被定义
|
||
if (typeof selectBodyAnswer === 'function') {
|
||
selectBodyAnswer(hand);
|
||
}
|
||
};
|
||
|
||
// 导出类
|
||
if (typeof module !== 'undefined' && module.exports) {
|
||
module.exports = AIGameGenerator;
|
||
}
|