'use strict'; // Vluchtige Node-test: laadt app.js met een minimale DOM-stub en // controleert of het neuraal netwerk echt leert (XOR / cirkel / spiraal). const fs = require('fs'); const path = require('path'); const src = fs.readFileSync(path.join(__dirname, 'app.js'), 'utf8'); function makeCtx() { const data = new Uint8ClampedArray(200 * 150 * 4); return { createImageData: () => ({ width: 200, height: 150, data }), putImageData: () => {}, clearRect: () => {}, drawImage: () => {}, fillRect: () => {}, fillText: () => {}, beginPath: () => {}, arc: () => {}, fill: () => {}, stroke: () => {}, moveTo: () => {}, lineTo: () => {}, strokeRect: () => {} }; } function makeEl() { return { width: 460, height: 320, style: {}, textContent: '', innerHTML: '', scrollTop: 0, scrollHeight: 0, classList: { add() {}, remove() {}, toggle() { return false; } }, appendChild() {}, addEventListener() {}, setAttribute() {}, click() {}, getContext: makeCtx, parentElement: { clientWidth: 800 }, dataset: {}, value: '6', toDataURL: () => 'data:image/png;base64,xx' }; } const documentStub = { getElementById: () => makeEl(), querySelectorAll: () => [], createElement: () => makeEl() }; const rafNoop = () => {}; const code = src + '\n;globalThis.__NN = NeuralNet; globalThis.__makeDataset = makeDataset;'; new Function('document', 'requestAnimationFrame', 'window', 'setTimeout', code)( documentStub, rafNoop, { addEventListener() {} }, setTimeout ); const NeuralNet = globalThis.__NN; const makeDataset = globalThis.__makeDataset; function trainOn(name, epochs, lr, hidden) { const net = new NeuralNet([2, hidden, 1]); const samples = makeDataset(name); const idx = samples.map((_, i) => i); let last = null; for (let e = 0; e < epochs; e++) { for (let i = idx.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); const t = idx[i]; idx[i] = idx[j]; idx[j] = t; } let sum = 0; for (const i of idx) sum += net.trainSample(samples[i].x, samples[i].y, lr); last = sum / idx.length; } const test = makeDataset(name); const n = Math.min(200, test.length); let correct = 0; for (let i = 0; i < n; i++) { if ((net.predict(test[i].x) > 0.5 ? 1 : 0) === test[i].y) correct++; } return { acc: (correct / n) * 100, loss: last }; } // Finale controle — zelfde instelling als app.js (Adam, lr 0.05) function trial(name, hidden, epochs) { const res = []; for (let t = 0; t < 6; t++) res.push(trainOn(name, epochs, 0.05, hidden)); const worst = Math.min(...res.map((a) => a.acc)); return { worst, raw: res.map((a) => a.acc.toFixed(1) + '%').join(' ') }; } const xor0 = trainOn('XOR', 0, 0.05, 10); const xor = trial('XOR', 10, 250); const cir = trial('CIRKEL', 14, 400); const spi = trial('SPIRAAL', 30, 1400); console.log('XOR 0ep :', xor0.acc.toFixed(1) + '%'); console.log('XOR 250ep :', xor.raw, ' (slechtste ' + xor.worst.toFixed(1) + '%)'); console.log('CIRKEL 400ep :', cir.raw, ' (slechtste ' + cir.worst.toFixed(1) + '%)'); console.log('SPIRAAL 1400ep :', spi.raw, ' (slechtste ' + spi.worst.toFixed(1) + '%)'); let fail = 0; if (xor0.acc < 35 || xor0.acc > 70) { console.error('FAIL: XOR ongetraind moet ~50% zijn'); fail = 1; } if (xor.worst < 85) { console.error('FAIL: XOR convergerde niet'); fail = 1; } if (cir.worst < 90) { console.error('FAIL: Cirkel convergerde niet'); fail = 1; } if (spi.worst < 85) { console.error('FAIL: Spiraal convergerde niet'); fail = 1; } if (fail) { console.error('VALIDATIE MISLUKT'); process.exit(1); } console.log('PASS: alle drie datasets leren betrouwbaar met Adam (lr 0.05).'); process.exit(0); if (fail) process.exit(1); console.log('PASS: app.js laadt schoon én het netwerk leert alle drie datasets.'); process.exit(0);