An visual IQ test generator
Prompt
Create an IQ test generator that runs entirely in the browser, generates simple graphical 3×3 sequence-completion tasks whose images are derived from mathematical transformation functions to ensure logical consistency, supports three difficulty levels, uses multiple-choice selection for the missing ninth image, tracks user responses, and outputs an approximate IQ estimate.
A system prompt was added to support web rendering
An IQ test generator that runs entirely in the browser, generates simple graphical 3×3 sequence-completion tasks whose images are derived from mathematical transformation functions to ensure logical consistency, supports three difficulty levels, uses multiple-choice selection for the missing ninth image, tracks user responses, and outputs an approximate IQ estimate. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>IQ Test Generator</title> <style> :root { --bg-color: #f0f2f5; --container-bg: #ffffff; --primary-text: #333; --secondary-text: #555; --border-color: #dbe2e8; --accent-color: #007bff; --accent-hover: #0056b3; --correct-color: #28a745; --incorrect-color: #dc3545; --shadow: 0 4px 12px rgba(0, 0, 0, 0.1); } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background-color: var(--bg-color); color: var(--primary-text); display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; padding: 20px; box-sizing: border-box; } .app-container { width: 100%; max-width: 600px; background-color: var(--container-bg); border-radius: 12px; box-shadow: var(--shadow); padding: 2rem; text-align: center; transition: all 0.3s ease; } h1 { color: var(--primary-text); margin-bottom: 0.5rem; } h2 { color: var(--primary-text); margin-top: 0; } p { color: var(--secondary-text); line-height: 1.6; margin-bottom: 1.5rem; } .screen { display: none; } .screen.active { display: block; } .difficulty-selection button { background-color: var(--accent-color); color: white; border: none; padding: 12px 24px; margin: 5px; border-radius: 8px; cursor: pointer; font-size: 1rem; font-weight: 500; transition: background-color 0.2s ease, transform 0.2s ease; } .difficulty-selection button:hover { background-color: var(--accent-hover); transform: translateY(-2px); } #question-status { margin-bottom: 1rem; font-weight: 500; color: var(--secondary-text); } .puzzle-grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-gap: 10px; width: 330px; margin: 1.5rem auto; border: 2px solid var(--border-color); padding: 10px; border-radius: 8px; } .grid-cell { width: 100px; height: 100px; background-color: #f8f9fa; border: 1px solid var(--border-color); display: flex; justify-content: center; align-items: center; border-radius: 4px; } .grid-cell.question-mark { font-size: 3rem; color: var(--accent-color); font-weight: bold; } .options-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); grid-gap: 15px; margin-top: 2rem; } .option-cell { height: 100px; border: 2px solid var(--border-color); border-radius: 8px; cursor: pointer; transition: all 0.2s ease; background-color: #fff; } .option-cell:hover { border-color: var(--accent-color); transform: scale(1.05); } .result-score { font-size: 1.8rem; font-weight: bold; color: var(--accent-color); margin: 1rem 0; } .result-iq { font-size: 2.5rem; font-weight: bold; color: var(--primary-text); margin-bottom: 2rem; } canvas { width: 100%; height: 100%; } </style> </head> <body> <div class="app-container"> <!-- Start Screen --> <div id="start-screen" class="screen active"> <h1>Graphical IQ Test</h1> <p>Complete the 3x3 pattern by choosing the correct image for the final square. Select a difficulty level to begin.</p> <div class="difficulty-selection"> <button onclick="startGame('easy')">Easy</button> <button onclick="startGame('medium')">Medium</button> <button onclick="startGame('hard')">Hard</button> </div> </div> <!-- Test Screen --> <div id="test-screen" class="screen"> <h2 id="question-status">Question 1 of 10</h2> <div class="puzzle-grid" id="puzzle-grid"> <!-- Grid cells will be generated by JS --> </div> <p>Which option completes the pattern?</p> <div class="options-grid" id="options-grid"> <!-- Options will be generated by JS --> </div> </div> <!-- Results Screen --> <div id="results-screen" class="screen"> <h1>Test Complete!</h1> <p>You answered:</p> <div class="result-score" id="final-score"></div> <p>Based on your performance, your estimated IQ is:</p> <div class="result-iq" id="iq-estimate"></div> <button onclick="restartTest()">Try Again</button> </div> </div> <script> const DOM = { screens: document.querySelectorAll('.screen'), startScreen: document.getElementById('start-screen'), testScreen: document.getElementById('test-screen'), resultsScreen: document.getElementById('results-screen'), questionStatus: document.getElementById('question-status'), puzzleGrid: document.getElementById('puzzle-grid'), optionsGrid: document.getElementById('options-grid'), finalScore: document.getElementById('final-score'), iqEstimate: document.getElementById('iq-estimate'), }; let gameState = {}; const CONFIG = { SHAPES: ['square', 'circle', 'triangle', 'cross', 'star'], COLORS: ['#3498db', '#e74c3c', '#2ecc71', '#f1c40f', '#9b59b6'], FILL_STYLES: [true, false], QUESTIONS_PER_DIFFICULTY: { easy: 8, medium: 10, hard: 12 }, OPTIONS_COUNT: 6 }; // --- UTILITY FUNCTIONS --- const util = { shuffle: (array) => array.sort(() => Math.random() - 0.5), getRandom: (arr) => arr[Math.floor(Math.random() * arr.length)], clone: (obj) => JSON.parse(JSON.stringify(obj)), getNextInCycle: (arr, current) => { const currentIndex = arr.indexOf(current); return arr[(currentIndex + 1) % arr.length]; } }; // --- DRAWING FUNCTIONS --- const drawer = { draw: (ctx, state) => { if (!state) return; ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); ctx.save(); ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2); const scale = state.size || 0.6; ctx.scale(scale, scale); ctx.rotate((state.rotation || 0) * Math.PI / 180); ctx.strokeStyle = state.color || '#000000'; ctx.fillStyle = state.color || '#000000'; ctx.lineWidth = 10; drawer[state.shape](ctx); if (state.fill) { ctx.fill(); } else { ctx.stroke(); } ctx.restore(); }, square: (ctx) => { ctx.beginPath(); ctx.rect(-50, -50, 100, 100); }, circle: (ctx) => { ctx.beginPath(); ctx.arc(0, 0, 50, 0, 2 * Math.PI); }, triangle: (ctx) => { ctx.beginPath(); ctx.moveTo(0, -58); ctx.lineTo(50, 50); ctx.lineTo(-50, 50); ctx.closePath(); }, cross: (ctx) => { ctx.beginPath(); ctx.moveTo(-50, -15); ctx.lineTo(-15, -15); ctx.lineTo(-15, -50); ctx.lineTo(15, -50); ctx.lineTo(15, -15); ctx.lineTo(50, -15); ctx.lineTo(50, 15); ctx.lineTo(15, 15); ctx.lineTo(15, 50); ctx.lineTo(-15, 50); ctx.lineTo(-15, 15); ctx.lineTo(-50, 15); ctx.closePath(); }, star: (ctx) => { ctx.beginPath(); ctx.moveTo(0, -50); for (let i = 0; i < 5; i++) { ctx.rotate(Math.PI / 5); ctx.lineTo(0, -(50 * 0.4)); ctx.rotate(Math.PI / 5); ctx.lineTo(0, -50); } ctx.closePath(); } }; // --- TRANSFORMATION LOGIC --- const transforms = { identity: (s) => util.clone(s), rotate45: (s) => (s.rotation = ((s.rotation || 0) + 45) % 360, s), rotate90: (s) => (s.rotation = ((s.rotation || 0) + 90) % 360, s), nextShape: (s) => (s.shape = util.getNextInCycle(CONFIG.SHAPES, s.shape), s), nextColor: (s) => (s.color = util.getNextInCycle(CONFIG.COLORS, s.color), s), toggleFill: (s) => (s.fill = !s.fill, s) }; // --- LOGICAL OPERATIONS for row 3 --- const operations = { // Row3 is combination of Row1 and Row2 properties superposition: (s1, s2) => { let newState = util.clone(s1); // Simple version: s2 color becomes s1's fill or vice versa if(s1.fill && !s2.fill) newState.fill = false; else if(!s1.fill && s2.fill) newState.fill = true; newState.rotation = (s1.rotation + s2.rotation) % 360; return newState; }, // Row3 shape is XOR of Row1 and Row2 xorShape: (s1, s2) => { let newState = util.clone(s1); if(s1.shape === s2.shape) { newState.shape = util.getNextInCycle(CONFIG.SHAPES, s1.shape); } return newState; } } // --- QUESTION GENERATION --- function generateQuestion(difficulty) { const type = (difficulty === 'hard' && Math.random() > 0.5) ? 'operation' : 'progression'; if (type === 'progression') { return generateProgressionQuestion(difficulty); } else { return generateOperationQuestion(difficulty); } } function generateProgressionQuestion(difficulty) { let baseState = { shape: util.getRandom(CONFIG.SHAPES), color: util.getRandom(CONFIG.COLORS), rotation: util.getRandom([0, 45, 90]), fill: util.getRandom(CONFIG.FILL_STYLES), }; let rowTransforms = [transforms.identity]; let colTransforms = [transforms.identity]; const availableTransforms = [transforms.rotate45, transforms.rotate90, transforms.nextColor, transforms.nextShape, transforms.toggleFill]; rowTransforms = [util.getRandom(availableTransforms)]; if(difficulty === 'easy' && Math.random() > 0.5) { // 50% chance of only one transform colTransforms = [transforms.identity]; } else { colTransforms = [util.getRandom(availableTransforms.filter(t => t !== rowTransforms[0]))]; } if (difficulty === 'medium') { if (Math.random() > 0.5) rowTransforms.push(util.getRandom(availableTransforms.filter(t => !rowTransforms.includes(t)))); } else if (difficulty === 'hard') { rowTransforms.push(util.getRandom(availableTransforms.filter(t => !rowTransforms.includes(t)))); colTransforms.push(util.getRandom(availableTransforms.filter(t => !colTransforms.includes(t)))); } const grid = Array(9).fill(null); for (let r = 0; r < 3; r++) { for (let c = 0; c < 3; c++) { let state = util.clone(baseState); for (let i = 0; i < c; i++) rowTransforms.forEach(t => state = t(state)); for (let i = 0; i < r; i++) colTransforms.forEach(t => state = t(state)); grid[r * 3 + c] = state; } } const correctAnswer = grid[8]; const distractors = generateDistractors(correctAnswer, {row: rowTransforms, col: colTransforms, base: baseState}); const options = util.shuffle([correctAnswer, ...distractors]); return { grid, options, correctAnswer }; } function generateOperationQuestion(difficulty) { // Rule: Grid[2][c] = Op(Grid[0][c], Grid[1][c]) const op = util.getRandom(Object.values(operations)); const grid = Array(9).fill(null); // Generate first two rows with simple progression const row0Base = { shape: util.getRandom(CONFIG.SHAPES), color: util.getRandom(CONFIG.COLORS), rotation: 0, fill: true }; const row1Base = { shape: util.getRandom(CONFIG.SHAPES), color: util.getRandom(CONFIG.COLORS), rotation: 0, fill: true }; const rowProgression = util.getRandom([transforms.nextShape, transforms.nextColor, transforms.rotate90]); for(let c = 0; c < 3; c++) { let r0State = util.clone(row0Base); let r1State = util.clone(row1Base); for(let i = 0; i<c; i++) { r0State = rowProgression(r0State); r1State = rowProgression(r1State); // can use same or different progression } grid[c] = r0State; grid[3+c] = r1State; grid[6+c] = op(r0State, r1State); } const correctAnswer = grid[8]; const distractors = generateDistractors(correctAnswer, {}); const options = util.shuffle([correctAnswer, ...distractors]); return { grid, options, correctAnswer }; } function generateDistractors(correctAnswer, rules) { const distractors = new Set(); while(distractors.size < CONFIG.OPTIONS_COUNT - 1) { let distractor = util.clone(correctAnswer); // Plausible distractors if(rules.row && Math.random() > 0.5) { // Apply row transform again rules.row.forEach(t => distractor = t(distractor)); } else if(rules.col && Math.random() > 0.5) { // Apply col transform again rules.col.forEach(t => distractor = t(distractor)); } else { // Random mutation const propToChange = util.getRandom(['shape', 'color', 'rotation', 'fill']); switch (propToChange) { case 'shape': distractor.shape = util.getRandom(CONFIG.SHAPES); break; case 'color': distractor.color = util.getRandom(CONFIG.COLORS); break; case 'rotation': distractor.rotation = (distractor.rotation + util.getRandom([45, 90])) % 360; break; case 'fill': distractor.fill = !distractor.fill; break; } } // ensure no duplicates distractors.add(JSON.stringify(distractor)); } return Array.from(distractors).map(s => JSON.parse(s)); } // --- GAME FLOW --- function changeScreen(activeScreen) { DOM.screens.forEach(s => s.classList.remove('active')); activeScreen.classList.add('active'); } function startGame(difficulty) { gameState = { difficulty, score: 0, currentQuestion: 0, totalQuestions: CONFIG.QUESTIONS_PER_DIFFICULTY[difficulty], questions: [] }; for (let i = 0; i < gameState.totalQuestions; i++) { gameState.questions.push(generateQuestion(difficulty)); } changeScreen(DOM.testScreen); displayQuestion(); } function displayQuestion() { const q = gameState.questions[gameState.currentQuestion]; DOM.questionStatus.textContent = `Question ${gameState.currentQuestion + 1} of ${gameState.totalQuestions}`; // Populate puzzle grid DOM.puzzleGrid.innerHTML = ''; for (let i = 0; i < 9; i++) { const cell = document.createElement('div'); cell.className = 'grid-cell'; if (i === 8) { cell.classList.add('question-mark'); cell.textContent = '?'; } else { const canvas = document.createElement('canvas'); canvas.width = 100; canvas.height = 100; cell.appendChild(canvas); const ctx = canvas.getContext('2d'); drawer.draw(ctx, q.grid[i]); } DOM.puzzleGrid.appendChild(cell); } // Populate options grid DOM.optionsGrid.innerHTML = ''; q.options.forEach((optionState, index) => { const cell = document.createElement('div'); cell.className = 'option-cell'; const canvas = document.createElement('canvas'); canvas.width = 100; canvas.height = 100; cell.appendChild(canvas); const ctx = canvas.getContext('2d'); drawer.draw(ctx, optionState); cell.onclick = () => selectAnswer(optionState); DOM.optionsGrid.appendChild(cell); }); } function selectAnswer(selectedOption) { const q = gameState.questions[gameState.currentQuestion]; // Deep comparison of objects if (JSON.stringify(selectedOption) === JSON.stringify(q.correctAnswer)) { gameState.score++; } gameState.currentQuestion++; if (gameState.currentQuestion < gameState.totalQuestions) { displayQuestion(); } else { showResults(); } } function showResults() { changeScreen(DOM.resultsScreen); const score = gameState.score; const total = gameState.totalQuestions; DOM.finalScore.textContent = `${score} / ${total} Correct`; // Approximate IQ calculation // This is a simplified model for entertainment purposes. // Real IQ is normalized against a population. const meanScoreRatio = { easy: 0.6, medium: 0.5, hard: 0.4 }; const sd = 0.15; // standard deviation for score ratio const userRatio = score / total; const meanRatio = meanScoreRatio[gameState.difficulty]; const zScore = (userRatio - meanRatio) / sd; const iq = Math.round(100 + zScore * 15); DOM.iqEstimate.textContent = iq; } function restartTest() { changeScreen(DOM.startScreen); } </script> </body> </html> ```
# IQ Test Generator with Graphical Sequence Completion I'll create a complete browser-based IQ test generator that displays 3×3 sequence-completion tasks with mathematical transformations, multiple difficulty levels, and IQ estimation. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Graphical IQ Test</title> <style> * { box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { background: linear-gradient(135deg, #1a2a6c, #b21f1f, #1a2a6c); color: white; margin: 0; padding: 20px; min-height: 100vh; } .container { max-width: 900px; margin: 0 auto; background: rgba(0, 0, 0, 0.7); border-radius: 15px; padding: 25px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); } header { text-align: center; margin-bottom: 20px; } h1 { font-size: 2.5rem; margin-bottom: 5px; text-shadow: 0 2px 4px rgba(0,0,0,0.5); background: linear-gradient(to right, #ff8a00, #da1b60); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } .subtitle { font-size: 1.1rem; opacity: 0.8; margin-bottom: 20px; } .difficulty-selector { display: flex; justify-content: center; gap: 15px; margin: 20px 0; } .difficulty-btn { padding: 12px 25px; border: none; border-radius: 50px; background: linear-gradient(145deg, #3a7bd5, #00d2ff); color: white; font-weight: bold; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 8px rgba(0,0,0,0.3); } .difficulty-btn:hover { transform: translateY(-3px); box-shadow: 0 6px 12px rgba(0,0,0,0.4); } .difficulty-btn.active { background: linear-gradient(145deg, #ff8a00, #da1b60); box-shadow: 0 0 15px rgba(255,138,0,0.7); } .test-container { display: none; flex-direction: column; align-items: center; } .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin: 20px 0; width: 100%; max-width: 600px; } .grid-cell { aspect-ratio: 1; background: rgba(255, 255, 255, 0.1); border-radius: 8px; display: flex; justify-content: center; align-items: center; position: relative; overflow: hidden; box-shadow: inset 0 0 10px rgba(0,0,0,0.3); } .grid-cell.missing { background: rgba(255, 255, 255, 0.05); border: 2px dashed rgba(255,255,255,0.3); } .options-container { display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; width: 100%; max-width: 600px; margin: 20px 0; } .option { aspect-ratio: 1; background: rgba(255, 255, 255, 0.1); border-radius: 8px; cursor: pointer; transition: all 0.2s ease; display: flex; justify-content: center; align-items: center; overflow: hidden; } .option:hover { transform: scale(1.05); background: rgba(255, 255, 255, 0.2); box-shadow: 0 0 15px rgba(100, 200, 255, 0.5); } .option.selected { transform: scale(1.08); box-shadow: 0 0 20px rgba(0, 200, 255, 0.8); border: 2px solid #00d2ff; } .progress-container { width: 100%; max-width: 600px; margin: 20px 0; } .progress-bar { height: 10px; background: rgba(255, 255, 255, 0.1); border-radius: 5px; overflow: hidden; } .progress-fill { height: 100%; background: linear-gradient(90deg, #3a7bd5, #00d2ff); border-radius: 5px; width: 0%; transition: width 0.5s ease; } .results { display: none; text-align: center; padding: 30px; } .iq-display { font-size: 5rem; font-weight: bold; margin: 20px 0; background: linear-gradient(to right, #ff8a00, #da1b60); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } .iq-description { font-size: 1.2rem; margin-bottom: 30px; max-width: 600px; margin-left: auto; margin-right: auto; } .restart-btn { padding: 12px 30px; background: linear-gradient(145deg, #3a7bd5, #00d2ff); color: white; border: none; border-radius: 50px; font-size: 1.1rem; cursor: pointer; transition: all 0.3s ease; } .restart-btn:hover { transform: translateY(-3px); box-shadow: 0 5px 15px rgba(0,0,0,0.3); } .status-bar { display: flex; justify-content: space-between; width: 100%; max-width: 600px; margin: 10px 0; } .score-display { font-size: 1.2rem; font-weight: bold; } canvas { max-width: 100%; max-height: 100%; } footer { text-align: center; margin-top: 30px; opacity: 0.7; font-size: 0.9rem; } </style> </head> <body> <div class="container"> <header> <h1>Graphical IQ Test</h1> <p class="subtitle">Complete the 3×3 sequences by selecting the missing pattern</p> </header> <div class="difficulty-selector"> <button class="difficulty-btn" data-level="easy">Easy</button> <button class="difficulty-btn" data-level="medium">Medium</button> <button class="difficulty-btn" data-level="hard">Hard</button> </div> <div class="test-container" id="testContainer"> <div class="grid-container" id="gridContainer"></div> <div class="options-container" id="optionsContainer"></div> <div class="status-bar"> <div class="score-display">Score: <span id="scoreValue">0</span></div> <div>Question: <span id="questionCount">1</span>/10</div> </div> <div class="progress-container"> <div class="progress-bar"> <div class="progress-fill" id="progressFill"></div> </div> </div> </div> <div class="results" id="resultsContainer"> <h2>Your IQ Estimate</h2> <div class="iq-display" id="iqValue">115</div> <p class="iq-description" id="iqDescription">Above average cognitive ability</p> <button class="restart-btn" id="restartBtn">Take Test Again</button> </div> <footer> <p>This test measures pattern recognition and logical reasoning abilities. Results are estimates only.</p> </footer> </div> <script> // DOM Elements const difficultyButtons = document.querySelectorAll('.difficulty-btn'); const testContainer = document.getElementById('testContainer'); const gridContainer = document.getElementById('gridContainer'); const optionsContainer = document.getElementById('optionsContainer'); const resultsContainer = document.getElementById('resultsContainer'); const scoreValue = document.getElementById('scoreValue'); const questionCount = document.getElementById('questionCount'); const progressFill = document.getElementById('progressFill'); const iqValue = document.getElementById('iqValue'); const iqDescription = document.getElementById('iqDescription'); const restartBtn = document.getElementById('restartBtn'); // Test state let currentDifficulty = null; let score = 0; let currentQuestion = 0; let totalQuestions = 10; let correctAnswerIndex = -1; let selectedOption = null; // Transformation functions for pattern generation const transformations = { // Shape transformations circle: (ctx, x, y, size, params) => { ctx.beginPath(); ctx.arc(x, y, size, 0, Math.PI * 2); ctx.fill(); }, square: (ctx, x, y, size, params) => { ctx.fillRect(x - size, y - size, size * 2, size * 2); }, triangle: (ctx, x, y, size, params) => { ctx.beginPath(); ctx.moveTo(x, y - size); ctx.lineTo(x + size, y + size); ctx.lineTo(x - size, y + size); ctx.closePath(); ctx.fill(); }, star: (ctx, x, y, size, params) => { ctx.beginPath(); for (let i = 0; i < 5; i++) { const angle = (i * 2 * Math.PI / 5) - Math.PI / 2; const outerX = x + size * Math.cos(angle); const outerY = y + size * Math.sin(angle); if (i === 0) ctx.moveTo(outerX, outerY); else ctx.lineTo(outerX, outerY); const innerAngle = angle + Math.PI / 5; const innerX = x + size * 0.4 * Math.cos(innerAngle); const innerY = y + size * 0.4 * Math.sin(innerAngle); ctx.lineTo(innerX, innerY); } ctx.closePath(); ctx.fill(); }, // Color transformations colorShift: (baseColor, row, col) => { const hueShift = (row * 40 + col * 20) % 360; return `hsl(${(baseColor + hueShift) % 360}, 80%, 60%)`; }, // Size transformations sizeScale: (baseSize, row, col) => { return baseSize * (0.8 + 0.2 * (row + col)); }, // Rotation transformations rotation: (baseRotation, row, col) => { return baseRotation + (row * 30 + col * 15); }, // Position transformations positionOffset: (baseX, baseY, row, col) => { return { x: baseX + (col - 1) * 15, y: baseY + (row - 1) * 15 }; } }; // Initialize the test function initTest() { score = 0; currentQuestion = 0; scoreValue.textContent = score; questionCount.textContent = "1"; progressFill.style.width = "0%"; testContainer.style.display = "flex"; resultsContainer.style.display = "none"; generateQuestion(); } // Generate a new question function generateQuestion() { currentQuestion++; questionCount.textContent = currentQuestion; progressFill.style.width = `${(currentQuestion-1)/totalQuestions*100}%`; // Clear containers gridContainer.innerHTML = ''; optionsContainer.innerHTML = ''; // Generate sequence pattern const pattern = generatePattern(); // Create grid cells for (let row = 0; row < 3; row++) { for (let col = 0; col < 3; col++) { const cell = document.createElement('div'); cell.className = 'grid-cell'; if (row === 2 && col === 2) { cell.classList.add('missing'); cell.textContent = '?'; } else { const canvas = document.createElement('canvas'); canvas.width = 100; canvas.height = 100; cell.appendChild(canvas); drawPattern(canvas, pattern, row, col); } gridContainer.appendChild(cell); } } // Generate answer options generateOptions(pattern); } // Generate pattern based on difficulty function generatePattern() { const pattern = { baseShape: ['circle', 'square', 'triangle', 'star'][Math.floor(Math.random() * 4)], baseColor: Math.floor(Math.random() * 360), baseSize: 20 + Math.floor(Math.random() * 15), baseRotation: Math.floor(Math.random() * 360), transformations: [] }; // Add transformations based on difficulty if (currentDifficulty === 'easy') { pattern.transformations.push('colorShift'); } else if (currentDifficulty === 'medium') { pattern.transformations.push('colorShift'); pattern.transformations.push('sizeScale'); } else { pattern.transformations.push('colorShift'); pattern.transformations.push('sizeScale'); pattern.transformations.push('rotation'); pattern.transformations.push('positionOffset'); } return pattern; } // Draw pattern in a grid cell function drawPattern(canvas, pattern, row, col) { const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); // Calculate position let x = canvas.width / 2; let y = canvas.height / 2; let size = pattern.baseSize; let color = pattern.baseColor; let rotation = pattern.baseRotation; // Apply transformations pattern.transformations.forEach(transformation => { if (transformation === 'colorShift') { color = transformations.colorShift(pattern.baseColor, row, col); } else if (transformation === 'sizeScale') { size = transformations.sizeScale(pattern.baseSize, row, col); } else if (transformation === 'rotation') { rotation = transformations.rotation(pattern.baseRotation, row, col); } else if (transformation === 'positionOffset') { const pos = transformations.positionOffset(x, y, row, col); x = pos.x; y = pos.y; } }); // Draw the shape ctx.save(); ctx.translate(x, y); ctx.rotate(rotation * Math.PI / 180); ctx.fillStyle = typeof color === 'string' ? color : `hsl(${color}, 80%, 60%)`; transformations[pattern.baseShape](ctx, 0, 0, size); ctx.restore(); } // Generate answer options function generateOptions(pattern) { const options = []; // Generate correct answer const correctCanvas = document.createElement('canvas'); correctCanvas.width = 100; correctCanvas.height = 100; drawPattern(correctCanvas, pattern, 2, 2); options.push(correctCanvas); // Generate incorrect options for (let i = 0; i < 3; i++) { const incorrectPattern = JSON.parse(JSON.stringify(pattern)); // Modify pattern to create plausible distractors if (Math.random() > 0.5) { // Change base shape const shapes = ['circle', 'square', 'triangle', 'star'].filter(s => s !== incorrectPattern.baseShape); incorrectPattern.baseShape = shapes[Math.floor(Math.random() * shapes.length)]; } else { // Modify transformation parameters if (incorrectPattern.transformations.includes('colorShift')) { incorrectPattern.baseColor = (incorrectPattern.baseColor + 120) % 360; } if (incorrectPattern.transformations.includes('sizeScale')) { incorrectPattern.baseSize = incorrectPattern.baseSize * (0.7 + Math.random() * 0.6); } if (incorrectPattern.transformations.includes('rotation')) { incorrectPattern.baseRotation = (incorrectPattern.baseRotation + 90) % 360; } } const canvas = document.createElement('canvas'); canvas.width = 100; canvas.height = 100; drawPattern(canvas, incorrectPattern, 2, 2); options.push(canvas); } // Shuffle options shuffleArray(options); // Add options to container options.forEach((canvas, index) => { const option = document.createElement('div'); option.className = 'option'; option.appendChild(canvas); // Store correct answer index if (canvas === correctCanvas) { correctAnswerIndex = index; } option.addEventListener('click', () => selectOption(option, index)); optionsContainer.appendChild(option); }); } // Select an option function selectOption(option, index) { // Remove previous selection if (selectedOption) { selectedOption.classList.remove('selected'); } // Set new selection option.classList.add('selected'); selectedOption = option; // Check answer after a short delay setTimeout(() => checkAnswer(index), 500); } // Check if answer is correct function checkAnswer(index) { if (index === correctAnswerIndex) { // Correct answer score += currentDifficulty === 'easy' ? 1 : currentDifficulty === 'medium' ? 2 : 3; scoreValue.textContent = score; } // Move to next question or show results if (currentQuestion < totalQuestions) { generateQuestion(); } else { showResults(); } } // Show final results function showResults() { testContainer.style.display = 'none'; resultsContainer.style.display = 'block'; // Calculate IQ estimate const maxScore = totalQuestions * 3; // 3 points for hard questions const iq = Math.floor(80 + (score / maxScore) * 40); // Scale to 80-120 iqValue.textContent = iq; // Set IQ description let description = ''; if (iq < 90) { description = 'Below average cognitive ability'; } else if (iq < 110) { description = 'Average cognitive ability'; } else if (iq < 130) { description = 'Above average cognitive ability'; } else { description = 'Superior cognitive ability'; } iqDescription.textContent = description; } // Utility function to shuffle array function shuffleArray(array) { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } } // Event listeners difficultyButtons.forEach(button => { button.addEventListener('click', () => { // Set active button difficultyButtons.forEach(btn => btn.classList.remove('active')); button.classList.add('active'); // Set difficulty and start test currentDifficulty = button.dataset.level; initTest(); }); }); restartBtn.addEventListener('click', () => { resultsContainer.style.display = 'none'; difficultyButtons.forEach(btn => btn.classList.remove('active')); }); </script> </body> </html> ``` ## Features Implemented 1. **Graphical Sequence Completion Tasks**: - Generates 3×3 grids with mathematical pattern sequences - Uses canvas to render geometric shapes with transformations - The bottom-right cell is always missing and needs completion 2. **Mathematical Transformations**: - Shape transformations (circle, square, triangle, star) - Color transformations using HSL color space - Size scaling based on position - Rotation transformations - Position offsets 3. **Difficulty Levels**: - Easy: Simple color transformations - Medium: Color and size transformations - Hard: All transformations including rotation and position 4. **Multiple-Choice Selection**: - Four options for the missing ninth image - One correct answer and three plausible distractors - Visual feedback when selecting an option 5. **Scoring and IQ Estimation**: - Tracks user responses and scores - Different points based on difficulty level - Calculates IQ estimate (80-120 range) after 10 questions - Provides IQ description based on score 6. **User Interface**: - Modern gradient-based design with dark theme - Progress bar showing test advancement - Responsive layout that works on different screen sizes - Visual feedback for selections and interactions The test runs entirely in the browser with no external dependencies. All pattern generation and transformation logic is implemented using JavaScript canvas API and mathematical functions.
```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Browser IQ Test Generator</title> <style> body { font-family: Arial, sans-serif; text-align: center; padding: 20px; } #difficultySelection, #testArea, #resultArea { margin: 20px auto; } button { margin: 5px; padding: 10px 20px; font-size: 16px; } #grid { display: grid; grid-template-columns: repeat(3, 100px); grid-gap: 10px; justify-content: center; margin-bottom: 20px; } .grid-canvas { border: 1px solid #ccc; } #options { display: grid; grid-template-columns: repeat(auto-fit, 80px); grid-gap: 10px; justify-content: center; } .option-canvas { border: 1px solid #ccc; cursor: pointer; } .option-canvas:hover { border-color: #666; } </style> </head> <body> <h1>Browser IQ Test</h1> <div id="difficultySelection"> <p>Select Difficulty Level:</p> <button id="easyBtn">Easy</button> <button id="medBtn">Medium</button> <button id="hardBtn">Hard</button> </div> <div id="testArea" style="display:none;"> <p id="questionNumber"></p> <div id="grid"></div> <div id="options"></div> </div> <div id="resultArea" style="display:none;"></div> <script> // Utility functions function randomChoice(arr){ return arr[Math.floor(Math.random()*arr.length)]; } function shuffle(arr){ for(let i=arr.length-1; i>0; i--){ let j = Math.floor(Math.random()*(i+1)); [arr[i], arr[j]] = [arr[j], arr[i]]; } return arr; } function applyTransform(params, type, delta){ let p = Object.assign({}, params); if(type === "rotation"){ p.rotation = (p.rotation + delta) % 360; } else if(type === "scale"){ p.scale = p.scale * delta; } else if(type === "hue"){ p.hue = (p.hue + delta) % 360; } return p; } // Drawing function function drawShape(ctx, params){ const w = ctx.canvas.width, h = ctx.canvas.height; ctx.clearRect(0,0,w,h); ctx.fillStyle = `hsl(${params.hue},60%,50%)`; ctx.save(); ctx.translate(w/2, h/2); ctx.rotate(params.rotation * Math.PI/180); ctx.scale(params.scale, params.scale); let sides = params.shape === "triangle" ? 3 : params.shape === "square" ? 4 : 5; let radius = Math.min(w, h)/4; ctx.beginPath(); for(let i=0; i<sides; i++){ let angle = (i * 2 * Math.PI / sides) - Math.PI/2; let x = radius * Math.cos(angle), y = radius * Math.sin(angle); if(i===0) ctx.moveTo(x,y); else ctx.lineTo(x,y); } ctx.closePath(); ctx.fill(); ctx.restore(); } // Question generation const shapeTypes = ["triangle","square","pentagon"]; const transformTypes = ["rotation","scale","hue"]; function generateQuestion(){ // Pick two distinct transformations let T1 = randomChoice(transformTypes); let T2 = randomChoice(transformTypes.filter(t=>t!==T1)); let delta1 = T1==="rotation"?90: T1==="scale"?1.5: 60; let delta2 = T2==="rotation"?90: T2==="scale"?1.5: 60; // Build a 3x3 grid of parameter objects let gridParams = []; for(let row=0; row<3; row++){ let base = { shape: randomChoice(shapeTypes), rotation: [0,90,180,270][Math.floor(Math.random()*4)], scale: 1, hue: Math.floor(Math.random()*360) }; let p0 = base; let p1 = applyTransform(p0, T1, delta1); let p2 = applyTransform(p1, T2, delta2); gridParams.push(p0, p1, p2); } // Save correct answer (last cell) and blank it let correct = gridParams[8]; gridParams[8] = null; // Generate multiple-choice options let options = [correct]; while(options.length < 6){ let d = { shape: randomChoice(shapeTypes), rotation: [0,90,180,270][Math.floor(Math.random()*4)], scale: [0.75,1,1.25,1.5][Math.floor(Math.random()*4)], hue: Math.floor(Math.random()*360) }; // avoid duplicates & correct let dup = options.some(o=> o.shape===d.shape && o.rotation===d.rotation && Math.abs(o.scale-d.scale)<0.01 && o.hue===d.hue ); if(!dup) options.push(d); } shuffle(options); return { grid: gridParams, answer: correct, options: options }; } // State let questions = [], currentIndex = 0, correctCount = 0; let difficulty = "", numQuestions = 0, expectedSuccess = 0; // UI Handlers function startTest(){ document.getElementById("difficultySelection").style.display = "none"; document.getElementById("testArea").style.display = "block"; questions = []; currentIndex = 0; correctCount = 0; if(difficulty==="Easy"){ numQuestions=5; expectedSuccess=0.9; } else if(difficulty==="Medium"){ numQuestions=7; expectedSuccess=0.7; } else { numQuestions=10; expectedSuccess=0.5; } for(let i=0; i<numQuestions; i++) questions.push(generateQuestion()); showQuestion(); } function showQuestion(){ let q = questions[currentIndex]; document.getElementById("questionNumber").innerText = `Question ${currentIndex+1} of ${numQuestions}`; // Draw grid let gridDiv = document.getElementById("grid"); gridDiv.innerHTML = ""; for(let i=0; i<9; i++){ let c = document.createElement("canvas"); c.width = 100; c.height = 100; c.className = "grid-canvas"; gridDiv.appendChild(c); if(q.grid[i]) drawShape(c.getContext("2d"), q.grid[i]); } // Draw options let optionsDiv = document.getElementById("options"); optionsDiv.innerHTML = ""; q.options.forEach(opt=>{ let c = document.createElement("canvas"); c.width = 80; c.height = 80; c.className = "option-canvas"; drawShape(c.getContext("2d"), opt); c.addEventListener("click", ()=>{ let ok = opt.shape===q.answer.shape && opt.rotation===q.answer.rotation && Math.abs(opt.scale-q.answer.scale)<0.01 && opt.hue===q.answer.hue; if(ok) correctCount++; currentIndex++; if(currentIndex < numQuestions) showQuestion(); else showResult(); }); optionsDiv.appendChild(c); }); } function showResult(){ document.getElementById("testArea").style.display = "none"; let resDiv = document.getElementById("resultArea"); resDiv.style.display = "block"; let successRate = correctCount/numQuestions; let IQ = Math.round(100 + (successRate - expectedSuccess)*(15/(1-expectedSuccess))); IQ = Math.max(70, Math.min(145, IQ)); resDiv.innerHTML = ` <h2>Test Complete</h2> <p>Your score: ${correctCount} / ${numQuestions}</p> <p>Estimated IQ: ${IQ}</p> <button id="retake">Retake Test</button> `; document.getElementById("retake").addEventListener("click", ()=>{ resDiv.style.display = "none"; document.getElementById("difficultySelection").style.display = "block"; }); } // Setup window.onload = function(){ document.getElementById("easyBtn").onclick = ()=>{ difficulty="Easy"; startTest(); }; document.getElementById("medBtn").onclick = ()=>{ difficulty="Medium"; startTest(); }; document.getElementById("hardBtn").onclick = ()=>{ difficulty="Hard"; startTest(); }; }; </script> </body> </html> ```
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>In-Browser IQ Test Generator</title> <style> body{font-family:Arial,Helvetica,sans-serif;background:#f4f4f8;margin:0;display:flex;flex-direction:column;align-items:center} h1{margin-top:20px} #screen{max-width:900px;width:100%;padding:20px;box-sizing:border-box} #matrix{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin:20px 0} #options{display:flex;justify-content:space-around;flex-wrap:wrap} .optBtn{border:2px solid #444;border-radius:4px;background:#fff;cursor:pointer;margin:6px;padding:0} .optBtn.selected{border-color:#1e90ff} #startBtn,#nextBtn,#againBtn{padding:10px 24px;font-size:16px;border:none;border-radius:4px;background:#1e90ff;color:#fff;cursor:pointer} #difficultySel{font-size:16px;padding:6px} #resultBox{font-size:20px;margin:20px 0} canvas{display:block} </style> </head> <body> <h1>Mini Raven-style IQ Test</h1> <div id="screen"> <div id="setup"> <p>Select a difficulty and press Start.</p> <select id="difficultySel"> <option value="1">Easy</option> <option value="2">Medium</option> <option value="3">Hard</option> </select> <button id="startBtn">Start</button> </div> <div id="quiz" style="display:none"> <div id="qCounter"></div> <div id="matrix"></div> <h3>Pick the missing image:</h3> <div id="options"></div> <button id="nextBtn" disabled>Next</button> </div> <div id="result" style="display:none"> <div id="resultBox"></div> <button id="againBtn">Take Again</button> </div> </div> <script> //------------- configuration ------------- const PUZZLES_PER_TEST = 10; const CANVAS_SIZE = 90; //------------- state --------------------- let difficulty=1,currentPuzzle=0,score=0,selectedOpt=-1,puzzles=[]; //------------- helpers ------------------- function randInt(n){return Math.floor(Math.random()*n);} function copy(o){return JSON.parse(JSON.stringify(o));} //------------- drawing ------------------- function drawShape(ctx,attr){ const {shape,color,hue,rotation} = attr; const size = CANVAS_SIZE*0.5; ctx.clearRect(0,0,CANVAS_SIZE,CANVAS_SIZE); ctx.save(); ctx.translate(CANVAS_SIZE/2, CANVAS_SIZE/2); ctx.rotate(rotation*Math.PI/180); ctx.fillStyle = `hsl(${hue},70%,60%)`; ctx.beginPath(); switch(shape){ case 'circle': ctx.arc(0,0,size/1.5,0,Math.PI*2);break; case 'square': ctx.rect(-size/2,-size/2,size,size);break; case 'triangle': ctx.moveTo(0,-size/1.1); ctx.lineTo(size/1.1,size/1.1); ctx.lineTo(-size/1.1,size/1.1); ctx.closePath(); break; case 'pentagon': for(let i=0;i<5;i++){ const ang = (i*72-90)*Math.PI/180; ctx.lineTo(Math.cos(ang)*size/1.2,Math.sin(ang)*size/1.2); } ctx.closePath();break; } ctx.fill(); ctx.restore(); } function canvasToDataURL(attr){ const cv = document.createElement('canvas'); cv.width=cv.height=CANVAS_SIZE; drawShape(cv.getContext('2d'),attr); return cv.toDataURL(); } //------------- puzzle generation ---------- function generatePuzzle(diff){ const shapes = ['circle','square','triangle','pentagon']; // choose which attributes will have systematic transformations const attrsPool = ['shape','hue','rotation']; const usedAttrs = []; while(usedAttrs.length<diff){ let a = attrsPool[randInt(attrsPool.length)]; if(!usedAttrs.includes(a))usedAttrs.push(a); } // base attribute values const base={ shape:shapes[randInt(shapes.length)], hue:randInt(360), rotation: [0,45,90,135,180,225,270,315][randInt(8)] }; // increments const inc={ shape:1+hypersign(), hue:30*(1+randInt(5)), rotation:45 }; function hypersign(){return randInt(2)?1:2;} // grid attributes const grid=[]; for(let r=0;r<3;r++){ for(let c=0;c<3;c++){ if(r===2&&c===2)continue; // missing const a=copy(base); if(usedAttrs.includes('shape')){ const idx=(shapes.indexOf(base.shape)+ r + c)%shapes.length; a.shape=shapes[idx]; } if(usedAttrs.includes('hue')){ a.hue=(base.hue + inc.hue*(r+c))%360; } if(usedAttrs.includes('rotation')){ a.rotation=(base.rotation + inc.rotation*(r*c))%360; } grid.push(a); } } // correct answer attributes (for 2,2 cell) const correct=copy(base); if(usedAttrs.includes('shape')){ correct.shape=shapes[(shapes.indexOf(base.shape)+2+2)%shapes.length]; } if(usedAttrs.includes('hue')){ correct.hue=(base.hue + inc.hue*(4))%360; } if(usedAttrs.includes('rotation')){ correct.rotation=(base.rotation + inc.rotation*(4))%360; } // options const options=[correct]; while(options.length<4){ const d=copy(correct); const changeAttr=usedAttrs[randInt(usedAttrs.length)]; if(changeAttr==='shape'){ d.shape=shapes[randInt(shapes.length)]; }else if(changeAttr==='hue'){ d.hue=(d.hue+inc.hue*(1+randInt(4)))%360; }else if(changeAttr==='rotation'){ d.rotation=(d.rotation+inc.rotation*(1+randInt(3)))%360; } // uniqueness if(!options.some(x=>JSON.stringify(x)===JSON.stringify(d)))options.push(d); } // shuffle options for(let i=options.length-1;i>0;i--){ const j=randInt(i+1); [options[i],options[j]]=[options[j],options[i]]; } const correctIndex=options.findIndex(o=>JSON.stringify(o)===JSON.stringify(correct)); return {grid,options,correctIndex}; } //------------- UI rendering --------------- const matrixDiv = document.getElementById('matrix'); const optionsDiv = document.getElementById('options'); const qCounter = document.getElementById('qCounter'); const nextBtn = document.getElementById('nextBtn'); function renderPuzzle(p){ matrixDiv.innerHTML=''; // draw first 8 cells p.grid.forEach(attr=>{ const img=document.createElement('img'); img.src=canvasToDataURL(attr); img.width=CANVAS_SIZE;img.height=CANVAS_SIZE; matrixDiv.appendChild(img); }); // placeholder for missing cell const place=document.createElement('div'); place.style.width=CANVAS_SIZE+'px'; place.style.height=CANVAS_SIZE+'px'; place.style.border='3px dashed #999'; place.style.display='flex'; place.style.alignItems='center'; place.style.justifyContent='center'; place.textContent='?'; place.style.fontSize='36px'; matrixDiv.appendChild(place); // options optionsDiv.innerHTML=''; p.options.forEach((attr,idx)=>{ const btn=document.createElement('button'); btn.className='optBtn'; const img=document.createElement('img'); img.src=canvasToDataURL(attr); img.width=CANVAS_SIZE;img.height=CANVAS_SIZE; btn.appendChild(img); btn.onclick=()=>{ [...optionsDiv.children].forEach(b=>b.classList.remove('selected')); btn.classList.add('selected'); selectedOpt=idx; nextBtn.disabled=false; }; optionsDiv.appendChild(btn); }); nextBtn.disabled=true; selectedOpt=-1; qCounter.textContent=`Question ${currentPuzzle+1} of ${PUZZLES_PER_TEST}`; } //------------- flow control --------------- function startTest(){ difficulty=parseInt(document.getElementById('difficultySel').value,10); currentPuzzle=0;score=0;puzzles=[]; for(let i=0;i<PUZZLES_PER_TEST;i++)puzzles.push(generatePuzzle(difficulty)); document.getElementById('setup').style.display='none'; document.getElementById('result').style.display='none'; document.getElementById('quiz').style.display=''; renderPuzzle(puzzles[0]); } function nextStep(){ if(selectedOpt===puzzles[currentPuzzle].correctIndex)score++; currentPuzzle++; if(currentPuzzle<PUZZLES_PER_TEST){ renderPuzzle(puzzles[currentPuzzle]); }else{ endTest(); } } function endTest(){ document.getElementById('quiz').style.display='none'; const pct=score/PUZZLES_PER_TEST; const estIQ = Math.round(80+ pct*40); // crude estimate 80-120 document.getElementById('resultBox').innerHTML= `You answered ${score} / ${PUZZLES_PER_TEST} correctly.<br> Estimated IQ: <strong>${estIQ}</strong> (±10).<br> This is an informal approximation, not a clinical measurement.`; document.getElementById('result').style.display=''; } //------------- hooks ---------------------- document.getElementById('startBtn').onclick=startTest; nextBtn.onclick=nextStep; document.getElementById('againBtn').onclick=()=>{document.getElementById('setup').style.display='';document.getElementById('result').style.display='none';}; </script> </body> </html> ```
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>IQ Test Generator</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <link href="https://fonts.googleapis.com/css?family=Roboto:400,700" rel="stylesheet"> <style> body { font-family: 'Roboto', sans-serif; background: #f7f9fa; color: #222; margin: 0; padding: 0; min-height: 100vh; } .container { max-width: 600px; margin: 30px auto; background: #fff; border-radius: 16px; box-shadow: 0 6px 24px rgba(60,90,120,0.10), 0 1.5px 6px rgba(0,0,0,0.03); padding: 24px 20px 30px 20px; } h1 { font-size: 2em; font-weight: 700; margin-bottom: 0.35em; letter-spacing: -1px; } .settings, .stats, .question, .final-score { margin-top: 25px; text-align: center; } .difficulty-btn { font-size: 1em; padding: 10px 30px; margin: 12px 8px; border: none; border-radius: 8px; background: #e3eaf1; color: #283848; font-weight: 600; cursor: pointer; transition: background 0.18s; } .difficulty-btn.selected, .difficulty-btn:hover { background: #54b6fa; color: #fff; } .matrix-container { display: grid; gap: 10px; grid-template-columns: repeat(3, 64px); justify-content: center; margin: 0 auto 22px auto; background: #e8edf4; border-radius: 12px; padding: 18px 14px 7px 14px; min-width: 230px; } .matrix-cell { background: #fff; border-radius: 8px; border: 1px solid #e3e8ed; width: 64px; height: 64px; display: flex; align-items: center; justify-content: center; position: relative; } .matrix-cell.missing { background: #e0e0e0; border: 2px dashed #a0a0a0; } .choices { display: grid; grid-template-columns: repeat(2, 110px); gap: 14px; justify-content: center; align-items: center; margin-bottom: 8px; } .choice-btn { background: #f8fafb; border: 1.5px solid #bcd1e6; border-radius: 11px; padding: 5px 3px 2px 3px; cursor: pointer; transition: border 0.14s, box-shadow 0.14s; min-width: 110px; min-height: 77px; display: flex; align-items: center; justify-content: center; } .choice-btn.selected, .choice-btn:hover { border: 2.5px solid #47a0ea; background: #e6f1fb; box-shadow: 0 0 8px #74bffe40; } .stats { font-size: 1.04em; color: #55698e; margin-top: 3px; margin-bottom: 9px; } .submit-btn, .restart-btn { margin-top: 16px; padding: 12px 38px; background: #36b37e; color: #fff; font-size: 1.09em; font-weight: 700; border: none; border-radius: 12px; cursor: pointer; transition: background 0.13s; box-shadow: 0 1.5px 6px #36b37e20; } .submit-btn:disabled { background: #b9e6d2; color: #97b6ac; cursor: default; } .restart-btn { background: #2590d1; margin-left: 8px; } .final-score { font-size: 1.23em; } .final-score .iq { font-weight: 700; font-size: 1.9em; color: #36b37e; letter-spacing: 0.5px; } @media (max-width: 700px) { .container { max-width: 100%; } .matrix-container { min-width: 0; } } @media (max-width: 550px) { .matrix-container {grid-template-columns: repeat(3, 48px);} .matrix-cell { width: 48px; height: 48px;} .choices {grid-template-columns: 1fr 1fr; gap: 7px;} .choice-btn { min-width: 60px; min-height: 45px; padding: 3px 1px;} } </style> </head> <body> <div class="container"> <h1>IQ Test Generator</h1> <div class="settings" id="settings"> <div>Select difficulty:</div> <button class="difficulty-btn" data-diff="easy">Easy</button> <button class="difficulty-btn" data-diff="medium">Medium</button> <button class="difficulty-btn" data-diff="hard">Hard</button> <div style="margin-top:14px; color:#6c7fa2; font-size:0.97em;">Each test has 10 3×3 pattern tasks</div> </div> <div class="question" id="question" style="display:none"></div> <div class="final-score" id="final" style="display:none"></div> </div> <script> const DIFFICULTY_SETTINGS = { easy: { transforms: ['rotate', 'size', 'count', 'shape'], rounds: 10, timePerQ: 36, // seconds distractorDeviation: 1, }, medium: { transforms: ['rotate', 'size', 'count', 'shape', 'flip', 'fill'], rounds: 10, timePerQ: 43, distractorDeviation: 1.5, }, hard: { transforms: ['rotate', 'size', 'count', 'shape', 'flip', 'fill', 'arithmetic', 'composition'], rounds: 10, timePerQ: 59, distractorDeviation: 2.2, } }; const SHAPES = ['circle', 'square', 'triangle', 'diamond', 'pentagon']; const FILLS = ['solid', 'hollow']; const COLORS = ['#3590fa', '#f23a3c', '#f2ad22', '#24b363', '#b05fd9', '#000']; let userData = { difficulty: 'easy', questions: [], responses: [], IQ: 100, startTime: 0 }; const $ = (id) => document.getElementById(id); // =================== Settings UI logic ==================== const settingsDiv = $('settings'); Array.from(settingsDiv.querySelectorAll('.difficulty-btn')).forEach(btn => { btn.onclick = (e) => { Array.from(settingsDiv.querySelectorAll('.difficulty-btn')).forEach(b => b.classList.remove('selected')); btn.classList.add('selected'); userData.difficulty = btn.dataset.diff; setTimeout(startTest, 350); } }); // =========== Canv Generation Helpers ======================= function getRandomItem(arr) { return arr[Math.floor(Math.random() * arr.length)]; } function randInt(n, m) { return Math.floor(Math.random() * (m - n + 1)) + n; } function shuffle(arr) { for(let i = arr.length-1; i>0; --i) { let j = randInt(0, i); [arr[i], arr[j]] = [arr[j], arr[i]]; } } function deepCopy(obj) { // Safe for our params return JSON.parse(JSON.stringify(obj)); } // ============= Single shape draw =========================== function drawShape(ctx, shape, x, y, r, fillType, color, rotation=0) { ctx.save(); ctx.translate(x, y); ctx.rotate(rotation*Math.PI/180); ctx.beginPath(); ctx.strokeStyle = color; ctx.fillStyle = color; ctx.lineWidth = 3; // All shapes fit in a max radius = r. switch(shape) { case 'circle': ctx.arc(0,0,r,0,2*Math.PI); break; case 'square': ctx.moveTo(-r,-r); ctx.lineTo(r,-r); ctx.lineTo(r,r); ctx.lineTo(-r,r); ctx.closePath(); break; case 'triangle': let h = r*Math.sqrt(3); ctx.moveTo(0,-r); ctx.lineTo(h/2, r/2); ctx.lineTo(-h/2, r/2); ctx.closePath(); break; case 'diamond': ctx.moveTo(0,-r); ctx.lineTo(r,0); ctx.lineTo(0,r); ctx.lineTo(-r,0); ctx.closePath(); break; case 'pentagon': for(let i=0;i<5;++i){ let angle = Math.PI/2 + i*2*Math.PI/5; let xi = Math.cos(angle)*r; let yi = Math.sin(angle)*r; (i===0) ? ctx.moveTo(xi,yi) : ctx.lineTo(xi,yi); } ctx.closePath(); break; } if (fillType === 'solid') ctx.fill(); else ctx.stroke(); ctx.restore(); } // ================ Compose a cell graphic ==================== function cellToCanvas({ shape = 'circle', size = 18, count = 1, orientation = 0, fill = 'solid', color = '#3590fa', flipH = false, extra = undefined // for composite }, W=64, H=64) { let canvas = document.createElement('canvas'); canvas.width = W; canvas.height = H; let ctx = canvas.getContext('2d'); // Multiple count = radial arrangement let angleInc = (360/count), angle0 = -90; for (let i=0;i<count;i++) { let a = angle0 + i*angleInc; ctx.save(); ctx.translate(W/2, H/2); if (flipH) ctx.scale(-1,1); ctx.rotate((orientation+0)/180*Math.PI); ctx.rotate(a*Math.PI/180); ctx.translate(0, -((count > 1) ? size+6 : 0)); if (extra) { // composite extra.forEach(spec=>drawShape(ctx, ...spec)); } else { drawShape(ctx, shape, 0, 0, size, fill, color); } ctx.restore(); } return canvas; } // ============== Pattern Generation Logic ===================== function generatePattern(transformList, difficulty) { // Select one or two transforms based on difficulty let nTransforms = (difficulty==='easy'? 1 : (difficulty==='medium'?randInt(1,2):randInt(2,2))); shuffle(transformList); let chosen = transformList.slice(0, nTransforms); // Choose parameters for each // Starting state let start = { shape: getRandomItem(SHAPES), size: randInt(10,19), count: randInt(1, (difficulty==='easy'?2: (difficulty==='medium'?3:4))), orientation: [0,90,180,270][randInt(0,3)], fill: getRandomItem(FILLS), color: getRandomItem(COLORS), flipH: false }; // For 'composition', base shape, then add+ let composite = false; let compSpec = null; // Build grid with rules let grid = Array.from({length:3}, ()=> Array(3).fill(null)); // Determine step for each transform let tfParams = {}; chosen.forEach(tf => { switch(tf) { case 'rotate': tfParams.rotate = [ [0, randInt(30, 90)], [0, randInt(30,90)] ]; break; case 'size': tfParams.size = [ [0, randInt(3,8)], [0, randInt(2,9)] ]; break; case 'count': tfParams.count = [ [0, randInt(1,2)], [0, randInt(1,2)] ]; break; case 'shape': { let shapeIdx = SHAPES.indexOf(start.shape); tfParams.shape = [ [0, 1], [0, 1] ]; } break; case 'fill': tfParams.fill = [ [null, getRandomItem(FILLS)], [null, getRandomItem(FILLS)] ]; break; case 'flip': tfParams.flipH = [ [false, true], [false, Math.random() > 0.5]]; break; case 'arithmetic': tfParams.size = [ [0, randInt(2,4)], [0, randInt(2,4)] ]; // changes in both axis tfParams.count = [ [0, randInt(1,2)], [0, randInt(1,2)] ]; break; case 'composition': composite = true; compSpec = [ [ getRandomItem(SHAPES), 0, 0, randInt(13,19), 'solid', getRandomItem(COLORS), 0 ], [ getRandomItem(SHAPES), 0, 0, randInt(7,13), 'hollow', getRandomItem(COLORS), 0 ], ]; break; } }); // Compose cells let tmpShapes = SHAPES.slice(); let f = (i,j) => { let cell = deepCopy(start); // Apply transform for first transform chosen.forEach(tf => { let param = tfParams[tf]; switch(tf) { case 'rotate': cell.orientation = start.orientation + i*param[0][1] + j*param[1][1]; break; case 'size': cell.size = Math.max(7, start.size + i*param[0][1] + j*param[1][1]); break; case 'count': cell.count = Math.max(1, start.count + i*param[0][1] + j*param[1][1]); break; case 'shape': let shapeIdx = SHAPES.indexOf(start.shape); cell.shape = SHAPES[(shapeIdx + i*param[0][1] + j*param[1][1])%SHAPES.length]; break; case 'fill': if (i+j == 2) cell.fill = param[1][1]; // For only one break; case 'flip': if (i+j >= 2) cell.flipH = param[1][1]; break; case 'arithmetic': cell.size = Math.max(8, start.size + i*param[0][1] - j*param[1][1]); cell.count = Math.max(1, start.count + i*param[0][1] + j*param[1][1]); break; case 'composition': cell.extra = compSpec; break; } }); return cell; } // Build grid for(let i=0;i<3;i++) for(let j=0;j<3;j++) grid[i][j]=f(i,j); // Set missing slot let missing = {i:2, j:2, cell:grid[2][2]}; grid[2][2] = null; // Generate distractors let distractors = []; let nChoices = 4; let correctIdx = randInt(0, nChoices-1); for (let d=0; d<nChoices; ++d) { if (d === correctIdx) { distractors.push(deepCopy(missing.cell)); } else { // Mutation from correct cell let dist = DIFFICULTY_SETTINGS[difficulty].distractorDeviation; let wrong = deepCopy(missing.cell); let option = getRandomItem(chosen); switch(option) { case 'rotate': wrong.orientation = (wrong.orientation + randInt(1,3)*30*dist)%360; break; case 'size': wrong.size = Math.max(7, wrong.size + randInt(-2,2)*dist); break; case 'count': wrong.count = Math.max(1, wrong.count + randInt(-1,2)*dist); break; case 'shape': let idx = SHAPES.indexOf(wrong.shape); wrong.shape = SHAPES[(idx + randInt(1, SHAPES.length-1))%SHAPES.length]; break; case 'fill': wrong.fill = wrong.fill==='solid'?'hollow':'solid'; break; case 'flip': wrong.flipH = !wrong.flipH; break; case 'arithmetic': (Math.random()>0.5) ? (wrong.size = Math.max(9, wrong.size+randInt(-3,5))) : (wrong.count = Math.max(1, wrong.count+randInt(-1,2)) ); break; case 'composition': // Swap order if (wrong.extra) [wrong.extra[0],wrong.extra[1]] = [wrong.extra[1], wrong.extra[0]]; break; } distractors.push(wrong) } } // Shuffle choices shuffle(distractors); // Solution idx let solutionIdx = distractors.findIndex(d=>(JSON.stringify(d)===JSON.stringify(missing.cell))); let pattern = { grid, missing, distractors, chosen, solutionIdx }; return pattern; } // ========== UI: Render Pattern as 3x3 Canvas grid + choices ======== function renderPattern(pattern, qidx, totalQ, selected = null, disabled=true) { let out = `<div class="stats">Task ${qidx+1} of ${totalQ}</div> <div class="matrix-container">`; for(let i=0;i<3;i++) for(let j=0;j<3;j++) { if(i===2&&j===2){ out += `<div class="matrix-cell missing"></div>`; } else { out += `<div class="matrix-cell"> <div style="text-align:center;">${canvasToImgTag(cellToCanvas(pattern.grid[i][j]))}</div> </div>`; } } out += '</div>'; // Choices out += `<div style="font-size:1.10em;font-weight:600;margin-bottom:6px;">Select the missing image:</div> <div class="choices">`; pattern.distractors.forEach((cell, idx)=>{ let sel = (selected===idx)?'selected':''; out += `<button class="choice-btn ${sel}" data-choice="${idx}" ${disabled? '':'tabindex="0"'}>${canvasToImgTag(cellToCanvas(cell, 66,66))}</button>` }); out += `</div>`; return out; } function canvasToImgTag(canvas) { return `<img src="${canvas.toDataURL('image/png')}" width="${canvas.width}" height="${canvas.height}" alt="option" draggable="false" style="display:block;max-width:100%;margin:auto;">`; } // ==================== Test Logic ============================ function startTest() { // Setup settingsDiv.style.display = 'none'; userData.questions = []; userData.responses = []; userData.startTime = performance.now(); let difficulty = userData.difficulty; let rounds = DIFFICULTY_SETTINGS[difficulty].rounds; let tfList = DIFFICULTY_SETTINGS[difficulty].transforms; // Generate all patterns in advance: userData.questions = []; for(let i=0; i<rounds; ++i){ userData.questions.push(generatePattern(tfList, difficulty)); } nextQuestion(0); } function nextQuestion(qidx) { const totalQ = userData.questions.length; if (qidx >= totalQ) { finishTest(); return; } let qdiv = $('question'); qdiv.style.display = ''; let pattern = userData.questions[qidx]; qdiv.innerHTML = renderPattern(pattern, qidx, totalQ, null, false) + `<button class="submit-btn" id="submitbtn" disabled>Submit</button>`; // Option logic let selected = null; let choices = Array.from(qdiv.querySelectorAll('.choice-btn')); choices.forEach(btn => { btn.onclick = e => { selected = parseInt(btn.dataset.choice); choices.forEach(b => b.classList.remove('selected')); btn.classList.add('selected'); $('submitbtn').disabled = false; } }); $('submitbtn').onclick = () => { // Save response time and correctness let res = { q: qidx, correct: (selected === pattern.solutionIdx), selected, solutionIdx: pattern.solutionIdx, t: Date.now() }; userData.responses.push(res); // Show answer feedback let qdiv2 = $('question'); qdiv2.innerHTML = renderPattern(pattern, qidx, totalQ, selected, true) + `<button class="submit-btn" id="nextbtn">Next</button>`; let choiceEls = qdiv2.querySelectorAll('.choice-btn'); choiceEls[pattern.solutionIdx].style.borderColor = '#36b37e'; choiceEls[pattern.solutionIdx].style.background = '#e7faee'; if (selected !== pattern.solutionIdx) { choiceEls[selected].style.borderColor = '#f86353'; choiceEls[selected].style.background = '#fbe6e4'; } qdiv2.querySelector('#nextbtn').onclick = () => nextQuestion(qidx+1); }; } // =========== IQ scoring (approximate, not clinical!) ========= function finishTest() { $('question').style.display = 'none'; // Estimate score let total = userData.questions.length; let correct = userData.responses.filter(r=>r.correct).length; let tScores = []; for(let i=0; i<userData.responses.length; ++i) { let s = userData.responses[i], prev = i===0?userData.startTime: userData.responses[i-1].t; tScores.push( Math.max(1, (s.t-prev)/1000) ); } // Basic model: for this kind of task, ~70% correct is "average", // boost for hard mode, penalty for time let diff = userData.difficulty; let norm = (diff==='easy'?0.7:(diff==='medium'?0.54:0.39)); let acc = correct/total; let avgTime = tScores.length ? (tScores.reduce((a,b)=>a+b,0)/tScores.length) : DIFFICULTY_SETTINGS[diff].timePerQ; // IQ = 100 + (acc-norm)/0.08*15 + (bonus for hard) - (penalty for slow resp) let bonus = (diff==='easy'?0:(diff==='medium'?2:6)); let penalty = 0; if (avgTime > DIFFICULTY_SETTINGS[diff].timePerQ*0.95) penalty = (avgTime-DIFFICULTY_SETTINGS[diff].timePerQ)*3.8; let estIQ = Math.round(100 + (acc-norm)/0.09*14 + bonus - penalty); if (estIQ > 145) estIQ = randInt(140,147); if (estIQ < 65) estIQ = randInt(64,70); userData.IQ = estIQ; // Feedback let txt = `<div style="margin-bottom:16px;color:#3572a7;">Test complete!</div> <div style="font-size:1.21em;">Accuracy: <b>${correct}/${total}</b> correct<br> Avg. time per task: <b>${Math.round(avgTime*10)/10}s</b></div> <div style="margin:25px 0 9px 0;"> <span>Your estimated IQ:</span> <div class="iq">${estIQ}</div> <div style="font-size:0.97em;color:#868c9b;margin-top:6px;"> (Range: 100 = average, 115 = 1SD above average, 130 = 2SD above average) </div> </div> <button class="restart-btn" onclick="window.location.reload()">Restart Test</button> `; $('final').style.display = ''; $('final').innerHTML = txt; } </script> </body> </html> ```
```html <!DOCTYPE html> <html> <head> <title>Browser-Based IQ Test Generator</title> <style> body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; } #grid { display: grid; grid-template-columns: repeat(3, 60px); grid-gap: 10px; margin: 20px auto 0; } #grid > div { width: 60px; height: 60px; display: flex; align-items: center; justify-content: center; border: 1px solid #ccc; background: #f8f8f8; } canvas { image-rendering: auto; } #options { display: flex; flex-wrap: wrap; gap: 10px; margin: 20px 0; } .option-btn { padding: 0; border: 2px solid #ccc; background: none; cursor: pointer; } .option-btn:hover { border-color: #888; } .option-btn.correct { border-color: green; } .option-btn.incorrect { border-color: red; } </style> </head> <body> <div id="setup"> <h2>IQ Test Generator</h2> <p>Select difficulty level and start the test:</p> <label for="difficulty">Difficulty: </label> <select id="difficulty"> <option value="easy">Easy</option> <option value="medium">Medium</option> <option value="hard">Hard</option> </select> <button onclick="startTest()" style="margin-left: 10px;">Start</button> </div> <div id="test" style="display:none;"> <h2>Question <span id="questionNumber"></span></h2> <div id="grid"></div> <div id="options"></div> <button onclick="skipToResults()" id="skipButton" style="margin-top:15px;">Skip to Results</button> </div> <div id="results" style="display:none;"> <h2>Test Results</h2> <p>Your estimated IQ score is <strong id="iqScore"></strong>.</p> <p>This is calculated based on number of correct answers and relative difficulty level.</p> <p><small>(This is a simplified IQ simulation. Actual IQ tests are far more complex and standardized.)</small></p> <button onclick="location.reload()">Restart</button> </div> <script> let currentQuestionIndex = 0; let totalQuestions = 6; let correctCount = 0; let difficulty; let questions = []; // Helper functions function statesEqual(a, b) { return a.shape === b.shape && a.rotation === b.rotation && a.fill === b.fill; } // State transformations function rotateRight(state) { return {...state, rotation: (state.rotation + 90) % 360}; } function flipColor(state) { return {...state, fill: state.fill === 'black' ? 'white' : 'black'}; } function nextShape(state) { const shapes = ['circle', 'square', 'triangle']; const idx = (shapes.indexOf(state.shape) + 1) % 3; return {...state, shape: shapes[idx]}; } function generateInitialState() { const shapes = ['circle', 'square', 'triangle']; return { shape: shapes[Math.floor(Math.random() * 3)], rotation: Math.floor(Math.random() * 4) * 90, fill: Math.random() > 0.5 ? 'black' : 'white' }; } function getRuleFunction(difficulty) { switch (difficulty) { case 'easy': const easyOpt = [rotateRight, flipColor, nextShape]; return easyOpt[Math.floor(Math.random() * 3)]; case 'medium': const mediumOpt = [ (s) => rotateRight(flipColor(s)), (s) => nextShape(rotateRight(s)), (s) => flipColor(rotateRight(s)) ]; return mediumOpt[Math.floor(Math.random() * 3)]; case 'hard': return (state, i) => { if (i % 2 === 0) return rotateRight(state); return i % 3 === 1 ? nextShape(state) : flipColor(state); }; default: return rotateRight; } } function generateStateSequence(ruleFunction, length = 9) { let states = [generateInitialState()]; for (let i = 1; i < length; i++) { states.push(ruleFunction(states[i-1], i)); } return states; } function generateDistractors(correctState, count = 3) { const distractors = []; while (distractors.length < count) { let variation = {...correctState}; const method = Math.floor(Math.random() * 3); if (method === 0) { // Change shape const shapeOpt = ['circle', 'square', 'triangle'].filter(s => s !== variation.shape); variation.shape = shapeOpt[Math.floor(Math.random() * 2)]; } else if (method === 1) { // Change rotation const rotationOpt = [0, 90, 180, 270].filter(r => r !== variation.rotation); variation.rotation = rotationOpt[Math.floor(Math.random() * 3)]; } else { // Change color variation.fill = variation.fill === 'black' ? 'white' : 'black'; } // Ensure uniqueness if (!distractors.some(d => statesEqual(d, variation)) && !statesEqual(variation, correctState)) { distractors.push(variation); } } return distractors; } // Canvas rendering function renderStateToCanvas(canvas, state) { const ctx = canvas.getContext('2d'); const w = canvas.width; const h = canvas.height; // Clear canvas ctx.clearRect(0, 0, w, h); // Save context ctx.save(); ctx.translate(w/2, h/2); ctx.rotate(state.rotation * Math.PI / 180); // Set fill color ctx.fillStyle = state.fill; // Draw shape switch (state.shape) { case 'circle': ctx.beginPath(); ctx.arc(0, 0, w/3, 0, 2*Math.PI); ctx.fill(); break; case 'square': ctx.fillRect(-w/3, -w/3, w*2/3, w*2/3); break; case 'triangle': ctx.beginPath(); ctx.moveTo(0, -h/3); ctx.lineTo(h/3, h/3); ctx.lineTo(-h/3, h/3); ctx.closePath(); ctx.fill(); break; } // Restore context ctx.restore(); } // Game functions function startTest() { difficulty = document.getElementById('difficulty').value; document.getElementById('setup').style.display = 'none'; document.getElementById('test').style.display = 'block'; currentQuestionIndex = 0; correctCount = 0; questions = []; showQuestion(); } function showQuestion() { document.getElementById('questionNumber').textContent = currentQuestionIndex + 1; // Generate question const rule = getRuleFunction(difficulty); const fullSequence = generateStateSequence(rule, 9); const correctState = fullSequence[8]; // Create question const question = { gridStates: fullSequence.slice(0, 8), options: [correctState, ...generateDistractors(correctState)], correctState: correctState }; // Shuffle options question.options.sort(() => Math.random() - 0.5); questions.push(question); renderQuestion(question); } function renderQuestion(question) { const grid = document.getElementById('grid'); grid.innerHTML = ''; // Add grid cells for (let i = 0; i < 9; i++) { const cell = document.createElement('div'); if (i < question.gridStates.length) { const canvas = document.createElement('canvas'); canvas.width = 50; canvas.height = 50; renderStateToCanvas(canvas, question.gridStates[i]); cell.appendChild(canvas); } else { cell.textContent = '•••'; } grid.appendChild(cell); } // Render options const options = document.getElementById('options'); options.innerHTML = ''; question.options.forEach((state, index) => { const btn = document.createElement('button'); btn.className = 'option-btn'; btn.innerHTML = '<canvas width="40" height="40"></canvas>'; // Render to button canvas const canvas = btn.querySelector('canvas'); renderStateToCanvas(canvas, state); btn.onclick = () => { // Check answer const isCorrect = statesEqual(state, question.correctState); // Add class for visual feedback btn.classList.add(isCorrect ? 'correct' : 'incorrect'); // Update count if (isCorrect) correctCount++; // Proceed to next question setTimeout(() => { currentQuestionIndex++; if (currentQuestionIndex < totalQuestions) { showQuestion(); } else { showResults(); } }, 500); }; options.appendChild(btn); }); } function showResults() { document.getElementById('test').style.display = 'none'; document.getElementById('results').style.display = 'block'; // Simple IQ estimation based on difficulty let iq; const percentage = correctCount / totalQuestions; switch (difficulty) { case 'easy': iq = 60 + 40 * percentage; break; case 'medium': iq = 80 + 40 * percentage; break; case 'hard': iq = 85 + 50 * percentage; break; } document.getElementById('iqScore').textContent = Math.round(iq); } function skipToResults() { currentQuestionIndex++; if (currentQuestionIndex < totalQuestions) { showQuestion(); } else { showResults(); } } </script> </body> </html> ```
Here's a complete HTML implementation of an IQ test generator that meets your requirements. It creates 3×3 sequence-completion tasks with mathematically generated patterns, offers three difficulty levels, tracks responses, and provides an IQ estimate. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Pattern IQ Test</title> <style> body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background-color: #f5f5f5; } .container { background-color: white; padding: 20px; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.1); } .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin: 20px 0; } .cell { width: 100%; aspect-ratio: 1; border: 1px solid #ddd; display: flex; justify-content: center; align-items: center; background-color: white; } .options { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin: 20px 0; } .option { aspect-ratio: 1; border: 2px solid #ddd; cursor: pointer; transition: all 0.2s; } .option:hover { border-color: #4CAF50; } .selected { border-color: #4CAF50; background-color: #e8f5e9; } button { background-color: #4CAF50; color: white; border: none; padding: 10px 20px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 10px 2px; cursor: pointer; border-radius: 5px; } button:hover { background-color: #45a049; } .difficulty-selector { margin: 20px 0; } .result { font-size: 18px; font-weight: bold; margin: 20px 0; padding: 10px; background-color: #e8f5e9; border-radius: 5px; display: none; } .progress { margin: 20px 0; height: 10px; background-color: #e0e0e0; border-radius: 5px; } .progress-bar { height: 100%; background-color: #4CAF50; border-radius: 5px; width: 0%; transition: width 0.3s; } </style> </head> <body> <div class="container"> <h1>Pattern IQ Test</h1> <div class="difficulty-selector"> <h3>Select Difficulty:</h3> <button id="easy">Easy</button> <button id="medium">Medium</button> <button id="hard">Hard</button> </div> <div id="test-area" style="display: none;"> <h3>Complete the pattern:</h3> <div class="grid" id="pattern-grid"></div> <h3>Select the correct option:</h3> <div class="options" id="options-grid"></div> <button id="submit">Submit Answer</button> <button id="next" style="display: none;">Next Question</button> <div class="progress"> <div class="progress-bar" id="progress-bar"></div> </div> </div> <div class="result" id="result"></div> </div> <script> // Configuration const TEST_LENGTH = 10; const DIFFICULTIES = { easy: { complexity: 1, transformations: 1, patterns: 1 }, medium: { complexity: 2, transformations: 2, patterns: 2 }, hard: { complexity: 3, transformations: 3, patterns: 3 } }; // State variables let currentDifficulty = null; let currentQuestion = 0; let score = 0; let questions = []; let selectedOption = null; // DOM elements const patternGrid = document.getElementById('pattern-grid'); const optionsGrid = document.getElementById('options-grid'); const submitBtn = document.getElementById('submit'); const nextBtn = document.getElementById('next'); const resultDiv = document.getElementById('result'); const progressBar = document.getElementById('progress-bar'); const testArea = document.getElementById('test-area'); // Difficulty buttons document.getElementById('easy').addEventListener('click', () => startTest('easy')); document.getElementById('medium').addEventListener('click', () => startTest('medium')); document.getElementById('hard').addEventListener('click', () => startTest('hard')); // Test buttons submitBtn.addEventListener('click', checkAnswer); nextBtn.addEventListener('click', nextQuestion); // Pattern generation functions function generatePattern(difficulty) { const config = DIFFICULTIES[difficulty]; const patternType = Math.floor(Math.random() * config.patterns) + 1; // Base pattern (first cell) const baseSize = 0.3 + Math.random() * 0.4; const baseRotation = Math.floor(Math.random() * 4) * 90; const baseColor = `hsl(${Math.floor(Math.random() * 360)}, 70%, 60%)`; // Generate transformations const transformations = []; for (let i = 0; i < config.transformations; i++) { transformations.push({ type: Math.floor(Math.random() * 4) + 1, axis: Math.floor(Math.random() * 2), amount: 0.1 + Math.random() * 0.3 }); } // Generate the 3x3 pattern const pattern = []; for (let row = 0; row < 3; row++) { for (let col = 0; col < 3; col++) { if (row === 2 && col === 2) { pattern.push(null); // This will be the missing piece } else { pattern.push(generateCell(row, col, baseSize, baseRotation, baseColor, transformations, patternType)); } } } // Generate options (including the correct one) const correctOption = generateCell(2, 2, baseSize, baseRotation, baseColor, transformations, patternType); const options = [correctOption]; // Generate incorrect options while (options.length < 6) { // Vary one parameter to create plausible but incorrect options const variationType = Math.floor(Math.random() * 4); let newOption; switch (variationType) { case 0: // Different size newOption = generateCell(2, 2, baseSize * (0.7 + Math.random() * 0.6), baseRotation, baseColor, transformations, patternType); break; case 1: // Different rotation newOption = generateCell(2, 2, baseSize, (baseRotation + (Math.floor(Math.random() * 3) + 1) * 90) % 360, baseColor, transformations, patternType); break; case 2: // Different color newOption = generateCell(2, 2, baseSize, baseRotation, `hsl(${(parseInt(baseColor.match(/\d+/)[0]) + 30 + Math.floor(Math.random() * 60)) % 360}, 70%, 60%)`, transformations, patternType); break; case 3: // Different transformation const newTransformations = JSON.parse(JSON.stringify(transformations)); const changeIndex = Math.floor(Math.random() * newTransformations.length); newTransformations[changeIndex].amount *= (0.5 + Math.random()); newOption = generateCell(2, 2, baseSize, baseRotation, baseColor, newTransformations, patternType); break; } // Check if this option is sufficiently different from existing ones if (!options.some(opt => arePatternsSimilar(opt, newOption))) { options.push(newOption); } } // Shuffle options shuffleArray(options); return { pattern: pattern, options: options, correctIndex: options.indexOf(correctOption) }; } function generateCell(row, col, baseSize, baseRotation, baseColor, transformations, patternType) { // Apply transformations based on position let size = baseSize; let rotation = baseRotation; let color = baseColor; transformations.forEach(trans => { const positionValue = trans.axis === 0 ? row : col; switch (trans.type) { case 1: // Size change size += trans.amount * positionValue; break; case 2: // Rotation change rotation += trans.amount * 90 * positionValue; break; case 3: // Color hue change const hue = parseInt(color.match(/\d+/)[0]); color = `hsl(${(hue + trans.amount * 30 * positionValue) % 360}, 70%, 60%)`; break; case 4: // Pattern complexity change // This affects how we draw the pattern later break; } }); return { size: Math.max(0.1, Math.min(0.9, size)), rotation: rotation % 360, color: color, patternType: patternType, transformations: transformations }; } function arePatternsSimilar(a, b) { // Simple similarity check const sizeDiff = Math.abs(a.size - b.size); const rotationDiff = Math.min(Math.abs(a.rotation - b.rotation), 360 - Math.abs(a.rotation - b.rotation)); const colorDiff = Math.abs(parseInt(a.color.match(/\d+/)[0]) - parseInt(b.color.match(/\d+/)[0])); return sizeDiff < 0.1 && rotationDiff < 30 && colorDiff < 30; } function drawPattern(cell, canvas) { const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); const centerX = canvas.width / 2; const centerY = canvas.height / 2; const size = Math.min(canvas.width, canvas.height) * cell.size; ctx.save(); ctx.translate(centerX, centerY); ctx.rotate(cell.rotation * Math.PI / 180); switch (cell.patternType) { case 1: // Simple geometric shapes ctx.fillStyle = cell.color; ctx.beginPath(); ctx.arc(0, 0, size/2, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = 'black'; ctx.lineWidth = 1; ctx.stroke(); break; case 2: // More complex pattern ctx.fillStyle = cell.color; for (let i = 0; i < 4; i++) { ctx.rotate(Math.PI/2); ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(size/2, 0); ctx.lineTo(size/2, size/2); ctx.closePath(); ctx.fill(); ctx.stroke(); } break; case 3: // Most complex pattern ctx.fillStyle = cell.color; const segments = 5; for (let i = 0; i < segments; i++) { const angle1 = (i * 2 * Math.PI) / segments; const angle2 = ((i + 1) * 2 * Math.PI) / segments; ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(Math.cos(angle1) * size/2, Math.sin(angle1) * size/2); ctx.lineTo(Math.cos(angle2) * size/2, Math.sin(angle2) * size/2); ctx.closePath(); ctx.fill(); ctx.stroke(); } break; } ctx.restore(); } // Test flow functions function startTest(difficulty) { currentDifficulty = difficulty; currentQuestion = 0; score = 0; questions = []; // Generate all questions upfront for (let i = 0; i < TEST_LENGTH; i++) { questions.push(generatePattern(difficulty)); } // Show test area document.querySelector('.difficulty-selector').style.display = 'none'; testArea.style.display = 'block'; // Display first question displayQuestion(); } function displayQuestion() { // Update progress progressBar.style.width = `${(currentQuestion / TEST_LENGTH) * 100}%`; // Clear previous patternGrid.innerHTML = ''; optionsGrid.innerHTML = ''; selectedOption = null; nextBtn.style.display = 'none'; submitBtn.style.display = 'inline-block'; const question = questions[currentQuestion]; // Display pattern question.pattern.forEach((cell, index) => { const cellDiv = document.createElement('div'); cellDiv.className = 'cell'; if (cell !== null) { const canvas = document.createElement('canvas'); canvas.width = 100; canvas.height = 100; drawPattern(cell, canvas); cellDiv.appendChild(canvas); } else { cellDiv.textContent = '?'; } patternGrid.appendChild(cellDiv); }); // Display options question.options.forEach((option, index) => { const optionDiv = document.createElement('div'); optionDiv.className = 'option'; optionDiv.dataset.index = index; const canvas = document.createElement('canvas'); canvas.width = 100; canvas.height = 100; drawPattern(option, canvas); optionDiv.appendChild(canvas); optionDiv.addEventListener('click', () => { // Deselect all document.querySelectorAll('.option').forEach(opt => { opt.classList.remove('selected'); }); // Select this one optionDiv.classList.add('selected'); selectedOption = index; }); optionsGrid.appendChild(optionDiv); }); } function checkAnswer() { if (selectedOption === null) { alert('Please select an answer'); return; } const question = questions[currentQuestion]; const isCorrect = selectedOption === question.correctIndex; if (isCorrect) { score++; } // Show feedback document.querySelectorAll('.option').forEach((opt, index) => { if (index === question.correctIndex) { opt.style.borderColor = '#4CAF50'; opt.style.boxShadow = '0 0 5px #4CAF50'; } else if (index === selectedOption && !isCorrect) { opt.style.borderColor = '#f44336'; opt.style.boxShadow = '0 0 5px #f44336'; } }); // Prepare for next question submitBtn.style.display = 'none'; nextBtn.style.display = 'inline-block'; } function nextQuestion() { currentQuestion++; if (currentQuestion < TEST_LENGTH) { displayQuestion(); } else { showResults(); } } function showResults() { testArea.style.display = 'none'; // Calculate IQ estimate based on score and difficulty let iqEstimate; const percentage = score / TEST_LENGTH; if (currentDifficulty === 'easy') { iqEstimate = 85 + percentage * 30; // 85-115 } else if (currentDifficulty === 'medium') { iqEstimate = 100 + percentage * 30; // 100-130 } else { iqEstimate = 115 + percentage * 30; // 115-145 } resultDiv.textContent = `Your score: ${score}/${TEST_LENGTH}. Estimated IQ: ${Math.round(iqEstimate)}`; resultDiv.style.display = 'block'; // Show difficulty selector again document.querySelector('.difficulty-selector').style.display = 'block'; } // Utility functions function shuffleArray(array) { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } return array; } </script> </body> </html> ``` This implementation includes: 1. **Pattern Generation**: - Creates mathematically consistent 3×3 patterns using transformations - Supports three difficulty levels (easy, medium, hard) - Generates multiple-choice options with one correct answer 2. **Visual Representation**: - Draws patterns using HTML Canvas - Shows the incomplete grid with a missing ninth cell - Presents six options to choose from 3. **Test Flow**: - Tracks user responses - Provides feedback on correct/incorrect answers - Shows progress through the test - Calculates an IQ estimate based on performance and difficulty level 4. **User Interface**: - Clean, responsive design - Interactive elements with visual feedback - Progress indicator The patterns are generated using mathematical transformations (size, rotation, color changes) applied systematically across the grid, ensuring logical consistency. The difficulty levels control the complexity of these transformations.
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>IQ Test Generator</title> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; color: #333; } .container { background: white; border-radius: 15px; padding: 30px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); } h1 { text-align: center; color: #4a5568; margin-bottom: 30px; } .controls { display: flex; justify-content: center; gap: 20px; margin-bottom: 30px; flex-wrap: wrap; } button { padding: 12px 24px; border: none; border-radius: 8px; cursor: pointer; font-size: 16px; font-weight: bold; transition: all 0.3s ease; } .primary-btn { background: #4299e1; color: white; } .primary-btn:hover { background: #3182ce; } .secondary-btn { background: #e2e8f0; color: #4a5568; } .secondary-btn:hover { background: #cbd5e0; } select { padding: 12px; border-radius: 8px; border: 2px solid #e2e8f0; font-size: 16px; } .test-area { display: none; } .question-info { text-align: center; margin-bottom: 20px; font-size: 18px; font-weight: bold; } .grid-container { display: grid; grid-template-columns: repeat(3, 120px); gap: 10px; justify-content: center; margin-bottom: 30px; } .grid-cell { width: 120px; height: 120px; border: 3px solid #4a5568; border-radius: 8px; display: flex; align-items: center; justify-content: center; background: white; } .grid-cell.missing { background: #f7fafc; border-style: dashed; } .choices-container { display: grid; grid-template-columns: repeat(4, 120px); gap: 15px; justify-content: center; margin-bottom: 30px; } .choice { width: 120px; height: 120px; border: 3px solid #cbd5e0; border-radius: 8px; cursor: pointer; display: flex; align-items: center; justify-content: center; background: white; transition: all 0.3s ease; } .choice:hover { border-color: #4299e1; transform: scale(1.05); } .choice.selected { border-color: #38a169; background: #f0fff4; } .choice.correct { border-color: #38a169; background: #c6f6d5; } .choice.incorrect { border-color: #e53e3e; background: #fed7d7; } .results { display: none; text-align: center; padding: 30px; background: #f7fafc; border-radius: 12px; margin-top: 30px; } .iq-score { font-size: 48px; font-weight: bold; color: #4299e1; margin: 20px 0; } .progress { margin-bottom: 20px; text-align: center; } .progress-bar { width: 100%; height: 8px; background: #e2e8f0; border-radius: 4px; overflow: hidden; } .progress-fill { height: 100%; background: #4299e1; transition: width 0.3s ease; } .timer { text-align: center; font-size: 20px; font-weight: bold; color: #e53e3e; margin-bottom: 20px; } </style> </head> <body> <div class="container"> <h1>🧠 IQ Test Generator</h1> <div class="controls"> <select id="difficulty"> <option value="easy">Easy (Basic patterns)</option> <option value="medium">Medium (Complex patterns)</option> <option value="hard">Hard (Advanced logic)</option> </select> <button class="primary-btn" onclick="startTest()">Start Test</button> <button class="secondary-btn" onclick="resetTest()">Reset</button> </div> <div class="test-area" id="testArea"> <div class="progress"> <div class="question-info"> Question <span id="currentQ">1</span> of <span id="totalQ">12</span> </div> <div class="progress-bar"> <div class="progress-fill" id="progressFill"></div> </div> </div> <div class="timer" id="timer">Time: 15:00</div> <div class="grid-container" id="gridContainer"></div> <div class="choices-container" id="choicesContainer"></div> <button class="primary-btn" onclick="nextQuestion()" id="nextBtn" style="display: none; margin: 0 auto; display: block;">Next Question</button> </div> <div class="results" id="results"> <h2>Test Complete!</h2> <div class="iq-score" id="iqScore">120</div> <p>Your estimated IQ score</p> <div id="analysis"></div> <button class="primary-btn" onclick="resetTest()">Take Test Again</button> </div> </div> <script> class IQTestGenerator { constructor() { this.currentQuestion = 0; this.totalQuestions = 12; this.responses = []; this.difficulty = 'easy'; this.startTime = null; this.timeLimit = 15 * 60 * 1000; // 15 minutes this.timer = null; this.questions = []; } startTest() { this.difficulty = document.getElementById('difficulty').value; this.currentQuestion = 0; this.responses = []; this.startTime = Date.now(); this.generateAllQuestions(); document.querySelector('.controls').style.display = 'none'; document.getElementById('testArea').style.display = 'block'; document.getElementById('results').style.display = 'none'; this.startTimer(); this.displayQuestion(); } generateAllQuestions() { this.questions = []; for (let i = 0; i < this.totalQuestions; i++) { this.questions.push(this.generateQuestion()); } } generateQuestion() { const transforms = this.getTransforms(); const pattern = this.selectPattern(); const grid = this.generateGrid(pattern, transforms); const choices = this.generateChoices(grid, pattern, transforms); return { grid, choices, correctAnswer: 0 }; } getTransforms() { const base = { easy: [ { type: 'color', values: ['#ff6b6b', '#4ecdc4', '#45b7d1'] }, { type: 'size', values: [20, 40, 60] }, { type: 'shape', values: ['circle', 'square', 'triangle'] } ], medium: [ { type: 'color', values: ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4'] }, { type: 'size', values: [15, 30, 45, 60] }, { type: 'rotation', values: [0, 90, 180, 270] }, { type: 'shape', values: ['circle', 'square', 'triangle', 'diamond'] } ], hard: [ { type: 'color', values: ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#feca57'] }, { type: 'size', values: [12, 24, 36, 48, 60] }, { type: 'rotation', values: [0, 45, 90, 135, 180, 225, 270, 315] }, { type: 'count', values: [1, 2, 3] }, { type: 'shape', values: ['circle', 'square', 'triangle', 'diamond', 'star'] } ] }; return base[this.difficulty]; } selectPattern() { const patterns = [ 'increment', 'cycle', 'alternate', 'matrix_add', 'diagonal' ]; return patterns[Math.floor(Math.random() * patterns.length)]; } generateGrid(pattern, transforms) { const grid = []; const activeTransform = transforms[Math.floor(Math.random() * transforms.length)]; for (let i = 0; i < 9; i++) { const row = Math.floor(i / 3); const col = i % 3; if (i === 8) { // Missing cell grid.push(null); continue; } const element = this.generateElement(i, row, col, pattern, activeTransform); grid.push(element); } // Generate the correct answer for position 8 const correctElement = this.generateElement(8, 2, 2, pattern, activeTransform); grid[8] = correctElement; return grid; } generateElement(index, row, col, pattern, transform) { let value; switch (pattern) { case 'increment': value = transform.values[index % transform.values.length]; break; case 'cycle': value = transform.values[(row + col) % transform.values.length]; break; case 'alternate': value = transform.values[((row + col) % 2) * (transform.values.length - 1)]; break; case 'matrix_add': value = transform.values[(row + col) % transform.values.length]; break; case 'diagonal': if (row === col) { value = transform.values[0]; } else if (row + col === 2) { value = transform.values[1]; } else { value = transform.values[2] || transform.values[0]; } break; default: value = transform.values[index % transform.values.length]; } return { type: transform.type, value: value, shape: transform.type === 'shape' ? value : 'circle', color: transform.type === 'color' ? value : '#45b7d1', size: transform.type === 'size' ? value : 40, rotation: transform.type === 'rotation' ? value : 0, count: transform.type === 'count' ? value : 1 }; } generateChoices(grid, pattern, transforms) { const choices = []; const correct = grid[8]; choices.push(correct); // Generate 3 incorrect choices for (let i = 0; i < 3; i++) { const incorrect = this.generateIncorrectChoice(correct, transforms); choices.push(incorrect); } // Shuffle choices but keep track of correct answer const correctIndex = 0; for (let i = choices.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [choices[i], choices[j]] = [choices[j], choices[i]]; if (i === correctIndex) { this.questions[this.questions.length - 1].correctAnswer = j; } else if (j === correctIndex) { this.questions[this.questions.length - 1].correctAnswer = i; } } return choices; } generateIncorrectChoice(correct, transforms) { const incorrect = { ...correct }; const transform = transforms[Math.floor(Math.random() * transforms.length)]; const randomValue = transform.values[Math.floor(Math.random() * transform.values.length)]; switch (transform.type) { case 'color': incorrect.color = randomValue; break; case 'size': incorrect.size = randomValue; break; case 'shape': incorrect.shape = randomValue; break; case 'rotation': incorrect.rotation = randomValue; break; case 'count': incorrect.count = randomValue; break; } return incorrect; } displayQuestion() { const question = this.questions[this.currentQuestion]; document.getElementById('currentQ').textContent = this.currentQuestion + 1; document.getElementById('totalQ').textContent = this.totalQuestions; const progress = ((this.currentQuestion) / this.totalQuestions) * 100; document.getElementById('progressFill').style.width = progress + '%'; this.renderGrid(question.grid); this.renderChoices(question.choices); document.getElementById('nextBtn').style.display = 'none'; } renderGrid(grid) { const container = document.getElementById('gridContainer'); container.innerHTML = ''; for (let i = 0; i < 9; i++) { const cell = document.createElement('div'); cell.className = 'grid-cell'; if (i === 8) { cell.className += ' missing'; cell.innerHTML = '?'; cell.style.fontSize = '48px'; cell.style.color = '#a0aec0'; } else { const svg = this.createElement(grid[i]); cell.appendChild(svg); } container.appendChild(cell); } } renderChoices(choices) { const container = document.getElementById('choicesContainer'); container.innerHTML = ''; choices.forEach((choice, index) => { const choiceDiv = document.createElement('div'); choiceDiv.className = 'choice'; choiceDiv.onclick = () => this.selectChoice(index); const svg = this.createElement(choice); choiceDiv.appendChild(svg); container.appendChild(choiceDiv); }); } createElement(element) { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('width', '100'); svg.setAttribute('height', '100'); const count = element.count || 1; const spacing = 100 / (count + 1); for (let i = 0; i < count; i++) { const shape = this.createShape(element); const x = spacing * (i + 1); const y = 50; shape.setAttribute('transform', `translate(${x}, ${y}) rotate(${element.rotation || 0})`); svg.appendChild(shape); } return svg; } createShape(element) { const shape = element.shape || 'circle'; const color = element.color || '#45b7d1'; const size = (element.size || 40) / 2; let shapeElement; switch (shape) { case 'circle': shapeElement = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); shapeElement.setAttribute('r', size); shapeElement.setAttribute('cx', 0); shapeElement.setAttribute('cy', 0); break; case 'square': shapeElement = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); shapeElement.setAttribute('width', size * 2); shapeElement.setAttribute('height', size * 2); shapeElement.setAttribute('x', -size); shapeElement.setAttribute('y', -size); break; case 'triangle': shapeElement = document.createElementNS('http://www.w3.org/2000/svg', 'polygon'); const points = `0,${-size} ${-size},${size} ${size},${size}`; shapeElement.setAttribute('points', points); break; case 'diamond': shapeElement = document.createElementNS('http://www.w3.org/2000/svg', 'polygon'); const diamondPoints = `0,${-size} ${size},0 0,${size} ${-size},0`; shapeElement.setAttribute('points', diamondPoints); break; case 'star': shapeElement = document.createElementNS('http://www.w3.org/2000/svg', 'polygon'); const starPoints = this.generateStarPoints(size); shapeElement.setAttribute('points', starPoints); break; default: shapeElement = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); shapeElement.setAttribute('r', size); shapeElement.setAttribute('cx', 0); shapeElement.setAttribute('cy', 0); } shapeElement.setAttribute('fill', color); shapeElement.setAttribute('stroke', '#333'); shapeElement.setAttribute('stroke-width', '2'); return shapeElement; } generateStarPoints(size) { const points = []; const outerRadius = size; const innerRadius = size * 0.4; for (let i = 0; i < 10; i++) { const angle = (i * Math.PI) / 5; const radius = i % 2 === 0 ? outerRadius : innerRadius; const x = Math.cos(angle) * radius; const y = Math.sin(angle) * radius; points.push(`${x},${y}`); } return points.join(' '); } selectChoice(index) { const choices = document.querySelectorAll('.choice'); choices.forEach(choice => choice.classList.remove('selected')); choices[index].classList.add('selected'); const question = this.questions[this.currentQuestion]; const isCorrect = index === question.correctAnswer; this.responses.push({ questionIndex: this.currentQuestion, selectedChoice: index, correctChoice: question.correctAnswer, isCorrect: isCorrect, timeSpent: Date.now() - this.startTime }); // Show correct/incorrect feedback setTimeout(() => { choices[question.correctAnswer].classList.add('correct'); if (!isCorrect) { choices[index].classList.add('incorrect'); } document.getElementById('nextBtn').style.display = 'block'; }, 500); } nextQuestion() { this.currentQuestion++; if (this.currentQuestion >= this.totalQuestions) { this.finishTest(); } else { this.displayQuestion(); } } startTimer() { const updateTimer = () => { const elapsed = Date.now() - this.startTime; const remaining = Math.max(0, this.timeLimit - elapsed); const minutes = Math.floor(remaining / 60000); const seconds = Math.floor((remaining % 60000) / 1000); document.getElementById('timer').textContent = `Time: ${minutes}:${seconds.toString().padStart(2, '0')}`; if (remaining <= 0) { this.finishTest(); } }; this.timer = setInterval(updateTimer, 1000); updateTimer(); } finishTest() { clearInterval(this.timer); const score = this.calculateIQ(); const analysis = this.generateAnalysis(); document.getElementById('testArea').style.display = 'none'; document.getElementById('results').style.display = 'block'; document.getElementById('iqScore').textContent = score; document.getElementById('analysis').innerHTML = analysis; } calculateIQ() { const correct = this.responses.filter(r => r.isCorrect).length; const accuracy = correct / this.totalQuestions; const totalTime = Date.now() - this.startTime; const avgTimePerQuestion = totalTime / this.totalQuestions / 1000; // seconds // Base IQ calculation let baseIQ = 70 + (accuracy * 60); // Difficulty multiplier const difficultyMultiplier = { easy: 1.0, medium: 1.15, hard: 1.3 }; baseIQ *= difficultyMultiplier[this.difficulty]; // Time bonus (faster = higher IQ, but cap the bonus) const idealTimePerQuestion = 45; // seconds if (avgTimePerQuestion < idealTimePerQuestion) { const timeBonus = Math.min(10, (idealTimePerQuestion - avgTimePerQuestion) / 5); baseIQ += timeBonus; } // Ensure realistic range return Math.max(70, Math.min(160, Math.round(baseIQ))); } generateAnalysis() { const correct = this.responses.filter(r => r.isCorrect).length; const accuracy = (correct / this.totalQuestions * 100).toFixed(1); const totalTime = Math.round((Date.now() - this.startTime) / 1000); const avgTime = Math.round(totalTime / this.totalQuestions); return ` <div style="text-align: left; max-width: 400px; margin: 0 auto;"> <h3>Performance Analysis:</h3> <p><strong>Accuracy:</strong> ${accuracy}% (${correct}/${this.totalQuestions} correct)</p> <p><strong>Total Time:</strong> ${Math.floor(totalTime/60)}:${(totalTime%60).toString().padStart(2,'0')}</p> <p><strong>Average Time per Question:</strong> ${avgTime} seconds</p> <p><strong>Difficulty Level:</strong> ${this.difficulty.charAt(0).toUpperCase() + this.difficulty.slice(1)}</p> <hr> <p><em>Note: This is an approximate estimate based on pattern recognition tasks. Professional IQ tests include multiple cognitive domains and standardized conditions.</em></p> </div> `; } resetTest() { clearInterval(this.timer); this.currentQuestion = 0; this.responses = []; document.querySelector('.controls').style.display = 'flex'; document.getElementById('testArea').style.display = 'none'; document.getElementById('results').style.display = 'none'; } } const iqTest = new IQTestGenerator(); function startTest() { iqTest.startTest(); } function nextQuestion() { iqTest.nextQuestion(); } function resetTest() { iqTest.resetTest(); } </script> </body> </html> ``` This IQ test generator creates a comprehensive browser-based intelligence assessment tool with the following features: ## Key Features: 1. **Mathematical Transformation Functions**: Uses logical patterns like increment, cycle, alternate, matrix addition, and diagonal relationships to ensure consistent logic. 2. **Three Difficulty Levels**: - **Easy**: Basic color, size, and shape patterns - **Medium**: Adds rotation and more complex variations - **Hard**: Includes element count, advanced rotations, and star shapes 3. **3×3 Grid Format**: Standard Raven's Progressive Matrices style with the 9th cell missing 4. **Multiple Choice Selection**: 4 choices per question with visual feedback 5. **Performance Tracking**: - Accuracy percentage - Response time per question - Total time taken - Difficulty adjustment in scoring 6. **IQ Estimation Algorithm**: - Base score from accuracy (70-130 range) - Difficulty multiplier (Easy: 1.0x, Medium: 1.15x, Hard: 1.3x) - Time bonus for quick, accurate responses - Final range capped between 70-160 7. **Visual Elements**: - SVG-rendered shapes (circle, square, triangle, diamond, star) - Color variations, size changes, rotations - Multiple element counts - Professional UI with progress tracking The test generates 12 questions with a 15-minute time limit and provides detailed performance analysis along with an estimated IQ score based on pattern recognition abilities.
# IQ Test Sequence Completion Task Generator ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Sequence Completion IQ Test</title> <style> body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; line-height: 1.6; } h1, h2 { color: #333; } .controls { margin: 20px 0; padding: 15px; background-color: #f5f5f5; border-radius: 5px; } .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); grid-gap: 10px; margin: 20px 0; } .grid-cell { aspect-ratio: 1; border: 1px solid #ccc; display: flex; justify-content: center; align-items: center; background-color: #f9f9f9; position: relative; } .grid-cell canvas { width: 100%; height: 100%; display: block; } .missing { background-color: #eee; display: flex; justify-content: center; align-items: center; } .missing:after { content: "?"; font-size: 2em; color: #999; } .options-container { display: grid; grid-template-columns: repeat(4, 1fr); grid-gap: 10px; margin: 20px 0; } .option { aspect-ratio: 1; border: 1px solid #ddd; cursor: pointer; transition: all 0.2s; } .option:hover { border-color: #666; transform: scale(1.05); } .option.selected { border: 3px solid #4CAF50; transform: scale(1.05); } .results { margin: 20px 0; padding: 15px; background-color: #e9f7ef; border-radius: 5px; display: none; } .progress { margin-top: 10px; font-weight: bold; } #progressBar { height: 20px; background-color: #ddd; border-radius: 4px; margin-top: 5px; } #progressBarFill { height: 100%; width: 0%; background-color: #4CAF50; border-radius: 4px; transition: width 0.3s ease; } button { padding: 10px 15px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; margin-right: 10px; } button:hover { background-color: #45a049; } .explanation { margin-top: 20px; padding: 10px; border-left: 4px solid #4CAF50; background-color: #f9f9f9; display: none; } </style> </head> <body> <h1>Sequence Completion IQ Test</h1> <div class="controls"> <p>Select difficulty level and press "Start Test" to begin.</p> <div> <input type="radio" id="easy" name="difficulty" value="easy" checked> <label for="easy">Easy</label> <input type="radio" id="medium" name="difficulty" value="medium"> <label for="medium">Medium</label> <input type="radio" id="hard" name="difficulty" value="hard"> <label for="hard">Hard</label> </div> <div style="margin-top: 15px;"> <button id="startTest">Start Test</button> <button id="showExplanation" style="display:none;">Show Explanation</button> </div> </div> <div class="grid-container" id="grid"> <!-- Grid cells will be generated here --> </div> <div class="progress"> <div id="progressText">Question 0 of 0</div> <div id="progressBar"> <div id="progressBarFill"></div> </div> </div> <div class="options-container" id="options"> <!-- Options will be generated here --> </div> <div class="explanation" id="explanation"></div> <div class="results" id="results"> <h2>Test Results</h2> <p id="score"></p> <p id="iqEstimate"></p> <button id="restartTest">Try Another Test</button> </div> <script> // Main variables const NUM_QUESTIONS = 10; const TOTAL_CELLS = 9; let currentQuestion = 0; let correctAnswers = 0; let currentCorrectOption = 0; let questionsData = []; let testStartTime; // DOM elements const grid = document.getElementById('grid'); const optionsContainer = document.getElementById('options'); const startTestButton = document.getElementById('startTest'); const restartTestButton = document.getElementById('restartTest'); const resultsDiv = document.getElementById('results'); const progressText = document.getElementById('progressText'); const progressBarFill = document.getElementById('progressBarFill'); const showExplanationButton = document.getElementById('showExplanation'); const explanationDiv = document.getElementById('explanation'); // Transformation functions for generating patterns const transformations = { // Rotation functions rotateClockwise: (x, y, canvasSize, shape, params) => { const ctx = shape.getContext('2d'); ctx.translate(canvasSize/2, canvasSize/2); ctx.rotate(params.angle * Math.PI / 180); ctx.translate(-canvasSize/2, -canvasSize/2); }, // Color functions changeColor: (x, y, canvasSize, shape, params) => { const ctx = shape.getContext('2d'); ctx.fillStyle = params.color; }, // Size functions scaleShape: (x, y, canvasSize, shape, params) => { const ctx = shape.getContext('2d'); ctx.translate(canvasSize/2, canvasSize/2); ctx.scale(params.scale, params.scale); ctx.translate(-canvasSize/2, -canvasSize/2); }, // Position functions translateShape: (x, y, canvasSize, shape, params) => { const ctx = shape.getContext('2d'); ctx.translate(params.dx, params.dy); }, // Shape count multipleShapes: (x, y, canvasSize, shape, params) => { // Will be handled in the draw function return params.count; } }; // Shape generators const shapeGenerators = { circle: (ctx, x, y, size, params) => { ctx.beginPath(); ctx.arc(x, y, size/2, 0, 2 * Math.PI); ctx.fill(); if (params.stroke) { ctx.stroke(); } }, square: (ctx, x, y, size, params) => { ctx.beginPath(); ctx.rect(x - size/2, y - size/2, size, size); ctx.fill(); if (params.stroke) { ctx.stroke(); } }, triangle: (ctx, x, y, size, params) => { ctx.beginPath(); ctx.moveTo(x, y - size/2); ctx.lineTo(x + size/2, y + size/2); ctx.lineTo(x - size/2, y + size/2); ctx.closePath(); ctx.fill(); if (params.stroke) { ctx.stroke(); } }, line: (ctx, x, y, size, params) => { ctx.beginPath(); const angle = params.angle || 0; const rad = angle * Math.PI / 180; ctx.moveTo(x - Math.cos(rad) * size/2, y - Math.sin(rad) * size/2); ctx.lineTo(x + Math.cos(rad) * size/2, y + Math.sin(rad) * size/2); ctx.lineWidth = params.thickness || 3; ctx.stroke(); } }; // Generate a pattern based on row and column position function generatePattern(row, col, difficulty) { const baseShape = Math.floor(Math.random() * 4); let shapes = ['circle', 'square', 'triangle', 'line'][baseShape]; // Base properties let pattern = { shapes: shapes, color: '#000000', size: 0.5, x: 0.5, y: 0.5, angle: 0, stroke: false, count: 1 }; // Apply transformations based on difficulty and position switch (difficulty) { case 'easy': // Simple row-based transformations if (row === 0) pattern.size = 0.3; else if (row === 1) pattern.size = 0.5; else if (row === 2) pattern.size = 0.7; if (col === 0) pattern.color = '#FF0000'; else if (col === 1) pattern.color = '#00FF00'; else if (col === 2) pattern.color = '#0000FF'; break; case 'medium': // More complex transformations pattern.size = 0.3 + (row * 0.2); if (col === 0) pattern.color = '#FF0000'; else if (col === 1) pattern.color = '#00FF00'; else if (col === 2) pattern.color = '#0000FF'; pattern.angle = row * 45 + col * 30; if ((row + col) % 2 === 0) { pattern.stroke = true; } break; case 'hard': // Advanced transformations pattern.size = 0.25 + (row * 0.15) + (col * 0.1); const hue = (row * 120 + col * 60) % 360; pattern.color = `hsl(${hue}, 80%, 50%)`; pattern.angle = row * 45 + col * 45; pattern.x = 0.3 + col * 0.2; pattern.y = 0.3 + row * 0.2; pattern.count = 1 + (row + col) % 3; if ((row * col) % 2 === 0) { pattern.stroke = true; } break; } return pattern; } // Generate a consistent pattern set for all 9 cells function generatePatternSet(difficulty) { const patternSet = []; // Function to check if two patterns are similar enough const areTooSimilar = (p1, p2) => { const sizeThreshold = 0.1; const colorThreshold = 0.2; // Very simple similarity check return Math.abs(p1.size - p2.size) < sizeThreshold; }; // Generate patterns for each position for (let row = 0; row < 3; row++) { for (let col = 0; col < 3; col++) { let pattern = generatePattern(row, col, difficulty); patternSet.push({ row, col, pattern }); } } return patternSet; } // Generate alternative options for the answer function generateAlternativeOptions(correctPattern, difficulty) { const options = []; options.push(JSON.parse(JSON.stringify(correctPattern))); // Correct option // Generate 3 incorrect options for (let i = 0; i < 3; i++) { let altPattern = JSON.parse(JSON.stringify(correctPattern)); // Modify the pattern based on difficulty switch (difficulty) { case 'easy': // Change color or size if (i % 2 === 0) { const colors = ['#FF0000', '#00FF00', '#0000FF', '#FF00FF', '#FFFF00']; altPattern.color = colors[Math.floor(Math.random() * colors.length)]; } else { altPattern.size = correctPattern.size + (Math.random() * 0.4 - 0.2); } break; case 'medium': // Change two properties altPattern.size = correctPattern.size + (Math.random() * 0.4 - 0.2); altPattern.angle = correctPattern.angle + (Math.floor(Math.random() * 8) * 45); if (i === 2) { const shapes = ['circle', 'square', 'triangle', 'line']; altPattern.shapes = shapes[Math.floor(Math.random() * shapes.length)]; } break; case 'hard': // Change multiple properties altPattern.size = correctPattern.size + (Math.random() * 0.4 - 0.2); altPattern.angle = correctPattern.angle + (Math.floor(Math.random() * 8) * 45); altPattern.x = correctPattern.x + (Math.random() * 0.4 - 0.2); altPattern.y = correctPattern.y + (Math.random() * 0.4 - 0.2); altPattern.count = Math.max(1, correctPattern.count + (Math.floor(Math.random() * 3) - 1)); if (i === 2) { const shapes = ['circle', 'square', 'triangle', 'line']; altPattern.shapes = shapes[Math.floor(Math.random() * shapes.length)]; } break; } options.push(altPattern); } // Shuffle options for (let i = options.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [options[i], options[j]] = [options[j], options[i]]; // Track the correct option if (i === 0 && j === 0) currentCorrectOption = j; else if (j === 0) currentCorrectOption = i; } return options; } // Draw a pattern on a canvas function drawPattern(canvas, pattern) { const ctx = canvas.getContext('2d'); const canvasSize = canvas.width; // Clear canvas ctx.clearRect(0, 0, canvasSize, canvasSize); // Set up styling ctx.fillStyle = pattern.color || '#000000'; ctx.strokeStyle = '#000000'; ctx.lineWidth = 2; const centerX = canvasSize * (pattern.x || 0.5); const centerY = canvasSize * (pattern.y || 0.5); const size = canvasSize * pattern.size; // Handle multiple shapes const count = pattern.count || 1; const shapeType = pattern.shapes || 'circle'; ctx.save(); // Apply rotation if any if (pattern.angle) { ctx.translate(centerX, centerY); ctx.rotate(pattern.angle * Math.PI / 180); ctx.translate(-centerX, -centerY); } if (count === 1) { // Draw a single shape shapeGenerators[shapeType](ctx, centerX, centerY, size, { stroke: pattern.stroke, angle: pattern.angle }); } else { // Draw multiple shapes in a pattern const radius = size / 2; for (let i = 0; i < count; i++) { const angle = (i * 2 * Math.PI) / count; const x = centerX + Math.cos(angle) * radius; const y = centerY + Math.sin(angle) * radius; shapeGenerators[shapeType](ctx, x, y, size / count, { stroke: pattern.stroke, angle: pattern.angle }); } } ctx.restore(); } // Generate a question with a grid of patterns function generateQuestion(difficulty) { const patternSet = generatePatternSet(difficulty); const missingPatternIndex = 8; // Last cell (bottom right) const correctPattern = patternSet[missingPatternIndex].pattern; const options = generateAlternativeOptions(correctPattern, difficulty); // Generate explanation let explanation = `This sequence follows a pattern where:<br>`; if (difficulty === 'easy') { explanation += `• Shapes get larger from top to bottom<br>`; explanation += `• Colors change from red to green to blue from left to right`; } else if (difficulty === 'medium') { explanation += `• Shapes get larger from top to bottom<br>`; explanation += `• Colors change from left to right<br>`; explanation += `• Rotation increases by row and column position<br>`; explanation += `• Some shapes have outlines based on position`; } else { explanation += `• Size changes based on both row and column position<br>`; explanation += `• Colors follow a hue rotation pattern<br>`; explanation += `• Rotation angles depend on position<br>`; explanation += `• Position shifts slightly by row and column<br>`; explanation += `• Number of shapes increases with sum of row and column<br>`; explanation += `• Outlines appear in a checker pattern`; } return { patterns: patternSet, missingIndex: missingPatternIndex, options: options, correctOptionIndex: currentCorrectOption, explanation: explanation }; } // Initialize the grid with the pattern function initializeGrid(questionData) { grid.innerHTML = ''; // Create the 3x3 grid for (let i = 0; i < TOTAL_CELLS; i++) { const cell = document.createElement('div'); cell.className = 'grid-cell'; // Make the missing cell empty if (i === questionData.missingIndex) { cell.classList.add('missing'); } else { const canvas = document.createElement('canvas'); canvas.width = 150; canvas.height = 150; cell.appendChild(canvas); // Find the relevant pattern const row = Math.floor(i / 3); const col = i % 3; const patternData = questionData.patterns.find(p => p.row === row && p.col === col); // Draw the pattern drawPattern(canvas, patternData.pattern); } grid.appendChild(cell); } } // Initialize the options function initializeOptions(questionData) { optionsContainer.innerHTML = ''; // Create the options questionData.options.forEach((optionPattern, index) => { const optionDiv = document.createElement('div'); optionDiv.className = 'option'; optionDiv.dataset.index = index; const canvas = document.createElement('canvas'); canvas.width = 100; canvas.height = 100; optionDiv.appendChild(canvas); // Draw the pattern drawPattern(canvas, optionPattern); // Add click event optionDiv.addEventListener('click', () => { selectOption(index, questionData.correctOptionIndex); }); optionsContainer.appendChild(optionDiv); }); } // Select an option function selectOption(selectedIndex, correctIndex) { // Clear previous selections document.querySelectorAll('.option').forEach(opt => { opt.classList.remove('selected'); }); // Add selected class document.querySelector(`.option[data-index="${selectedIndex}"]`).classList.add('selected'); // Record the answer if (selectedIndex === correctIndex) { correctAnswers++; } // Show explanation button showExplanationButton.style.display = 'inline-block'; // Allow a small delay before moving to next question setTimeout(() => { moveToNextQuestion(); }, 1000); } // Move to the next question or finish the test function moveToNextQuestion() { currentQuestion++; if (currentQuestion < NUM_QUESTIONS) { // Update progress updateProgress(); // Load next question loadQuestion(); } else { // Test is complete finishTest(); } } // Update the progress indicators function updateProgress() { progressText.textContent = `Question ${currentQuestion} of ${NUM_QUESTIONS}`; const percentage = (currentQuestion / NUM_QUESTIONS) * 100; progressBarFill.style.width = `${percentage}%`; } // Load a question function loadQuestion() { // Hide the explanation explanationDiv.style.display = 'none'; showExplanationButton.style.display = 'none'; // Get difficulty level const difficultyRadios = document.getElementsByName('difficulty'); let difficulty = 'easy'; for (let radio of difficultyRadios) { if (radio.checked) { difficulty = radio.value; break; } } // Generate question data const questionData = generateQuestion(difficulty); questionsData[currentQuestion] = questionData; // Set up the grid and options initializeGrid(questionData); initializeOptions(questionData); } // Calculate an IQ estimate based on performance function calculateIQEstimate(correctCount, totalQuestions, difficulty, timeSpentMinutes) { const baseIQ = 100; const performanceRatio = correctCount / totalQuestions; // Adjust for difficulty let difficultyFactor = 1; switch(difficulty) { case 'easy': difficultyFactor = 0.9; break; case 'medium': difficultyFactor = 1; break; case 'hard': difficultyFactor = 1.15; break; } // Adjust for time (diminishing returns for very fast times) const avgExpectedTimeMinutes = 1; // 1 minute per question const timeRatio = Math.min(avgExpectedTimeMinutes * totalQuestions / timeSpentMinutes, 1.5); // Calculate IQ adjustment const performanceAdjustment = (performanceRatio - 0.5) * 50; // -25 to +25 range const iqEstimate = Math.round(baseIQ + (performanceAdjustment * difficultyFactor * timeRatio)); // Cap the estimate within reasonable bounds return Math.min(Math.max(iqEstimate, 70), 130); } // Finish the test and show results function finishTest() { const testEndTime = new Date(); const timeSpentMinutes = (testEndTime - testStartTime) / (1000 * 60); // Get difficulty const difficultyRadios = document.getElementsByName('difficulty'); let difficulty = 'easy'; for (let radio of difficultyRadios) { if (radio.checked) { difficulty = radio.value; break; } } // Calculate IQ estimate const iqEstimate = calculateIQEstimate(correctAnswers, NUM_QUESTIONS, difficulty, timeSpentMinutes); // Update results display const scoreElement = document.getElementById('score'); scoreElement.textContent = `You answered ${correctAnswers} out of ${NUM_QUESTIONS} questions correctly.`; const iqEstimateElement = document.getElementById('iqEstimate'); iqEstimateElement.textContent = `Based on your performance, your estimated IQ is approximately ${iqEstimate}.`; // Show the results resultsDiv.style.display = 'block'; // Hide the test grid.style.display = 'none'; optionsContainer.style.display = 'none'; // Hide explanation elements explanationDiv.style.display = 'none'; showExplanationButton.style.display = 'none'; } // Start the test startTestButton.addEventListener('click', () => { // Reset test data currentQuestion = 0; correctAnswers = 0; questionsData = []; testStartTime = new Date(); // Set up UI grid.style.display = 'grid'; optionsContainer.style.display = 'grid'; resultsDiv.style.display = 'none'; startTestButton.style.display = 'none'; // Update progress updateProgress(); // Load first question loadQuestion(); }); // Restart the test restartTestButton.addEventListener('click', () => { // Reset UI grid.style.display = 'grid'; optionsContainer.style.display = 'grid'; resultsDiv.style.display = 'none'; startTestButton.style.display = 'inline-block'; // Show controls again document.querySelector('.controls').style.display = 'block'; }); // Show explanation showExplanationButton.addEventListener('click', () => { if (currentQuestion > 0 && questionsData[currentQuestion - 1]) { explanationDiv.innerHTML = questionsData[currentQuestion - 1].explanation; explanationDiv.style.display = 'block'; } }); </script> </body> </html> ``` This IQ test generator creates sequence-completion tasks where the user must identify the missing pattern in a 3×3 grid. Key features: - Three difficulty levels (easy, medium, hard) with increasing complexity - Mathematically-derived patterns ensuring logical consistency - Multiple-choice selection for the missing pattern - Progress tracking and scoring - Interactive explanation of pattern logic - IQ score estimation based on correct answers, difficulty, and time taken - Fully client-side implementation with no external dependencies The patterns are generated using transformations that follow logical rules based on position in the grid, with properties like size, color, rotation, and shape count changing consistently.
``` <html> <head> <title>IQ Test Generator</title> <style> body { font-family: Arial, sans-serif; text-align: center; } #grid { display: grid; grid-template-columns: repeat(3, 100px); grid-gap: 5px; margin: 20px auto; } .cell { width: 100px; height: 100px; border: 1px solid black; position: relative; } canvas { width: 100%; height: 100%; } button { margin: 10px; padding: 10px; } #options { margin: 20px; } .option { display: inline-block; margin: 5px; padding: 10px; border: 1px solid black; cursor: pointer; } </style> </head> <body> <h1>IQ Test Generator</h1> <label for="difficulty">Select Difficulty: </label> <select id="difficulty"> <option value="easy">Easy</option> <option value="medium">Medium</option> <option value="hard">Hard</option> </select> <button onclick="generateTest()">Start Test</button> <div id="testArea"> <div id="grid"></div> <div id="options"></div> <button onclick="checkAnswer()" id="submitBtn" style="display:none;">Submit Answer</button> <p id="feedback"></p> <p>Score: <span id="score">0</span> / <span id="total">0</span></p> <button onclick="endTest()" id="endBtn" style="display:none;">End Test</button> <p id="iqResult"></p> </div> <script> let score = 0; let totalQuestions = 5; // Fixed for simplicity let currentQuestion = 0; let correctAnswer = null; function generateTest() { score = 0; currentQuestion = 0; document.getElementById('score').textContent = score; document.getElementById('total').textContent = totalQuestions; document.getElementById('feedback').textContent = ''; document.getElementById('iqResult').textContent = ''; generateQuestion(); } function generateQuestion() { const difficulty = document.getElementById('difficulty').value; const grid = document.getElementById('grid'); grid.innerHTML = ''; // Clear grid const optionsDiv = document.getElementById('options'); optionsDiv.innerHTML = ''; // Clear options document.getElementById('submitBtn').style.display = 'none'; // Generate 3x3 grid data const gridData = Array(3).fill().map(() => Array(3).fill(null)); let transformation; if (difficulty === 'easy') { // Simple: Increasing size circles transformation = (x, y) => ({ type: 'circle', radius: 10 + (x + y) * 5 }); } else if (difficulty === 'medium') { // Medium: Rotating squares transformation = (x, y) => ({ type: 'square', size: 20, rotation: (x + y) * 45 }); } else { // Hard: Combination: circles with increasing size and color change transformation = (x, y) => ({ type: 'circle', radius: 10 + x * 5, color: `hsl(${y * 60}, 100%, 50%)` }); } for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { if (!(i === 2 && j === 2)) { // Leave the last one for options gridData[i][j] = transformation(i, j); } } } // Render the grid except the last cell for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { const cell = document.createElement('div'); cell.className = 'cell'; if (gridData[i][j]) { const canvas = document.createElement('canvas'); canvas.width = 100; canvas.height = 100; const ctx = canvas.getContext('2d'); drawShape(ctx, gridData[i][j]); cell.appendChild(canvas); } grid.appendChild(cell); } } // Generate options for the last cell correctAnswer = transformation(2, 2); // The correct one const options = [correctAnswer]; while (options.length < 4) { // 4 options total const wrong = generateWrongOption(transformation); if (!options.some(opt => JSON.stringify(opt) === JSON.stringify(wrong))) { options.push(wrong); } } options.sort(() => Math.random() - 0.5); // Shuffle options.forEach((opt, index) => { const optionDiv = document.createElement('div'); optionDiv.className = 'option'; optionDiv.innerHTML = `Option ${index + 1}`; const canvas = document.createElement('canvas'); canvas.width = 100; canvas.height = 100; const ctx = canvas.getContext('2d'); drawShape(ctx, opt); optionDiv.appendChild(canvas); optionDiv.onclick = () => selectOption(index, opt); optionsDiv.appendChild(optionDiv); }); document.getElementById('submitBtn').style.display = 'block'; // Show submit button } function drawShape(ctx, shape) { ctx.clearRect(0, 0, 100, 100); if (shape.type === 'circle') { ctx.beginPath(); ctx.arc(50, 50, shape.radius, 0, 2 * Math.PI); if (shape.color) ctx.fillStyle = shape.color; ctx.fill(); } else if (shape.type === 'square') { ctx.save(); ctx.translate(50, 50); // Center ctx.rotate(shape.rotation * Math.PI / 180); ctx.fillStyle = 'blue'; ctx.fillRect(-shape.size / 2, -shape.size / 2, shape.size, shape.size); ctx.restore(); } } function generateWrongOption(transformation) { const difficulty = document.getElementById('difficulty').value; if (difficulty === 'easy') { return { type: 'circle', radius: transformation(2, 2).radius + 5 }; // Slightly off } else if (difficulty === 'medium') { return { type: 'square', size: transformation(2, 2).size, rotation: transformation(2, 2).rotation + 45 }; } else { return { type: 'circle', radius: transformation(2, 2).radius - 5, color: 'red' }; // Altered } } function selectOption(index, option) { // For simplicity, just store the selected one when submitting sessionStorage.setItem('selectedOption', JSON.stringify(option)); } function checkAnswer() { const selected = JSON.parse(sessionStorage.getItem('selectedOption')); if (JSON.stringify(selected) === JSON.stringify(correctAnswer)) { score++; document.getElementById('feedback').textContent = 'Correct!'; document.getElementById('score').textContent = score; } else { document.getElementById('feedback').textContent = 'Incorrect!'; } currentQuestion++; if (currentQuestion < totalQuestions) { setTimeout(generateQuestion, 1000); // Next question } else { endTest(); } } function endTest() { const iqEstimate = 100 + (score / totalQuestions) * 50; // Arbitrary formula document.getElementById('iqResult').textContent = `Test Complete! Your approximate IQ estimate: ${Math.round(iqEstimate)}`; document.getElementById('endBtn').style.display = 'none'; // Hide if needed } </script> </body> </html> ```