fix(worksheets): prevent infinite loop when problem space is empty

Add guards against empty problem space in both addition and subtraction
generators. When constraints are impossible to satisfy (e.g., 1-digit
with 100% borrowing), the generate-all approach would return 0 problems,
causing:
- Subtraction: infinite while loop trying to fill from empty array
- Addition (interpolate): array index -1 crash
- Addition (non-interpolate): division by zero in cycle calculation

All three paths now detect empty arrays and return safe fallback problems
with appropriate error logging instead of hanging the server at 85% CPU.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-12-05 10:17:52 -06:00
parent 822ef78e58
commit 02463df8e5

View File

@@ -398,6 +398,28 @@ export function generateProblems(
// ===== PROGRESSIVE DIFFICULTY MODE =====
// Sort all problems by difficulty (carry count), then sample based on
// position in worksheet to create easy→medium→hard progression
// CRITICAL: Handle empty problem space to prevent crashes
if (allProblems.length === 0) {
console.error(
`[ADD GEN] ERROR: No valid problems exist for constraints digitRange=${minDigits}-${maxDigits}, pAnyStart=${pAnyStart}. Using fallback problems.`
)
// Return fallback problems to prevent crash
const fallbackProblems: AdditionProblem[] = []
for (let i = 0; i < total; i++) {
fallbackProblems.push({
a: minDigits === 1 ? 3 : 23,
b: minDigits === 1 ? 4 : 45,
operator: 'add',
})
}
const elapsed = Date.now() - startTime
console.log(
`[ADD GEN] Complete: ${fallbackProblems.length} fallback problems in ${elapsed}ms (empty problem space)`
)
return fallbackProblems
}
console.log(`[ADD GEN] Sorting problems by difficulty for progressive difficulty`)
const sortedByDifficulty = [...allProblems].sort((a, b) => {
const diffA = countRegroupingOperations(a.a, a.b)
@@ -494,6 +516,28 @@ export function generateProblems(
// Shuffle all problems randomly, no sorting by difficulty
const shuffled = shuffleArray(allProblems, rand)
// CRITICAL: Handle empty problem space to prevent division by zero and infinite loops
// This can happen when constraints are impossible to satisfy
if (shuffled.length === 0) {
console.error(
`[ADD GEN] ERROR: No valid problems exist for constraints digitRange=${minDigits}-${maxDigits}, pAnyStart=${pAnyStart}. Using fallback problems.`
)
// Return fallback problems to prevent crash
const fallbackProblems: AdditionProblem[] = []
for (let i = 0; i < total; i++) {
fallbackProblems.push({
a: minDigits === 1 ? 3 : 23,
b: minDigits === 1 ? 4 : 45,
operator: 'add',
})
}
const elapsed = Date.now() - startTime
console.log(
`[ADD GEN] Complete: ${fallbackProblems.length} fallback problems in ${elapsed}ms (empty problem space)`
)
return fallbackProblems
}
// If we need more problems than available, cycle through the shuffled array
// Example: 45 unique problems, requesting 100
// - Problems 0-44: First complete shuffle
@@ -929,6 +973,29 @@ export function generateSubtractionProblems(
// Shuffle deterministically using seed
const shuffled = shuffleArray(allProblems, rand)
// CRITICAL: Handle empty problem space to prevent infinite loop
// This can happen when constraints are impossible to satisfy (e.g., 1-digit with 100% borrowing)
if (shuffled.length === 0) {
console.error(
`[SUB GEN] ERROR: No valid problems exist for constraints digitRange=${minDigits}-${maxDigits}, pAnyBorrow=${pAnyBorrow}. Using fallback problems.`
)
// Return fallback problems to prevent infinite loop
const fallbackProblems: SubtractionProblem[] = []
for (let i = 0; i < count; i++) {
// Use safe fallback that always works
fallbackProblems.push({
minuend: minDigits === 1 ? 9 : 52,
subtrahend: minDigits === 1 ? 3 : 17,
operator: 'sub',
})
}
const elapsed = Date.now() - startTime
console.log(
`[SUB GEN] Complete: ${fallbackProblems.length} fallback problems in ${elapsed}ms (empty problem space)`
)
return fallbackProblems
}
// If we need more problems than available, we'll have duplicates
if (count > shuffled.length) {
console.warn(