fix: letterblokken bij wereldoriëntatie verschillen nu ook per niveau (v0.4.20-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 51s

- De nieuwe "Letterblokken"-werkvorm (v0.4.18) verklapte op elk niveau
  altijd elke letter - alleen de soorten-/plaatsenpool werd moeilijker,
  de spelopdracht zelf bleef identiek. Bij de originele letterblokken-
  widget (letters.js) maakt niveau juist de opdracht zelf moeilijker:
  niveau 1 verklapt alles, niveau 3 niets.
- Zelfde schaal nu ook hier: niveau 1 toont elke letter, niveau 2 ongeveer
  de helft (willekeurig gekozen, vast per opdracht), niveau 3 toont geen
  enkele letter. Het puntenpatroon per vak blijft op elk niveau zichtbaar
  als tastbare hint, ook wanneer de letter zelf verborgen is.
- Geverifieerd: niveau 1 toont 11/11 letters, niveau 2 ~helft, niveau 3
  0/13 (met de puntenpatronen nog intact).
This commit is contained in:
Ramon 2026-07-19 00:23:39 +02:00
parent 705bb7800a
commit 63490719d6
3 changed files with 21 additions and 12 deletions

View file

@ -1 +1 @@
0.4.19-beta
0.4.20-beta

View file

@ -2,7 +2,7 @@
"use strict";
/* version shown until /api/version resolves (or if the fetch fails, e.g. offline).
Kept in sync by hand with the VERSION file at the repo root on every release. */
const VERSION = "0.4.19-beta";
const VERSION = "0.4.20-beta";
(function(){
const tag = document.getElementById("verTag");
tag.textContent = "v"+VERSION;

View file

@ -361,22 +361,30 @@ function worldFlashCard(card,name,visual,S,redraw,onNext,onAssess){
}
/* ---- letterblokken: spel de naam met dezelfde sleep-blokken-mechaniek als
letters.js, hier gevoed door de naam van de soort/plaats. Elk vak toont
meteen het puntenpatroon (en de letter) van het juiste antwoord - bewust
de makkelijkste ondersteuning, want deze namen zijn voor de meeste
kinderen onbekende spelling; het quiz/foto/latijn-werk hierboven test
het herkennen al veel strenger. */
letters.js, hier gevoed door de naam van de soort/plaats. Het puntenpatroon
van het juiste antwoord staat altijd in het vak (zelfde tastbare hint als
overal elders); of de LETTER er ook gewoon bij staat hangt af van het
niveau, net als bij letters.js: niveau 1 verklapt alles, niveau 2 de
helft, niveau 3 niets - alleen de stippen blijven dan over als hint. */
const worldIsLetter=ch=>/\p{L}/u.test(ch);
const worldSpellPool=name=>[...name.toLowerCase()].filter(worldIsLetter);
function worldSpellRevealMask(count,level){
if(level===1)return new Array(count).fill(true);
if(level===3)return new Array(count).fill(false);
const hideCount=Math.max(1,Math.min(count-1,Math.round(count/2)));
const hidden=new Set(worldShuffle([...Array(count).keys()]).slice(0,hideCount));
return Array.from({length:count},(_,i)=>!hidden.has(i));
}
function worldSpellNeedsNewTray(S,name){
const letters=worldSpellPool(name);
return !Array.isArray(S.answerLetters)||S.answerLetters.length!==letters.length||!Array.isArray(S.trayLetters);
return !Array.isArray(S.answerLetters)||S.answerLetters.length!==letters.length||!Array.isArray(S.trayLetters)||!Array.isArray(S.revealMask)||S.revealMask.length!==letters.length;
}
function worldSpellNewTray(S,name){
const letters=worldSpellPool(name);
const distractPool="abcdefghijklmnopqrstuvwxyz".split("").filter(c=>!letters.includes(c));
S.trayLetters=worldShuffle([...letters,...worldShuffle(distractPool).slice(0,4)]);
S.answerLetters=letters.map(()=>null);
S.revealMask=worldSpellRevealMask(letters.length,S.level);
S.spellSolved=false;
}
function worldLetterBlock(ch,trayIndex,letters,S,redraw){
@ -472,7 +480,7 @@ function worldSpellAnswer(name,S,redraw){
slot.dataset.index=idx;
const ghost=document.createElement("div");
ghost.className="ghost";
ghost.textContent=letters[idx];
ghost.textContent=S.revealMask[idx]?letters[idx]:"?";
const dots=dotGrid(letters[idx]);
dots.classList.add("slot-dots");
slot.append(ghost,dots);
@ -506,7 +514,7 @@ function mountWorldQuiz(root,init,config){
const S=Object.assign({
activity:"quiz",exercise:"recognize",level:1,view:config.type==="trees"?"mixed":config.type==="birds"?"bird":"plant",
score:0,streak:0,target:"",taskView:"",options:[],picked:"",solved:false,feedback:"",
flashRevealed:false,answerLetters:null,trayLetters:null,spellSolved:false,
flashRevealed:false,answerLetters:null,trayLetters:null,revealMask:null,spellSolved:false,
},init||{});
S.level=Math.max(1,Math.min(3,+S.level||1));
if(!exerciseOptions.some(([id])=>id===S.exercise))S.exercise="recognize";
@ -538,6 +546,7 @@ function mountWorldQuiz(root,init,config){
S.flashRevealed=false;
S.answerLetters=null;
S.trayLetters=null;
S.revealMask=null;
S.spellSolved=false;
draw();
};
@ -933,7 +942,7 @@ function mountTopography(root,init){
const S=Object.assign({
activity:"quiz",mode:"provinces",level:1,score:0,streak:0,
target:"",picked:"",solved:false,feedback:"",
flashRevealed:false,answerLetters:null,trayLetters:null,spellSolved:false,
flashRevealed:false,answerLetters:null,trayLetters:null,revealMask:null,spellSolved:false,
},init||{});
S.level=Math.max(1,Math.min(3,+S.level||1));
if(!modeOptions.some(([id])=>id===S.mode))S.mode="provinces";
@ -955,7 +964,7 @@ function mountTopography(root,init){
let target=items[Math.floor(Math.random()*items.length)];
if(items.length>1)while(target.id===previous)target=items[Math.floor(Math.random()*items.length)];
S.target=target.id;S.picked="";S.solved=false;S.feedback="";
S.flashRevealed=false;S.answerLetters=null;S.trayLetters=null;S.spellSolved=false;
S.flashRevealed=false;S.answerLetters=null;S.trayLetters=null;S.revealMask=null;S.spellSolved=false;
draw();
};
const answer=id=>{