Ga direct naar de hoofdinhoud
Klasklaar.nl
Home
Blog
Gr. 1/2
Inloopactiviteiten
Thema's
Thema Herfst
Thema Ruimte
Thema Sinterklaas
Thema Boerderij
Thema Vlinder
Thema Vervoer
Thema Bouwen
Thema Koningsspelen
Thema Gezondheid
Motoriek 1/2
Rekenen 1/2
Lezen 1/2
Verklanken
Voorleesteksten
SEO 1/2
Energizers
dagritmekaarten
Gr. 3
Thema's
Rekenen 3
Bussommen
Dobbelsteen sommen
Contextsommen
Lezen 3
Teksten op thema
Sinterklaas
Kerst
Carnaval
Feest
Koningsspelen
Beroepen
De bakker
De bouw
De hulpdiensten
De post
De bibliotheek
De boerderij
De speeltuin
De tuin
Dierentuin
Het bos
het restaurant
Strand
Het zwembad
Thuis
School
Supermarkt
Verkeer
Het vliegtuig
De trein
Winter
Lente
Zomer
Herfst
Piraten
Sprookjes
Familie
Huisdieren
Kleding
Gezondheid
Gezond eten
Sport
Insecten
Teksten B3
Teksten M3
Teksten E3
Lees en kleur
Luister, lees en kies
Lees en zoek
Waar of niet waar
SEO 3
Creatief
dagritmekaarten
Gr. 4
Rekenen 4
Getallen en bewerkingen
Verhaaltjessommen
Werkbladen
Rekenspelletjes
Meten
Verhaaltjessommen
Werkbladen
Tijd
Verhaaltjessommen
Werkbladen
Geld
Verhaaltjessommen
Werkbladen
Som bij het plaatje
Steunkaarten
Taal 4
Woordbenoemen
Zinsontleding
Taal Algemeen
Steunkaarten
Spelling 4
Steunkaarten
Spellingwerkbladen
Spellen
Lezen 4
Teksten B4
Teksten M4
Teksten E4
Leeslessen 4
Begrijpend Lezen 4
SEO 4
Overig
Geschiedenis 4
dagritmekaarten
Gr. 5
Rekenen 5
Getallen en bewerkingen
Verhaalsommen
Werkbladen
Rekenspelletjes
Meten
Werkbladen
Verhaalsommen
Tijd
Verhaalsommen
Werkbladen
Geld
Verhaalsommen
Werkbladen
Breuken
Verhaalsommen
Werkbladen
Steunkaarten
Taal 5
Woordbenoemen
Zinsontleding
Taal Algemeen
Spelling 5
Spellingwerkbladen
Spellen
Lezen 5
Teksten B5
Teksten M5
Teksten E5
Leeslessen 5
Begrijpend Lezen 5
SEO 5
Geschiedenis 5
Gr. 6
Rekenen 6
Getallen en Bewerkingen
Verhaalsommen
Werkbladen
Meten
Verhaalsommen
Werkbladen
Tijd
Verhaalsommen
Werkbladen
Geld
Verhaalsommen
Werkbladen
Breuken
Verhaalsommen
Werkbladen
Procenten
Verhaalsommen
Werkbladen
Steunkaarten
Taal 6
Woordbenoemen
Zinsontleding
Taal Algemeen
Spelling 6
Spellingwerkbladen
Spellen
Werkwoordspelling
Lezen 6
Teksten B6
Teksten M6
Teksten E6
Leeslessen 6
Begrijpend Lezen 6
SEO 6
Gr. 7
Rekenen 7
Getallen en Bewerkingen
Werkbladen
Verhaalsommen
Meten
Werkbladen
Verhaalsommen
Tijd
Werkbladen
Verhaalsommen
Geld
Werkbladen
Verhaalsommen
Breuken
Verhaalsommen
Werkbladen
Procenten
Werkbladen
Verhaalsommen
Verhoudingen
Steunkaarten
Taal 7
Woordbenoemen
Zinsontleding
Taal Algemeen
Spelling 7
Spellingwerkbladen
Spellen
Lezen 7
Teksten B7
Teksten M7
Teksten E7
Leeslessen 7
Begrijpend Lezen 7
SEO 7
Gr. 8
Rekenen 8
Getallen en bewerkingen
Verhaalsommen
Werkbladen
Meten
Verhaalsommen
Werkbladen
Tijd
Verhaalsommen
Werkbladen
Geld
Verhaalsommen
Werkbladen
Breuken
Verhaalsommen
Werkbladen
Procenten
Verhaalsommen
Werkbladen
Verhoudingen
Steunkaarten
Taal 8
Woordbenoemen
Zinsontleding
Taal Algemeen
Spelling 8
Spellingwerkbladen
Spellen
Lezen 8
Leeslessen
Lezen B8
Begrijpend Lezen
SEO 8
Afscheid
Didactiek
Sommenmaker
Taalmaker
Woordbenoemen generator
Woorden generator
Zinnen generator
NT2
Lezen
Pictokaarten
Taal
Portfolio
Dit ben ik
Mijn werk in beeld
Doeboekjes
Prentenboeken
Vlam en het feest
Blaadje en de storm
Kooltje en steenkolen
Wolkje in de lucht
Tok en Tik - De taart
Tok en Tik - We gaan je missen
Tok en Tik - De storm
Tok en Tik - Het ruim
Groepsvorming
Gouden Weken
Kinderliedjes
Digitale Geletterdheid
B-bot
Techniek
Kleurplaten
Sinterklaas
Kerst
Pasen
Carnaval
Koningsdag
Koningsspelen
Oud en Nieuw
Verjaardag
Moederdag
Vaderdag
Halloween
Valentijn
Lente
Zomer
Herfst
Winter
Regen
Sneeuw
Strand
Bloemen
Paddenstoelen
Weer
Bakker
Bouw
Kunstenaar
Politie
Brandweer
Dokter
Tandarts
Boer
Kapper
Post
Winkel
Auto
Tractor
Bus
Vliegtuig
Boot
Fiets
Trein
Vrachtwagen
Graafmachine
Brandweerauto
Politieauto
Voetbal
Hockey
Korfbal
Zwemmen
Dansen
Turnen
Sportdag
Speeltuin
Speelgoed
Spelletjes
Sprookjes
Draken
Ridders
Piraten
Prinsessen
Zeemeerminnen
Heksen
Tovenaars
Eenhoorns
Monsters
Natuur
Bos
Boerderij
Ruimte
Verkeer
Gezondheid
Lichaam
Eten en drinken
Techniek
Recycling
Boerderijdieren
Dierentuin
Huisdieren
Bosdieren
Zeedieren
Insecten
Vogels
Dino's
Paarden
Honden
Katten
Ballet
Timer
Sommenflitser
Woordenflitser
Keuzerad
Dagstarter
Praatplaten
Naamplaatjes
Splitsmachine
Bussommen
Rekenrek
AR Aquarium
Woordzoeker maker
Sudoku Maker
Online spellen
Blikgooien (tafels)
Eendjes vangen (tafels)
Ringwerpen (tafels)
Roodkapje (splitsen)
Hans en Grietje (plus en min)
Zeven kleine geitjes (plus via de 10)
Verliefde harten
Ruimtemissie (automatiseren)
Locomotief (automatiseren)
contact
Zoeken
Sudoku maken
Maak een printklare sudoku. Klasklaar controleert automatisch of er precies één oplossing is.
Werkblad
Antwoordblad
6 × 6 · Normaal · 1 sudoku
Sudoku wordt gemaakt
De preview verschijnt automatisch.
window.__KKSU_LOGO_DATA='data:image/png;base64,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'; (function(global){ 'use strict'; const CONFIGS = { 4: { size:4, boxRows:2, boxCols:2, digits:[1,2,3,4], clueBands:{easy:[10,12], normal:[8,10], hard:[6,8]} }, 6: { size:6, boxRows:2, boxCols:3, digits:[1,2,3,4,5,6], clueBands:{easy:[24,28], normal:[18,22], hard:[14,18]} }, 9: { size:9, boxRows:3, boxCols:3, digits:[1,2,3,4,5,6,7,8,9], clueBands:{easy:[42,48], normal:[34,40], hard:[27,33]} } }; function getConfig(size){ const c=CONFIGS[Number(size)]; if(!c) throw new Error('UNSUPPORTED_SIZE'); return c; } function hashSeed(seed){ let x=Number(seed); if(!Number.isFinite(x)){ const s=String(seed); x=2166136261; for(let i=0;i<s.length;i++){x^=s.charCodeAt(i);x=Math.imul(x,16777619);} } return (x>>>0)||0x9e3779b9; } function createRng(seed){ let a=hashSeed(seed); return function(){ a|=0; a=(a+0x6D2B79F5)|0; let t=Math.imul(a^(a>>>15),1|a); t=(t+Math.imul(t^(t>>>7),61|t))^t; return ((t^(t>>>14))>>>0)/4294967296; }; } function randInt(rng,max){return Math.floor(rng()*max)} function shuffle(arr,rng){ const a=arr.slice(); for(let i=a.length-1;i>0;i--){const j=randInt(rng,i+1);[a[i],a[j]]=[a[j],a[i]];} return a; } function range(n){return Array.from({length:n},(_,i)=>i)} function popcount(x){let c=0;while(x){x&=x-1;c++;}return c} function firstDigitFromMask(mask,size){for(let d=1;d<=size;d++)if(mask&(1<<(d-1)))return d;return 0} function digitsFromMask(mask,size){const a=[];for(let d=1;d<=size;d++)if(mask&(1<<(d-1)))a.push(d);return a} function boxIndex(row,col,cfg){return Math.floor(row/cfg.boxRows)*(cfg.size/cfg.boxCols)+Math.floor(col/cfg.boxCols)} function generateFullSolution(size,seed){ const cfg=getConfig(size), n=cfg.size, rng=typeof seed==='function'?seed:createRng(seed); const baseDigits=shuffle(cfg.digits,rng); const bandCount=n/cfg.boxRows; const stackCount=n/cfg.boxCols; const bandOrder=shuffle(range(bandCount),rng); const stackOrder=shuffle(range(stackCount),rng); const rows=[]; for(const b of bandOrder){ const inner=shuffle(range(cfg.boxRows),rng); for(const r of inner) rows.push(b*cfg.boxRows+r); } const cols=[]; for(const s of stackOrder){ const inner=shuffle(range(cfg.boxCols),rng); for(const c of inner) cols.push(s*cfg.boxCols+c); } // General pattern for boxRows x boxCols where boxRows*boxCols=size. const pattern=(r,c)=>(cfg.boxCols*(r%cfg.boxRows)+Math.floor(r/cfg.boxRows)+c)%n; let board=[]; for(const r of rows)for(const c of cols)board.push(baseDigits[pattern(r,c)]); // For square boxes, transpose is another safe source of variety. if(cfg.boxRows===cfg.boxCols && rng()<0.5){ const t=Array(n*n); for(let r=0;r<n;r++)for(let c=0;c<n;c++)t[c*n+r]=board[r*n+c]; board=t; } if(!validateComplete(board,cfg))throw new Error('FULL_SOLUTION_INVALID'); return board; } function validateComplete(board,cfgOrSize){ const cfg=typeof cfgOrSize==='object'?cfgOrSize:getConfig(cfgOrSize),n=cfg.size; if(!Array.isArray(board)||board.length!==n*n)return false; const expected=(1<<n)-1; for(let r=0;r<n;r++){ let m=0;for(let c=0;c<n;c++){const v=board[r*n+c];if(v<1||v>n)return false;m|=1<<(v-1);}if(m!==expected)return false; } for(let c=0;c<n;c++){ let m=0;for(let r=0;r<n;r++){const v=board[r*n+c];m|=1<<(v-1);}if(m!==expected)return false; } const boxes=(n/cfg.boxRows)*(n/cfg.boxCols); for(let b=0;b<boxes;b++){ const br=Math.floor(b/(n/cfg.boxCols))*cfg.boxRows; const bc=(b%(n/cfg.boxCols))*cfg.boxCols; let m=0; for(let dr=0;dr<cfg.boxRows;dr++)for(let dc=0;dc<cfg.boxCols;dc++)m|=1<<(board[(br+dr)*n+(bc+dc)]-1); if(m!==expected)return false; } return true; } function buildMasks(board,cfg){ const n=cfg.size,rowMask=Array(n).fill(0),colMask=Array(n).fill(0),boxMask=Array(n).fill(0),full=(1<<n)-1; for(let i=0;i<n*n;i++){ const v=board[i]||0;if(!v)continue; if(v<1||v>n)return {valid:false}; const r=Math.floor(i/n),c=i%n,b=boxIndex(r,c,cfg),bit=1<<(v-1); if((rowMask[r]&bit)||(colMask[c]&bit)||(boxMask[b]&bit))return {valid:false}; rowMask[r]|=bit;colMask[c]|=bit;boxMask[b]|=bit; } return {valid:true,rowMask,colMask,boxMask,full}; } function countSolutionsFast(input,cfgOrSize,limit=2){ const cfg=typeof cfgOrSize==='object'?cfgOrSize:getConfig(cfgOrSize),n=cfg.size,board=input.slice(); const m=buildMasks(board,cfg);if(!m.valid)return {count:0,firstSolution:null,nodes:0}; let count=0,firstSolution=null,nodes=0; function dfs(){ if(count>=limit)return; nodes++; let best=-1,bestMask=0,bestCount=99; for(let i=0;i<n*n;i++)if(!board[i]){ const r=Math.floor(i/n),c=i%n,b=boxIndex(r,c,cfg); const cand=m.full & ~(m.rowMask[r]|m.colMask[c]|m.boxMask[b]); const pc=popcount(cand); if(pc===0)return; if(pc<bestCount){best=i;bestMask=cand;bestCount=pc;if(pc===1)break;} } if(best<0){count++;if(count===1)firstSolution=board.slice();return;} const r=Math.floor(best/n),c=best%n,b=boxIndex(r,c,cfg); for(let d=1;d<=n;d++){ const bit=1<<(d-1);if(!(bestMask&bit))continue; board[best]=d;m.rowMask[r]|=bit;m.colMask[c]|=bit;m.boxMask[b]|=bit; dfs(); board[best]=0;m.rowMask[r]&=~bit;m.colMask[c]&=~bit;m.boxMask[b]&=~bit; if(count>=limit)return; } } dfs(); return {count,firstSolution,nodes}; } // Independent solution counter: deliberately does not use masks/boxIndex/buildMasks. function countSolutionsIndependent(input,cfgOrSize,limit=2){ const cfg=typeof cfgOrSize==='object'?cfgOrSize:getConfig(cfgOrSize),n=cfg.size,board=input.slice(); function allowed(idx,d){ const r=Math.floor(idx/n),c=idx%n; for(let x=0;x<n;x++){if(x!==c&&board[r*n+x]===d)return false;if(x!==r&&board[x*n+c]===d)return false;} const br=Math.floor(r/cfg.boxRows)*cfg.boxRows,bc=Math.floor(c/cfg.boxCols)*cfg.boxCols; for(let rr=br;rr<br+cfg.boxRows;rr++)for(let cc=bc;cc<bc+cfg.boxCols;cc++){ const j=rr*n+cc;if(j!==idx&&board[j]===d)return false; } return true; } for(let i=0;i<n*n;i++){const v=board[i];if(v&&(v<1||v>n||!allowed(i,v)))return {count:0,firstSolution:null,nodes:0};} let count=0,firstSolution=null,nodes=0; function dfs(){ if(count>=limit)return;nodes++; let best=-1,bestCandidates=null; for(let i=0;i<n*n;i++)if(!board[i]){ const cand=[];for(let d=1;d<=n;d++)if(allowed(i,d))cand.push(d); if(cand.length===0)return; if(bestCandidates===null||cand.length<bestCandidates.length){best=i;bestCandidates=cand;if(cand.length===1)break;} } if(best<0){count++;if(count===1)firstSolution=board.slice();return;} for(const d of bestCandidates){board[best]=d;dfs();board[best]=0;if(count>=limit)return;} } dfs();return {count,firstSolution,nodes}; } function humanSolve(input,cfgOrSize){ const cfg=typeof cfgOrSize==='object'?cfgOrSize:getConfig(cfgOrSize),n=cfg.size,full=(1<<n)-1,board=input.slice(); if(!buildMasks(board,cfg).valid)return {solved:false,invalid:true,score:Infinity,maxTechnique:99,steps:[],solution:null}; const cand=Array(n*n).fill(0); const steps=[];let score=0,maxTechnique=0; const peers=Array.from({length:n*n},()=>new Set()); const units=[]; for(let r=0;r<n;r++){const u=[];for(let c=0;c<n;c++)u.push(r*n+c);units.push(u);} for(let c=0;c<n;c++){const u=[];for(let r=0;r<n;r++)u.push(r*n+c);units.push(u);} for(let br=0;br<n;br+=cfg.boxRows)for(let bc=0;bc<n;bc+=cfg.boxCols){const u=[];for(let dr=0;dr<cfg.boxRows;dr++)for(let dc=0;dc<cfg.boxCols;dc++)u.push((br+dr)*n+bc+dc);units.push(u);} for(const u of units)for(const a of u)for(const b of u)if(a!==b)peers[a].add(b); function candidateMask(i){ if(board[i])return 0;const r=Math.floor(i/n),c=i%n;let used=0; for(let x=0;x<n;x++){const vr=board[r*n+x],vc=board[x*n+c];if(vr)used|=1<<(vr-1);if(vc)used|=1<<(vc-1);} const br=Math.floor(r/cfg.boxRows)*cfg.boxRows,bc=Math.floor(c/cfg.boxCols)*cfg.boxCols; for(let rr=br;rr<br+cfg.boxRows;rr++)for(let cc=bc;cc<bc+cfg.boxCols;cc++){const v=board[rr*n+cc];if(v)used|=1<<(v-1);} return full&~used; } for(let i=0;i<n*n;i++)cand[i]=candidateMask(i); function place(i,d,tech,pts){ board[i]=d;cand[i]=0;const bit=1<<(d-1); for(const p of peers[i])if(!board[p]){cand[p]&=~bit;if(cand[p]===0)return false;} steps.push({technique:tech,cell:i,digit:d});score+=pts;maxTechnique=Math.max(maxTechnique,tech);return true; } function solved(){for(const v of board)if(!v)return false;return true;} let guard=0; while(!solved()&&guard++<2000){ let progress=false; // Naked singles for(let i=0;i<n*n;i++)if(!board[i]&&popcount(cand[i])===1){if(!place(i,firstDigitFromMask(cand[i],n),1,1))return {solved:false,invalid:true,score,maxTechnique,steps,solution:null};progress=true;break;} if(progress)continue; // Hidden singles across all units outerHidden: for(const u of units){ for(let d=1;d<=n;d++){ const bit=1<<(d-1);let found=-1,count=0,present=false; for(const i of u){if(board[i]===d){present=true;break;}if(!board[i]&&(cand[i]&bit)){found=i;count++;if(count>1)break;}} if(!present&&count===1){if(!place(found,d,2,2))return {solved:false,invalid:true,score,maxTechnique,steps,solution:null};progress=true;break outerHidden;} } } if(progress)continue; // Locked candidates: box -> row/column and row/column -> box. let eliminated=false; const boxUnits=units.slice(2*n); for(const u of boxUnits){ for(let d=1;d<=n&&!eliminated;d++){ const bit=1<<(d-1),cells=u.filter(i=>!board[i]&&(cand[i]&bit));if(cells.length<2)continue; const rows=new Set(cells.map(i=>Math.floor(i/n))),cols=new Set(cells.map(i=>i%n)); if(rows.size===1){const r=Math.floor(cells[0]/n),uset=new Set(u);for(let c=0;c<n;c++){const i=r*n+c;if(!uset.has(i)&&!board[i]&&(cand[i]&bit)){cand[i]&=~bit;eliminated=true;}}} if(!eliminated&&cols.size===1){const c=cells[0]%n,uset=new Set(u);for(let r=0;r<n;r++){const i=r*n+c;if(!uset.has(i)&&!board[i]&&(cand[i]&bit)){cand[i]&=~bit;eliminated=true;}}} } if(eliminated)break; } if(!eliminated){ for(let ui=0;ui<2*n&&!eliminated;ui++){ const u=units[ui];for(let d=1;d<=n&&!eliminated;d++){ const bit=1<<(d-1),cells=u.filter(i=>!board[i]&&(cand[i]&bit));if(cells.length<2)continue; const boxes=new Set(cells.map(i=>{const r=Math.floor(i/n),c=i%n;return Math.floor(r/cfg.boxRows)*(n/cfg.boxCols)+Math.floor(c/cfg.boxCols);})); if(boxes.size===1){const b=[...boxes][0],bu=boxUnits[b],uset=new Set(u);for(const i of bu)if(!uset.has(i)&&!board[i]&&(cand[i]&bit)){cand[i]&=~bit;eliminated=true;}} } } } if(eliminated){steps.push({technique:4});score+=4;maxTechnique=Math.max(maxTechnique,4);continue;} // Naked pairs in any unit. let pairProgress=false; outerPair: for(const u of units){ const byMask=new Map(); for(const i of u)if(!board[i]&&popcount(cand[i])===2){const m=cand[i];if(!byMask.has(m))byMask.set(m,[]);byMask.get(m).push(i);} for(const [m,cells] of byMask){if(cells.length!==2)continue;for(const i of u){if(board[i]||cells.includes(i))continue;const before=cand[i];cand[i]&=~m;if(cand[i]!==before){if(cand[i]===0)return {solved:false,invalid:true,score,maxTechnique,steps,solution:null};pairProgress=true;}}if(pairProgress){steps.push({technique:5});score+=5;maxTechnique=Math.max(maxTechnique,5);break outerPair;}} } if(pairProgress)continue; break; } const ok=solved()&&validateComplete(board,cfg); return {solved:ok,invalid:false,score,maxTechnique,steps,solution:ok?board:null}; } function targetClueCount(cfg,difficulty,rng){ const [min,max]=cfg.clueBands[difficulty]; let values=[];for(let v=min;v<=max;v++)values.push(v); // Symmetric pair removals mean even-sized boards retain even clue counts. if(cfg.size%2===0)values=values.filter(v=>v%2===0); return values[randInt(rng,values.length)]||Math.round((min+max)/2); } function removalGroups(cfg,rng){ const total=cfg.size*cfg.size,seen=new Set(),groups=[]; for(let i=0;i<total;i++){ if(seen.has(i))continue;const j=total-1-i;seen.add(i);seen.add(j);groups.push(i===j?[i]:[i,j]); } return shuffle(groups,rng); } function difficultyMetrics(board,cfg,human){ const clues=board.reduce((a,v)=>a+(v?1:0),0),total=cfg.size*cfg.size,empties=total-clues; // Human score matters, but clue density also captures how much information the learner must reconstruct. const normalizedHuman=human.score/Math.max(1,empties); const complexity=(empties/total)*100 + normalizedHuman*8 + human.maxTechnique*3; return {clues,empties,complexity,maxTechnique:human.maxTechnique,humanScore:human.score}; } function requestedFit(metrics,cfg,difficulty,human){ if(!human.solved)return false; const [min,max]=cfg.clueBands[difficulty]; if(metrics.clues<min||metrics.clues>max)return false; if(difficulty==='easy')return human.maxTechnique<=2; if(difficulty==='normal')return human.maxTechnique<=5; return human.maxTechnique<=5; } function finalValidate(puzzle,solution,cfg){ if(!validateComplete(solution,cfg))return {valid:false,reason:'SOLUTION_STRUCTURE'}; for(let i=0;i<puzzle.length;i++)if(puzzle[i]&&puzzle[i]!==solution[i])return {valid:false,reason:'GIVEN_MISMATCH'}; const fast=countSolutionsFast(puzzle,cfg,2);if(fast.count!==1)return {valid:false,reason:'FAST_NOT_UNIQUE',count:fast.count}; const independent=countSolutionsIndependent(puzzle,cfg,2);if(independent.count!==1)return {valid:false,reason:'INDEPENDENT_NOT_UNIQUE',count:independent.count}; if(!independent.firstSolution||independent.firstSolution.some((v,i)=>v!==solution[i]))return {valid:false,reason:'SOLUTION_MISMATCH'}; return {valid:true,fastNodes:fast.nodes,independentNodes:independent.nodes}; } function generatePuzzle(opts){ opts=opts||{};const cfg=getConfig(opts.size||6),difficulty=opts.difficulty||'normal'; if(!['easy','normal','hard'].includes(difficulty))throw new Error('BAD_DIFFICULTY'); const baseSeed=hashSeed(opts.seed==null?Date.now():opts.seed),maxAttempts=opts.maxAttempts||36; let best=null; for(let attempt=0;attempt<maxAttempts;attempt++){ const attemptSeed=(baseSeed+Math.imul(attempt+1,0x9e3779b1))>>>0,rng=createRng(attemptSeed); const solution=generateFullSolution(cfg.size,rng),puzzle=solution.slice(); const target=targetClueCount(cfg,difficulty,rng),groups=removalGroups(cfg,rng); let clues=puzzle.length; for(const group of groups){ if(clues<=target)break; if(clues-group.length<target)continue; const old=group.map(i=>puzzle[i]);group.forEach(i=>puzzle[i]=0); const unique=countSolutionsFast(puzzle,cfg,2); if(unique.count!==1){group.forEach((i,k)=>puzzle[i]=old[k]);continue;} const hs=humanSolve(puzzle,cfg); if(!hs.solved){group.forEach((i,k)=>puzzle[i]=old[k]);continue;} clues-=group.length; } const human=humanSolve(puzzle,cfg);if(!human.solved)continue; const metrics=difficultyMetrics(puzzle,cfg,human),validation=finalValidate(puzzle,solution,cfg);if(!validation.valid)continue; const result={size:cfg.size,boxRows:cfg.boxRows,boxCols:cfg.boxCols,puzzle,solution,seed:attemptSeed,difficulty,metrics,techniques:[...new Set(human.steps.map(s=>s.technique))],validation}; const band=cfg.clueBands[difficulty],mid=(band[0]+band[1])/2; let distance=Math.abs(metrics.clues-mid); if(difficulty==='easy'&&metrics.maxTechnique>2)distance+=50; if(!best||distance<best.distance)best={distance,result}; if(requestedFit(metrics,cfg,difficulty,human))return result; } throw new Error(best?'DIFFICULTY_NOT_REACHED':'GENERATION_FAILED'); } function generateSet(opts){ opts=opts||{};const count=Math.max(1,Math.min(2,Number(opts.count)||1)),seed=hashSeed(opts.seed==null?Date.now():opts.seed),out=[]; for(let i=0;i<count;i++)out.push(generatePuzzle({...opts,seed:(seed+Math.imul(i+1,0x85ebca6b))>>>0})); return out; } const api={CONFIGS,getConfig,createRng,generateFullSolution,validateComplete,countSolutionsFast,countSolutionsIndependent,humanSolve,finalValidate,generatePuzzle,generateSet,boxIndex}; if(typeof module!=='undefined'&&module.exports)module.exports=api; global.KlasklaarSudokuEngine=api; })(typeof window!=='undefined'?window:globalThis); (function(){ 'use strict'; const root=document.getElementById('kksu-sudoku-root'); if(!root||root.dataset.kksuInitialized==='1')return; root.dataset.kksuInitialized='1'; const E=window.KlasklaarSudokuEngine; if(!E)throw new Error('SUDOKU_ENGINE_MISSING'); const q=(s)=>root.querySelector(s),qa=(s)=>Array.from(root.querySelectorAll(s)); const els={ title:q('#kksu-titleInput'), status:q('#kksu-statusBox'), stage:q('#kksu-sheetStage'), meta:q('#kksu-previewMeta'), showName:q('#kksu-showName'),showGroup:q('#kksu-showGroup'),showDate:q('#kksu-showDate'),includeSolution:q('#kksu-includeSolution'), newBtn:q('#kksu-newVersionBtn'),print:q('#kksu-printBtn') }; const state={size:6,difficulty:'normal',count:1,seed:makeSeed(),results:null,activeTab:'student',generationId:0,generating:false}; const DIFF_LABEL={easy:'Makkelijk',normal:'Normaal',hard:'Moeilijk'}; function makeSeed(){ try{if(window.crypto&&crypto.getRandomValues){const a=new Uint32Array(1);crypto.getRandomValues(a);return a[0]>>>0;}}catch(_e){} return ((Date.now()^Math.floor(Math.random()*0xffffffff))>>>0); } function esc(s){return String(s==null?'':s).replace(/[&<>\"]/g,m=>({'&':'&','<':'<','>':'>','\"':'"'}[m]));} function setStatus(type,title,text){els.status.className='status '+type;els.status.innerHTML='<strong>'+esc(title)+'</strong><span>'+esc(text)+'</span>';} function formatCount(n){return n===1?'1 sudoku':'2 sudoku\'s';} function updateMeta(){els.meta.textContent=state.size+' × '+state.size+' · '+DIFF_LABEL[state.difficulty]+' · '+formatCount(state.count);} function setGenerating(on){state.generating=on;els.newBtn.disabled=on||!state.results;els.print.disabled=on||!state.results;root.classList.toggle('is-generating',on);} function selectButtons(selector,key,value){qa(selector).forEach(b=>b.classList.toggle('active',String(b.dataset[key])===String(value)));} function generate(){ const id=++state.generationId;setGenerating(true);setStatus('info','Sudoku wordt gemaakt','De puzzel wordt op één unieke oplossing en oplosbaarheid gecontroleerd.'); els.stage.innerHTML='<div class="empty-preview"><div><strong class="spinning">Sudoku wordt gecontroleerd</strong>De preview verschijnt automatisch.</div></div>'; setTimeout(()=>{ if(id!==state.generationId)return; try{ const results=E.generateSet({size:state.size,difficulty:state.difficulty,count:state.count,seed:state.seed,maxAttempts:60}); if(id!==state.generationId)return; // Belt-and-braces: run independent validation once more at application boundary. for(const r of results){ const cfg=E.getConfig(r.size);const v=E.finalValidate(r.puzzle,r.solution,cfg);if(!v.valid)throw new Error('FINAL_VALIDATION_FAILED_'+v.reason); } state.results=results;setGenerating(false); setStatus('success','Gecontroleerd','Iedere sudoku heeft precies één oplossing en is onafhankelijk nagekeken.'); renderPreview();updateMeta(); }catch(err){ if(id!==state.generationId)return;state.results=null;setGenerating(false); setStatus('error','Deze sudoku kon niet worden gemaakt','Probeer Nieuwe versie of kies een ander formaat of niveau.'); els.stage.innerHTML='<div class="empty-preview"><div><strong>Geen geldige sudoku gevonden</strong>Er wordt nooit een puzzel getoond die niet volledig is gecontroleerd.</div></div>'; if(window.console&&console.error)console.error('Klasklaar sudoku generation:',err); } },30); } function instruction(size){return 'Vul de lege vakjes in met de cijfers 1 t/m '+size+'. Elk cijfer komt één keer voor in iedere rij, kolom en dik omlijnd blok.';} function studentFieldsHtml(){ const fields=[]; if(els.showName.checked)fields.push(['Naam','42mm']); if(els.showGroup.checked)fields.push(['Groep','26mm']); if(els.showDate.checked)fields.push(['Datum','30mm']); if(!fields.length)return ''; return '<div class="kksu-print-student-row">'+fields.map(([label,w])=>'<div class="kksu-print-student-field"><span class="kksu-print-student-label">'+label+'</span><span class="kksu-print-student-line" style="min-width:'+w+'"></span></div>').join('')+'</div>'; } function sudokuSvg(result,solutionMode,physicalMm){ const n=result.size,boxRows=result.boxRows,boxCols=result.boxCols,board=solutionMode?result.solution:result.puzzle,solution=result.solution; const U=900,cell=U/n,thin=Math.max(1.1,U/900*1.25),thick=Math.max(3.6,U/900*3.8),outer=Math.max(5,U/900*5.2); let s='<svg class="kksu-print-grid" role="img" aria-label="Sudoku '+n+' bij '+n+'" viewBox="0 0 '+U+' '+U+'" style="width:'+physicalMm+'mm;height:'+physicalMm+'mm">'; s+='<rect x="'+outer/2+'" y="'+outer/2+'" width="'+(U-outer)+'" height="'+(U-outer)+'" fill="white" stroke="#111" stroke-width="'+outer+'"/>'; for(let i=1;i<n;i++){ const x=i*cell,sw=(i%boxCols===0)?thick:thin;s+='<line x1="'+x+'" y1="0" x2="'+x+'" y2="'+U+'" stroke="#111" stroke-width="'+sw+'"/>'; } for(let i=1;i<n;i++){ const y=i*cell,sw=(i%boxRows===0)?thick:thin;s+='<line x1="0" y1="'+y+'" x2="'+U+'" y2="'+y+'" stroke="#111" stroke-width="'+sw+'"/>'; } const fs=n===4?cell*.44:n===6?cell*.43:cell*.42; for(let r=0;r<n;r++)for(let c=0;c<n;c++){ const i=r*n+c,v=board[i];if(!v)continue; const given=!!result.puzzle[i]; const weight=given?'800':solutionMode?'500':'800'; const fill=(solutionMode&&!given)?'#444':'#111'; s+='<text x="'+((c+.5)*cell)+'" y="'+((r+.5)*cell)+'" text-anchor="middle" dominant-baseline="central" font-size="'+fs+'" font-weight="'+weight+'" fill="'+fill+'">'+v+'</text>'; } s+='</svg>';return s; } function puzzleBlock(result,index,solutionMode,count){ const single=count===1; let mm; if(single) mm=result.size===4?108:result.size===6?124:138; else mm=result.size===4?78:result.size===6?82:88; const label=count===2?'<div class="kksu-print-puzzle-label">Sudoku '+(index+1)+'</div>':''; return '<div class="kksu-print-puzzle '+(single?'single':'double')+'">'+label+sudokuSvg(result,solutionMode,mm)+'</div>'; } function sheetHtml(solutionMode){ if(!state.results)return ''; const title=esc(els.title.value.trim()||'Sudoku'),count=state.results.length; const body=count===1 ?'<div class="kksu-print-puzzles one">'+puzzleBlock(state.results[0],0,solutionMode,count)+'</div>' :'<div class="kksu-print-puzzles two">'+state.results.map((r,i)=>puzzleBlock(r,i,solutionMode,count)).join('')+'</div>'; const info=solutionMode?'Antwoordblad · '+state.size+' × '+state.size:instruction(state.size); return '<section class="kksu-print-page">'+ '<div class="kksu-print-content">'+ '<header class="kksu-print-header"><div class="kksu-print-heading"><div class="kksu-print-kicker">Sudoku</div><h1 class="kksu-print-title">'+title+'</h1></div><div class="kksu-print-logo-box"><img class="kksu-print-logo" src="'+window.__KKSU_LOGO_DATA+'" alt="Klasklaar"></div></header>'+ studentFieldsHtml()+ '<p class="kksu-print-instruction">'+esc(info)+'</p>'+body+ '<div class="kksu-print-footer">Klasklaar.nl</div>'+ '</div></section>'; } function renderPreview(){ if(!state.results)return; if(state.activeTab==='solution'&&!els.includeSolution.checked)state.activeTab='student'; qa('.tab').forEach(t=>t.classList.toggle('active',t.dataset.tab===state.activeTab)); const solTab=q('.tab[data-tab="solution"]');if(solTab)solTab.style.display=els.includeSolution.checked?'':'none'; els.stage.innerHTML='<div class="kksu-preview-page-wrap kksu-doc" id="kksu-previewPageWrap">'+sheetHtml(state.activeTab==='solution')+'</div>'; requestAnimationFrame(fitSheet); } function fitSheet(){ const wrap=q('#kksu-previewPageWrap');if(!wrap)return; const page=wrap.querySelector('.kksu-print-page');if(!page)return; wrap.style.transform='none'; const stageW=els.stage.clientWidth,baseW=page.getBoundingClientRect().width; if(!stageW||!baseW)return; const maxScale=1.42,scale=Math.min(maxScale,Math.max(.25,(stageW-8)/baseW)); wrap.style.transform='scale('+scale+')';wrap.style.left=Math.max(0,(stageW-baseW*scale)/2)+'px';wrap.style.top='0px'; els.stage.style.height=Math.ceil(page.getBoundingClientRect().height*scale+8)+'px'; } function scheduleRender(){if(state.results)renderPreview();} qa('.size-card').forEach(b=>b.addEventListener('click',()=>{const v=Number(b.dataset.size);if(v===state.size)return;state.size=v;selectButtons('.size-card','size',v);state.seed=makeSeed();updateMeta();generate();})); qa('.level').forEach(b=>b.addEventListener('click',()=>{const v=b.dataset.level;if(v===state.difficulty)return;state.difficulty=v;selectButtons('.level','level',v);state.seed=makeSeed();updateMeta();generate();})); qa('.count-choice').forEach(b=>b.addEventListener('click',()=>{const v=Number(b.dataset.count);if(v===state.count)return;state.count=v;selectButtons('.count-choice','count',v);state.seed=makeSeed();updateMeta();generate();})); els.title.addEventListener('input',scheduleRender); [els.showName,els.showGroup,els.showDate].forEach(x=>x.addEventListener('change',scheduleRender)); els.includeSolution.addEventListener('change',()=>{if(!els.includeSolution.checked&&state.activeTab==='solution')state.activeTab='student';scheduleRender();}); els.newBtn.addEventListener('click',()=>{state.seed=makeSeed();generate();}); qa('.tab').forEach(t=>t.addEventListener('click',()=>{if(t.dataset.tab==='solution'&&!els.includeSolution.checked)return;state.activeTab=t.dataset.tab;renderPreview();})); window.addEventListener('resize',()=>requestAnimationFrame(fitSheet)); function printWorksheet(){ if(!state.results||state.generating)return; const pages=[sheetHtml(false)];if(els.includeSolution.checked)pages.push(sheetHtml(true)); const printLayer=document.createElement('div');printLayer.id='kksu-print-sandbox';printLayer.className='kksu-doc';printLayer.innerHTML=pages.join(''); // The JouwWeb embed stylesheet lives in . During isolated printing we temporarily // remove every body node, so clone the complete Sudoku stylesheet into first. // This keeps the printed A4 pixel-for-pixel styled like the live preview. const sourceStyle=document.getElementById('kksu-sudoku-style'); const tempStyle=document.createElement('style'); tempStyle.id='kksu-print-temp-style'; tempStyle.textContent=(sourceStyle?sourceStyle.textContent:'')+'\n'+ '@page{size:A4 portrait;margin:0}'+ 'html,body{margin:0!important;padding:0!important;background:#fff!important;width:210mm!important;min-width:210mm!important;max-width:210mm!important}'+ '#kksu-print-sandbox{display:block!important;margin:0!important;padding:0!important;width:210mm!important;min-width:210mm!important;max-width:210mm!important;background:#fff!important}'+ '#kksu-print-sandbox .kksu-print-page{display:block!important;width:210mm!important;min-width:210mm!important;max-width:210mm!important;height:297mm!important;min-height:297mm!important;max-height:297mm!important;margin:0!important;box-shadow:none!important;overflow:hidden!important}'; document.head.appendChild(tempStyle); const body=document.body,oldClass=body.className,oldStyle=body.getAttribute('style'),oldTitle=document.title,stash=document.createDocumentFragment(); Array.from(body.childNodes).forEach(node=>stash.appendChild(node)); body.className='kksu-print-isolated';body.removeAttribute('style');document.title='';body.appendChild(printLayer); let cleaned=false; const cleanup=()=>{if(cleaned)return;cleaned=true;try{printLayer.remove();}catch(_e){}while(body.firstChild)body.removeChild(body.firstChild);body.appendChild(stash);body.className=oldClass;document.title=oldTitle;if(oldStyle===null)body.removeAttribute('style');else body.setAttribute('style',oldStyle);tempStyle.remove();requestAnimationFrame(fitSheet);}; window.addEventListener('afterprint',cleanup,{once:true}); setTimeout(()=>{try{window.print();}catch(err){if(window.console&&console.error)console.error('Klasklaar sudoku print:',err);}finally{setTimeout(cleanup,0);}},80); } els.print.addEventListener('click',printWorksheet); selectButtons('.size-card','size',state.size);selectButtons('.level','level',state.difficulty);selectButtons('.count-choice','count',state.count);updateMeta();generate(); })();
${body}
${SITE_TEXT}
${rows}