forked from admin/french-vocab
Update: TTS settings modal and settings types
This commit is contained in:
@@ -1,23 +1,58 @@
|
||||
import type { StudyProgress, DifficultyRating } from '../types/vocabulary';
|
||||
|
||||
interface SRSConfig {
|
||||
initialEase: number;
|
||||
minEase: number;
|
||||
maxInterval: number;
|
||||
learningStepInterval: number;
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG: SRSConfig = {
|
||||
initialEase: 2.5,
|
||||
minEase: 1.3,
|
||||
maxInterval: 365, // 最大间隔 365 天
|
||||
learningStepInterval: 10, // 学习步骤间隔(分钟)
|
||||
};
|
||||
|
||||
/**
|
||||
* 改进的 SM-2 算法实现
|
||||
* 基于 Anki 和 SuperMemo 的研究
|
||||
*/
|
||||
export function calculateNextReview(
|
||||
progress: StudyProgress,
|
||||
rating: DifficultyRating
|
||||
rating: DifficultyRating,
|
||||
config: SRSConfig = DEFAULT_CONFIG
|
||||
): StudyProgress {
|
||||
const newProgress = { ...progress };
|
||||
const { minEase, maxInterval } = config;
|
||||
|
||||
// 如果是新单词(repetitions === 0),使用学习步骤
|
||||
if (progress.repetitions === 0 && rating === 'again') {
|
||||
// 标记为"再次",保持在第一步
|
||||
newProgress.interval = 0;
|
||||
newProgress.easeFactor = Math.max(minEase, progress.easeFactor - 0.2);
|
||||
newProgress.nextReviewDate = new Date(Date.now() + DEFAULT_CONFIG.learningStepInterval * 60 * 1000);
|
||||
newProgress.lastStudiedDate = new Date();
|
||||
return newProgress;
|
||||
}
|
||||
|
||||
switch (rating) {
|
||||
case 'again':
|
||||
// 完全重置,但保留一些学习历史
|
||||
newProgress.interval = 1;
|
||||
newProgress.repetitions = 0;
|
||||
newProgress.easeFactor = Math.max(1.3, progress.easeFactor - 0.2);
|
||||
newProgress.easeFactor = Math.max(minEase, progress.easeFactor - 0.2);
|
||||
break;
|
||||
|
||||
case 'hard':
|
||||
newProgress.interval = Math.round(progress.interval * 1.2);
|
||||
// 困难:间隔增长较慢
|
||||
newProgress.interval = Math.max(1, Math.round(progress.interval * 1.2));
|
||||
newProgress.repetitions += 1;
|
||||
newProgress.easeFactor = Math.max(1.3, progress.easeFactor - 0.15);
|
||||
newProgress.easeFactor = Math.max(minEase, progress.easeFactor - 0.15);
|
||||
break;
|
||||
|
||||
case 'good':
|
||||
// 良好:标准 SM-2 算法
|
||||
if (progress.repetitions === 0) {
|
||||
newProgress.interval = 1;
|
||||
} else if (progress.repetitions === 1) {
|
||||
@@ -27,7 +62,9 @@ export function calculateNextReview(
|
||||
}
|
||||
newProgress.repetitions += 1;
|
||||
break;
|
||||
|
||||
case 'easy':
|
||||
// 简单:间隔增长更快
|
||||
if (progress.repetitions === 0) {
|
||||
newProgress.interval = 4;
|
||||
} else {
|
||||
@@ -38,9 +75,52 @@ export function calculateNextReview(
|
||||
break;
|
||||
}
|
||||
|
||||
// 限制最大间隔
|
||||
newProgress.interval = Math.min(newProgress.interval, maxInterval);
|
||||
|
||||
// 确保 easeFactor 在合理范围内
|
||||
newProgress.easeFactor = Math.max(minEase, Math.min(newProgress.easeFactor, 3.0));
|
||||
|
||||
// 计算下次复习日期
|
||||
const now = new Date();
|
||||
newProgress.nextReviewDate = new Date(now.getTime() + newProgress.interval * 24 * 60 * 60 * 1000);
|
||||
newProgress.lastStudiedDate = now;
|
||||
|
||||
return newProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算单词的掌握程度 (0-100%)
|
||||
*/
|
||||
export function calculateMastery(progress: StudyProgress): number {
|
||||
if (progress.repetitions === 0) return 0;
|
||||
|
||||
const baseScore = Math.min(progress.repetitions * 20, 80);
|
||||
const intervalBonus = Math.min(progress.interval / 30 * 20, 20);
|
||||
|
||||
return Math.min(100, Math.round(baseScore + intervalBonus));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取复习优先级分数(越高越优先)
|
||||
*/
|
||||
export function getReviewPriority(progress: StudyProgress): number {
|
||||
const now = new Date();
|
||||
const nextReview = new Date(progress.nextReviewDate);
|
||||
const overdue = now.getTime() - nextReview.getTime();
|
||||
|
||||
// 逾期的单词优先级更高
|
||||
if (overdue > 0) {
|
||||
return 1000 + Math.min(overdue / (1000 * 60 * 60), 1000); // 每小时增加 1 点优先级
|
||||
}
|
||||
|
||||
// 即将到期的单词
|
||||
const timeUntilDue = nextReview.getTime() - now.getTime();
|
||||
const hoursUntilDue = timeUntilDue / (1000 * 60 * 60);
|
||||
|
||||
if (hoursUntilDue < 24) {
|
||||
return 500 + (24 - hoursUntilDue) * 20;
|
||||
}
|
||||
|
||||
return hoursUntilDue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user