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

73.87% Statements 82/111
81.25% Branches 13/16
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 5036x 5036x 5036x 5036x 5036x 5036x 5036x 1105x 1105x 1105x 1105x 1105x 1105x 1105x 3931x 3931x 3931x 5010x 1173x 1173x 1173x 1173x 1173x 1173x 1173x 2758x 2758x 5010x               2758x 2758x 2758x 4662x 2474x 2474x 2474x 2474x 2474x 2474x 2474x 284x 284x 4636x               284x 284x 284x 4636x               284x 284x 284x 284x 284x 284x 284x 284x 284x 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'
}