| class LearningLogic { |
| constructor() { |
| this.currentDay = 1; |
| this.maxDays = 10; |
| this.rewardSystem = new RewardSystem(); |
| this.utils = Utils; |
| this.userProgress = this.loadUserProgress(); |
| } |
|
|
| loadUserProgress() { |
| const defaultProgress = { |
| totalScore: 0, |
| completedLessons: [], |
| quizScores: {}, |
| exerciseScores: {}, |
| lessonScores: {}, |
| currentStreak: 0, |
| lastActivityDate: null, |
| achievements: [], |
| level: 1, |
| timeSpent: 0 |
| }; |
|
|
| const savedProgress = this.utils.loadFromLocalStorage('userProgress'); |
| return { ...defaultProgress, ...savedProgress }; |
| } |
|
|
| saveUserProgress() { |
| return this.utils.saveToLocalStorage('userProgress', this.userProgress); |
| } |
|
|
| async loadLesson(day) { |
| try { |
| const lessonData = await this.utils.loadJSON(`data/lessons/day${day}.json`); |
| return lessonData; |
| } catch (error) { |
| console.error(`Error loading lesson day ${day}:`, error); |
| return null; |
| } |
| } |
|
|
| async loadQuiz(day) { |
| try { |
| const quizData = await this.utils.loadJSON(`data/quizzes/day${day}.json`); |
| return quizData; |
| } catch (error) { |
| console.error(`Error loading quiz day ${day}:`, error); |
| return null; |
| } |
| } |
|
|
| async loadExercise(day) { |
| try { |
| const exerciseData = await this.utils.loadJSON(`data/exercises/day${day}.json`); |
| return exerciseData; |
| } catch (error) { |
| console.error(`Error loading exercise day ${day}:`, error); |
| return null; |
| } |
| } |
|
|
| checkQuizAnswers(day, userAnswers, timeSpent = 0) { |
| return this.loadQuiz(day).then(quizData => { |
| if (!quizData) { |
| throw new Error('Quiz data not found'); |
| } |
|
|
| let correctCount = 0; |
| const results = []; |
|
|
| quizData.questions.forEach((question, index) => { |
| const userAnswer = userAnswers[index]; |
| const isCorrect = userAnswer === question.correct_index; |
| |
| if (isCorrect) { |
| correctCount++; |
| } |
|
|
| results.push({ |
| questionId: question.id, |
| questionText: question.question, |
| userAnswer: userAnswer, |
| correctAnswer: question.correct_index, |
| isCorrect: isCorrect, |
| explanation: question.explanation || '' |
| }); |
| }); |
|
|
| const totalQuestions = quizData.questions.length; |
| const streak = this.calculateStreak(results); |
| const reward = this.rewardSystem.calculateQuizReward( |
| correctCount, |
| totalQuestions, |
| timeSpent, |
| streak |
| ); |
|
|
| |
| this.updateUserProgress({ |
| score: reward, |
| completedQuiz: `day_${day}`, |
| correctAnswers: correctCount, |
| totalQuestions: totalQuestions |
| }); |
|
|
| const feedback = this.rewardSystem.getRewardFeedback( |
| reward, |
| totalQuestions * 3, |
| 'quiz' |
| ); |
|
|
| return { |
| results: results, |
| correctCount: correctCount, |
| totalQuestions: totalQuestions, |
| reward: reward, |
| feedback: feedback, |
| streak: streak |
| }; |
| }); |
| } |
|
|
| checkExerciseAnswer(day, userAnswer) { |
| return this.loadExercise(day).then(exerciseData => { |
| if (!exerciseData) { |
| throw new Error('Exercise data not found'); |
| } |
|
|
| const exercise = exerciseData.exercise; |
| const userAnswerLower = userAnswer.toLowerCase(); |
| |
| let matches = 0; |
| const matchedKeywords = []; |
|
|
| exercise.expected_keywords.forEach(keyword => { |
| const keywordLower = keyword.toLowerCase(); |
| |
| if (userAnswerLower.includes(keywordLower)) { |
| matches++; |
| matchedKeywords.push(keyword); |
| } |
| }); |
|
|
| const matchThreshold = exercise.match_threshold || 0.6; |
| const isCorrect = matches >= exercise.expected_keywords.length * matchThreshold; |
|
|
| const reward = this.rewardSystem.calculateExerciseReward( |
| isCorrect, |
| matches, |
| exercise.expected_keywords.length, |
| exercise.complexity || 1 |
| ); |
|
|
| |
| if (isCorrect) { |
| this.updateUserProgress({ |
| score: reward, |
| completedExercise: `day_${day}`, |
| keywordMatches: matches, |
| totalKeywords: exercise.expected_keywords.length |
| }); |
| } |
|
|
| const feedback = this.rewardSystem.getRewardFeedback( |
| reward, |
| 25, |
| 'exercise' |
| ); |
|
|
| return { |
| isCorrect: isCorrect, |
| matchedKeywords: matchedKeywords, |
| expectedKeywords: exercise.expected_keywords, |
| reward: reward, |
| feedback: feedback, |
| hint: exercise.hint || '' |
| }; |
| }); |
| } |
|
|
| completeLesson(day, duration, interactions = 0) { |
| |
| const reward = this.rewardSystem.calculateLessonReward(duration, interactions); |
|
|
| this.updateUserProgress({ |
| score: reward, |
| completedLesson: day, |
| lessonDuration: duration, |
| interactions: interactions |
| }); |
|
|
| return { |
| reward: reward, |
| feedback: this.rewardSystem.getRewardFeedback(reward, 20, 'lesson'), |
| completed: true |
| }; |
| } |
|
|
| updateUserProgress(updates) { |
| const previousScore = this.userProgress.totalScore; |
| const previousStreak = this.userProgress.currentStreak; |
|
|
| |
| if (updates.score) { |
| this.userProgress.totalScore += updates.score; |
| } |
|
|
| |
| if (updates.completedLesson && !this.userProgress.completedLessons.includes(updates.completedLesson)) { |
| this.userProgress.completedLessons.push(updates.completedLesson); |
| } |
|
|
| |
| if (updates.completedQuiz) { |
| this.userProgress.quizScores[updates.completedQuiz] = { |
| score: updates.score, |
| correctAnswers: updates.correctAnswers, |
| totalQuestions: updates.totalQuestions, |
| completedAt: new Date().toISOString() |
| }; |
| } |
|
|
| |
| if (updates.completedExercise) { |
| this.userProgress.exerciseScores[updates.completedExercise] = { |
| score: updates.score, |
| keywordMatches: updates.keywordMatches, |
| totalKeywords: updates.totalKeywords, |
| completedAt: new Date().toISOString() |
| }; |
| } |
|
|
| |
| this.updateStreak(); |
|
|
| |
| this.checkAchievements(); |
|
|
| |
| const levelInfo = this.rewardSystem.calculateLevel(this.userProgress.totalScore); |
| this.userProgress.level = levelInfo.level; |
|
|
| |
| this.saveUserProgress(); |
|
|
| |
| return { |
| scoreIncrease: this.userProgress.totalScore - previousScore, |
| streakIncrease: this.userProgress.currentStreak - previousStreak, |
| newLevel: levelInfo.level !== Math.floor(this.rewardSystem.calculateLevel(previousScore).level), |
| levelInfo: levelInfo |
| }; |
| } |
|
|
| updateStreak() { |
| const today = this.utils.getCurrentDate(); |
| const lastActivity = this.userProgress.lastActivityDate; |
|
|
| if (lastActivity === today) { |
| return; |
| } |
|
|
| if (!lastActivity) { |
| |
| this.userProgress.currentStreak = 1; |
| } else { |
| const lastDate = new Date(lastActivity); |
| const currentDate = new Date(today); |
| const diffTime = currentDate - lastDate; |
| const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24)); |
|
|
| if (diffDays === 1) { |
| |
| this.userProgress.currentStreak++; |
| } else if (diffDays > 1) { |
| |
| this.userProgress.currentStreak = 1; |
| } |
| } |
|
|
| this.userProgress.lastActivityDate = today; |
| } |
|
|
| checkAchievements() { |
| const achievements = []; |
| const currentAchievements = this.userProgress.achievements || []; |
|
|
| |
| if (this.userProgress.completedLessons.length >= 1 && |
| !currentAchievements.includes('first_lesson')) { |
| achievements.push('first_lesson'); |
| } |
|
|
| |
| const perfectQuizzes = Object.values(this.userProgress.quizScores) |
| .filter(quiz => quiz.correctAnswers === quiz.totalQuestions).length; |
| if (perfectQuizzes >= 5 && !currentAchievements.includes('quiz_master')) { |
| achievements.push('quiz_master'); |
| } |
|
|
| |
| const completedExercises = Object.keys(this.userProgress.exerciseScores).length; |
| if (completedExercises >= 3 && !currentAchievements.includes('exercise_pro')) { |
| achievements.push('exercise_pro'); |
| } |
|
|
| |
| if (this.userProgress.currentStreak >= 7 && |
| !currentAchievements.includes('week_streak')) { |
| achievements.push('week_streak'); |
| } |
|
|
| |
| achievements.forEach(achievement => { |
| if (!currentAchievements.includes(achievement)) { |
| this.userProgress.achievements.push(achievement); |
| |
| const reward = this.rewardSystem.getAchievementReward(achievement); |
| this.userProgress.totalScore += reward; |
| } |
| }); |
|
|
| return achievements; |
| } |
|
|
| getUserStats() { |
| const completedLessons = this.userProgress.completedLessons.length; |
| const completedQuizzes = Object.keys(this.userProgress.quizScores).length; |
| const completedExercises = Object.keys(this.userProgress.exerciseScores).length; |
| |
| const totalQuizzes = Object.values(this.userProgress.quizScores).reduce( |
| (sum, quiz) => sum + quiz.totalQuestions, 0 |
| ); |
| const correctQuizzes = Object.values(this.userProgress.quizScores).reduce( |
| (sum, quiz) => sum + quiz.correctAnswers, 0 |
| ); |
| |
| const accuracy = totalQuizzes > 0 ? (correctQuizzes / totalQuizzes) * 100 : 0; |
|
|
| return { |
| totalScore: this.userProgress.totalScore, |
| level: this.userProgress.level, |
| streak: this.userProgress.currentStreak, |
| completedLessons: completedLessons, |
| completedQuizzes: completedQuizzes, |
| completedExercises: completedExercises, |
| accuracy: Math.round(accuracy), |
| achievements: this.userProgress.achievements.length, |
| rank: this.rewardSystem.getRankTitle(this.userProgress.level) |
| }; |
| } |
|
|
| resetProgress() { |
| this.userProgress = { |
| totalScore: 0, |
| completedLessons: [], |
| quizScores: {}, |
| exerciseScores: {}, |
| lessonScores: {}, |
| currentStreak: 0, |
| lastActivityDate: null, |
| achievements: [], |
| level: 1, |
| timeSpent: 0 |
| }; |
| this.saveUserProgress(); |
| return this.userProgress; |
| } |
|
|
| getLeaderboard(limit = 10) { |
| |
| |
| const stats = this.getUserStats(); |
| return [{ |
| username: 'شما', |
| score: stats.totalScore, |
| level: stats.level, |
| rank: stats.rank, |
| streak: stats.streak |
| }]; |
| } |
| } |
|
|
| |
| const learningLogic = new LearningLogic(); |