From 9159608dcd78e9bf7e02363df52c37e61167cb31 Mon Sep 17 00:00:00 2001 From: Thomas Hallock Date: Wed, 10 Dec 2025 11:32:19 -0600 Subject: [PATCH] feat(practice): reduce term count for visualization part MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Visualization problems now use 2-4 terms (75% of abacus's 3-6 terms) to make them easier since students don't have the physical abacus to help track their mental calculations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../web/src/lib/curriculum/session-planner.ts | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/apps/web/src/lib/curriculum/session-planner.ts b/apps/web/src/lib/curriculum/session-planner.ts index a4d84077..c847caec 100644 --- a/apps/web/src/lib/curriculum/session-planner.ts +++ b/apps/web/src/lib/curriculum/session-planner.ts @@ -208,7 +208,7 @@ function buildSessionPart( // Focus slots: current phase, primary skill for (let i = 0; i < focusCount; i++) { - slots.push(createSlot(slots.length, 'focus', phaseConstraints)) + slots.push(createSlot(slots.length, 'focus', phaseConstraints, type)) } // Reinforce slots: struggling skills get extra practice @@ -218,7 +218,8 @@ function buildSessionPart( createSlot( slots.length, 'reinforce', - skill ? buildConstraintsForSkill(skill) : phaseConstraints + skill ? buildConstraintsForSkill(skill) : phaseConstraints, + type ) ) } @@ -227,13 +228,18 @@ function buildSessionPart( for (let i = 0; i < reviewCount; i++) { const skill = needsReview[i % Math.max(1, needsReview.length)] slots.push( - createSlot(slots.length, 'review', skill ? buildConstraintsForSkill(skill) : phaseConstraints) + createSlot( + slots.length, + 'review', + skill ? buildConstraintsForSkill(skill) : phaseConstraints, + type + ) ) } // Challenge slots: use same mastered skills constraints (all problems should use student's skills) for (let i = 0; i < challengeCount; i++) { - slots.push(createSlot(slots.length, 'challenge', phaseConstraints)) + slots.push(createSlot(slots.length, 'challenge', phaseConstraints, type)) } // Shuffle to interleave purposes @@ -487,16 +493,33 @@ export async function abandonSessionPlan(planId: string): Promise { // Helper Functions // ============================================================================ +/** + * Get term count constraints based on part type + * + * - abacus: Full term count (3-6 terms) + * - visualization: 75% of abacus (easier since no physical abacus) - rounds to 2-4 terms + * - linear: Same as abacus (full difficulty) + */ +function getTermCountForPartType(partType: SessionPartType): { min: number; max: number } { + if (partType === 'visualization') { + // 75% of abacus term count (3*0.75=2.25→2, 6*0.75=4.5→4) + return { min: 2, max: 4 } + } + // abacus and linear use full term count + return { min: 3, max: 6 } +} + function createSlot( index: number, purpose: ProblemSlot['purpose'], - baseConstraints: ReturnType + baseConstraints: ReturnType, + partType: SessionPartType ): ProblemSlot { const constraints = { requiredSkills: baseConstraints.requiredSkills, targetSkills: baseConstraints.targetSkills, forbiddenSkills: baseConstraints.forbiddenSkills, - termCount: { min: 3, max: 6 }, + termCount: getTermCountForPartType(partType), digitRange: { min: 1, max: 2 }, }