All files / web/src/lib/curriculum/bkt skill-priors.ts

73.87% Statements 82/111
80% Branches 12/15
50% Functions 1/2
73.87% Lines 82/111

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 1122x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 4941x 4941x 4941x 4941x 4941x 4941x 4941x 1024x 1024x 1024x 1024x 1024x 1024x 1024x 3917x 3917x 3917x 4941x 1172x 1172x 1172x 1172x 1172x 1172x 1172x 2745x 2745x 4941x               2745x 2745x 2745x 4649x 2473x 2473x 2473x 2473x 2473x 2473x 2473x 272x 272x 4624x               272x 272x 272x 4624x               272x 272x 272x 272x 272x 272x 272x 272x 272x 2x 2x 2x 2x 2x                  
/**
 * Domain-Informed Priors for BKT Parameters
 *
 * Different skill types have different learning characteristics.
 * These priors encode our domain knowledge about soroban skill difficulty.
 */
 
import type { BktParams } from './types'
 
/**
 * Get default BKT parameters for a skill based on its type.
 *
 * Parameters are informed by soroban pedagogy:
 * - Basic skills (direct manipulation) are easier to learn
 * - Five complements require memorization and pattern recognition
 * - Ten complements are more complex and error-prone
 * - Mixed complements (combining techniques) are the hardest
 *
 * @param skillId - The skill identifier (e.g., "basic.directAddition")
 * @returns BKT parameters tuned for this skill type
 */
export function getDefaultParams(skillId: string): BktParams {
  // Basic skills - direct bead manipulation
  // High P(init) because these are intuitive
  // Moderate P(learn) - lowered to prevent P(known) exploding after few correct answers
  // Balanced pSlip/pGuess - the ratio pSlip/pGuess determines evidence strength
  // A correct answer provides evidence ratio of (1-pSlip)/pGuess = (1-0.15)/0.12 = 7.1x
  // (vs 45x with old values). This makes BKT updates more gradual.
  if (skillId.startsWith('basic.')) {
    return {
      pInit: 0.3,
      pLearn: 0.1, // Low learning rate - requires sustained practice
      pSlip: 0.15, // Higher slip - even known skills have errors
      pGuess: 0.12, // Higher guess - some problems can be guessed correctly
    }
  }
 
  // Five complements - using the "friends of 5"
  // Moderate difficulty, requires memorization
  if (skillId.startsWith('fiveComplements')) {
    return {
      pInit: 0.1,
      pLearn: 0.08, // Low learning rate
      pSlip: 0.18, // Moderate slip
      pGuess: 0.1, // Moderate guess
    }
  }
 
  // Five complement subtraction
  if (skillId.startsWith('fiveComplementsSub')) {
    return {
      pInit: 0.1,
      pLearn: 0.07,
      pSlip: 0.2,
      pGuess: 0.1,
    }
  }
 
  // Ten complements - using the "friends of 10"
  // More challenging, higher cognitive load
  if (skillId.startsWith('tenComplements')) {
    return {
      pInit: 0.05,
      pLearn: 0.06, // Very low learning rate for harder skills
      pSlip: 0.22, // Higher slip for complex skills
      pGuess: 0.08, // Lower guess for complex skills
    }
  }
 
  // Ten complement subtraction
  if (skillId.startsWith('tenComplementsSub')) {
    return {
      pInit: 0.05,
      pLearn: 0.05,
      pSlip: 0.25,
      pGuess: 0.08,
    }
  }
 
  // Mixed complements - combining techniques
  // Hardest category, requires fluency in multiple approaches
  if (skillId.startsWith('mixedComplements')) {
    return {
      pInit: 0.02,
      pLearn: 0.04, // Very slow learning
      pSlip: 0.28, // High slip even when known
      pGuess: 0.06, // Low guess - hard to luck into correct answer
    }
  }
 
  // Default for any unknown skill type
  return {
    pInit: 0.1,
    pLearn: 0.08,
    pSlip: 0.18,
    pGuess: 0.1,
  }
}
 
/**
 * Get a human-readable category name for a skill.
 */
export function getSkillCategory(skillId: string): string {
  if (skillId.startsWith('basic.')) return 'Basic Skills'
  if (skillId.startsWith('fiveComplementsSub')) return 'Five Complement Subtraction'
  if (skillId.startsWith('fiveComplements')) return 'Five Complements'
  if (skillId.startsWith('tenComplementsSub')) return 'Ten Complement Subtraction'
  if (skillId.startsWith('tenComplements')) return 'Ten Complements'
  if (skillId.startsWith('mixedComplements')) return 'Mixed Complements'
  return 'Other'
}