P(solve) and the zone of proximal development
Given per micro-skill we can predict the probability a student solves a specific problem right now — and pick one in the sweet spot.
For a task requiring one skill:
“Knows and doesn’t slip” plus “doesn’t know but guesses.”
Numerically:
| 0.0 | 0.20 |
| 0.2 | 0.34 |
| 0.5 | 0.55 |
| 0.7 | 0.69 |
| 0.9 | 0.83 |
| 1.0 | 0.90 |
So P(solve) always sits between and — never 0 (guess chance) and never 1 (slip chance).
What is ZPD
Section titled “What is ZPD”Lev Vygotsky, Soviet psychologist of the 1930s, crystallized the guiding idea:
Students grow fastest on tasks just beyond current ability — not what they already ace (no growth), not what’s far away (frustration, dropout).
That’s the Zone of Proximal Development (ZPD).
Quantitatively: “just beyond” in studies often maps to — tough enough to stretch, not hopeless.
Applying it in the selector
Section titled “Applying it in the selector”Task selection algorithm:
- For each candidate problem compute the student’s .
- Pick the task minimizing .
- Recommend it.
In code we use a Gaussian “distance to target”:
// Closeness to target — Gaussian-ish, peaks at target=0.7 const closeness = Math.exp(-Math.pow(pSolveJoint - target, 2) / 0.03);Closer is to 0.7, higher closeness; peak exactly at 0.7.
Gaussian: smooth, symmetric, penalises extremes fast. Absolute difference is sharp and lumps «almost 0.7» with «way off».
Gaussian vs absolute difference
Section titled “Gaussian vs absolute difference”Gaussian:
- symmetric around 0.7;
- punishes far-off tasks quickly;
- smooth — no kinks, stable selector behavior.
Denominator 0.03 controls width:
0.03⇒ tasks roughly still get closeness > 0.5;0.01— too tight (“exactly 0.7 ± 0.05”);0.10— too loose (“anything between 0.4 and 1.0 feels fine”).
Tune if needed; 0.03 works well in practice.
What if ZPD pushes toward a strong skill only
Section titled “What if ZPD pushes toward a strong skill only”Teacher scenario: Ivan’s arithmetic sits at 0.85, parentheses at 0.40. Which task?
Naïvely chasing might favor arithmetic-heavy items (combined with other micro-skills) and never train parentheses.
So the selector adds a rare-skill bonus:
// Rarity bonus: how many of this task's skills are below 0.4? const undertrained = task.microskills.filter( (s) => (state.mastery[s] ?? params.pInit) < 0.4 ).length; const rarity = undertrained / task.microskills.length;
const score = closeness + rareBonus * rarity;Translation: if many required skills have , bump the task slightly in ranking — exploration so we don’t stall in comfort zone.
Try it: ZPD curve
Section titled “Try it: ZPD curve”Drag target and σ². Watch how the ZPD “window” narrows — tighter σ² ⇒ pickier selector around target.
closeness = exp(−(p−target)²/σ²). Highest at the peak, drops fast to the sides. The smaller σ², the narrower the «ZPD window».
What the selector ends up choosing
Section titled “What the selector ends up choosing”Back to Ivan after six tasks (previous chapter):
- for parentheses.
- Single-skill .
Too hard — closeness ≈ 0; selector won’t lead with that.
Better:
- mix two skills (parentheses + familiar arithmetic);
- joint lands ~0.55–0.65 — nearer ZPD;
- trains the weak spot without total frustration.
See multi-skill tasks.