| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>Classification Tic Tac Toe</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| </head> |
| <body class="bg-gradient-to-br from-green-50 to-teal-100 flex items-center justify-center h-screen"> |
| <div class="max-w-5xl w-full bg-white rounded-2xl shadow-lg p-6"> |
| <h1 class="text-3xl font-extrabold mb-4 text-center text-teal-800">Classification Tic Tac Toe</h1> |
| <p class="text-center mb-4 text-sm text-gray-600"> |
| Reference: <a href="https://www.linkedin.com/pulse/generative-ai-action-building-end-to-end-text-pipelines-lively-qur9e" class="text-teal-600 hover:underline" target="_blank">Generative AI Action: Building End-to-End Text Pipelines</a> |
| </p> |
| <div id="banner" class="hidden bg-green-100 border border-green-300 text-green-800 font-bold text-center py-2 rounded mb-4"></div> |
|
|
| <div class="flex items-start justify-center space-x-4"> |
| |
| <div id="board" class="grid grid-cols-3 gap-1 border-4 border-teal-300 rounded-lg p-2 bg-white mx-auto"></div> |
|
|
| |
| <div id="questionPanel" class="w-1/2 bg-gray-50 rounded-lg shadow p-4"> |
| <h2 id="panelQuestion" class="text-xl font-semibold text-gray-800">Select a square to view the question</h2> |
| <ul id="panelChoices" class="mt-4 space-y-2"></ul> |
| <p id="panelHint" class="mt-3 text-sm text-gray-600 hidden"></p> |
| <button id="hintBtn" class="mt-4 px-3 py-1 bg-gray-200 text-gray-700 rounded hover:bg-gray-300">Show Hint</button> |
| </div> |
| </div> |
|
|
| <div class="flex justify-between mt-6"> |
| <p id="status" class="text-lg font-medium text-gray-800"></p> |
| <button id="restartBtn" class="px-4 py-2 bg-teal-600 text-white rounded hover:bg-teal-700">Restart</button> |
| </div> |
| </div> |
|
|
| <script> |
| const questions = [ |
| { |
| question: 'What is the primary difference between Text-to-Label and Text-to-Text tasks?', |
| choices: [ |
| 'They both generate text', |
| 'Text-to-Label assigns categories; Text-to-Text generates new text', |
| 'Text-to-Label uses images; Text-to-Text uses audio', |
| 'They require the same prompt structure' |
| ], |
| answer: 2, |
| hint: 'One is classification, the other is generation.' |
| }, |
| { |
| question: 'Which metric quantifies the harmonic mean of precision and recall?', |
| choices: ['Accuracy', 'F1 Score', 'Precision', 'Recall'], |
| answer: 2, |
| hint: 'It balances false positives and false negatives.' |
| }, |
| { |
| question: 'What summarization method selects important sentences directly from the source?', |
| choices: ['Abstractive', 'Extractive', 'Generative', 'Reductive'], |
| answer: 2, |
| hint: 'It works by splicing source sentences.' |
| }, |
| { |
| question: 'Which classification approach assigns categories without any task-specific training data?', |
| choices: ['Few-shot', 'Zero-shot', 'Fine-tuning', 'Reinforcement learning'], |
| answer: 2, |
| hint: 'No labeled examples are provided.' |
| }, |
| { |
| question: 'In Aspect-Based Sentiment Classification, what does ABSC pinpoint?', |
| choices: [ |
| 'Overall sentiment', |
| 'Sentiment on specific aspects', |
| 'Grammar errors', |
| 'Translation quality' |
| ], |
| answer: 2, |
| hint: 'It isolates sentiment for individual topics.' |
| }, |
| { |
| question: 'Which library is used to score abstractive summaries with ROUGE-L?', |
| choices: ['nltk', 'sklearn', 'rouge_score', 'transformers'], |
| answer: 3, |
| hint: 'Its name starts with "rouge".' |
| }, |
| { |
| question: 'What is a common pitfall when relying solely on automated metrics?', |
| choices: [ |
| 'Missing edge cases', |
| 'Overfitting data', |
| 'Slow inference', |
| 'High cost' |
| ], |
| answer: 1, |
| hint: 'You might overlook unusual inputs.' |
| }, |
| { |
| question: 'What practice helps detect model drift over time?', |
| choices: [ |
| 'Data augmentation', |
| 'Ongoing monitoring', |
| 'One-time evaluation', |
| 'Hyperparameter tuning' |
| ], |
| answer: 2, |
| hint: 'It involves continuous checks.' |
| }, |
| { |
| question: 'In hands-on exercises, which tool is suggested for building a real-time aspect-sentiment dashboard?', |
| choices: ['Flask', 'Streamlit', 'Django', 'Dash'], |
| answer: 2, |
| hint: 'A Python app framework with instant reload.' |
| } |
| ]; |
| |
| let currentPlayer, boardState, cellQuestions, selectedCell; |
| const boardEl = document.getElementById('board'); |
| const statusEl = document.getElementById('status'); |
| const bannerEl = document.getElementById('banner'); |
| const hintBtn = document.getElementById('hintBtn'); |
| const panelQuestion = document.getElementById('panelQuestion'); |
| const panelChoices = document.getElementById('panelChoices'); |
| const panelHint = document.getElementById('panelHint'); |
| const restartBtn = document.getElementById('restartBtn'); |
| |
| |
| function shuffle(arr) { |
| for (let i = arr.length - 1; i > 0; i--) { |
| const j = Math.floor(Math.random() * (i + 1)); |
| [arr[i], arr[j]] = [arr[j], arr[i]]; |
| } |
| return arr; |
| } |
| |
| |
| function buildBalancedPositions(nQuestions, kChoices) { |
| const base = Math.floor(nQuestions / kChoices); |
| const remainder = nQuestions % kChoices; |
| const slots = []; |
| for (let pos = 1; pos <= kChoices; pos++) { |
| for (let c = 0; c < base; c++) slots.push(pos); |
| } |
| |
| const extra = shuffle(Array.from({length: kChoices}, (_, i) => i + 1)).slice(0, remainder); |
| slots.push(...extra); |
| return shuffle(slots); |
| } |
| |
| function initGame() { |
| currentPlayer = 'X'; |
| boardState = Array(9).fill(''); |
| bannerEl.classList.add('hidden'); |
| statusEl.innerText = ''; |
| |
| |
| const shuffledQs = shuffle([...questions]).slice(0, 9); |
| |
| |
| const kChoices = 4; |
| const targetPositions = buildBalancedPositions(shuffledQs.length, kChoices); |
| |
| |
| |
| cellQuestions = shuffledQs.map((q, idx) => { |
| const originalCorrectIdx0 = q.answer - 1; |
| const correctText = q.choices[originalCorrectIdx0]; |
| const wrongs = q.choices.filter((_, i) => i !== originalCorrectIdx0); |
| shuffle(wrongs); |
| |
| const targetPos1 = targetPositions[idx]; |
| const targetPos0 = targetPos1 - 1; |
| const newChoices = Array(kChoices).fill(null); |
| newChoices[targetPos0] = correctText; |
| |
| |
| let w = 0; |
| for (let i = 0; i < kChoices; i++) { |
| if (newChoices[i] === null) { |
| newChoices[i] = wrongs[w++]; |
| } |
| } |
| |
| return { |
| question: q.question, |
| choices: newChoices, |
| answer: targetPos1, |
| hint: q.hint |
| }; |
| }); |
| |
| panelQuestion.innerText = 'Select a square to view the question'; |
| panelChoices.innerHTML = ''; |
| panelHint.classList.add('hidden'); |
| renderBoard(); |
| } |
| |
| function renderBoard() { |
| boardEl.innerHTML = ''; |
| boardState.forEach((mark, idx) => { |
| const btn = document.createElement('button'); |
| let cls = 'bg-white h-24 w-24 flex items-center justify-center text-2xl font-bold rounded-lg shadow hover:bg-gray-100'; |
| if (mark === 'X') cls += ' text-blue-600'; |
| if (mark === 'O') cls += ' text-red-600'; |
| btn.className = cls; |
| btn.innerText = mark; |
| btn.disabled = mark !== ''; |
| btn.addEventListener('click', () => openQuestion(idx)); |
| boardEl.appendChild(btn); |
| }); |
| statusEl.innerText = `Current: ${currentPlayer}`; |
| } |
| |
| function openQuestion(idx) { |
| selectedCell = idx; |
| const q = cellQuestions[idx]; |
| panelQuestion.innerText = q.question; |
| panelChoices.innerHTML = ''; |
| panelHint.classList.add('hidden'); |
| panelHint.innerText = q.hint; |
| q.choices.forEach((c, i) => { |
| const li = document.createElement('li'); |
| const btn = document.createElement('button'); |
| btn.innerText = c; |
| btn.className = 'w-full text-left px-4 py-2 bg-teal-50 rounded hover:bg-teal-100'; |
| btn.addEventListener('click', () => handleAnswer(i + 1)); |
| li.appendChild(btn); |
| panelChoices.appendChild(li); |
| }); |
| } |
| |
| function handleAnswer(choice) { |
| const q = cellQuestions[selectedCell]; |
| if (choice === q.answer) { |
| boardState[selectedCell] = currentPlayer; |
| renderBoard(); |
| if (checkWin()) return endGame(`${currentPlayer} wins!`); |
| if (boardState.every(c => c)) return endGame('Stalemate!'); |
| } else { |
| alert('Incorrect! Turn missed.'); |
| } |
| currentPlayer = currentPlayer === 'X' ? 'O' : 'X'; |
| statusEl.innerText = `Current: ${currentPlayer}`; |
| } |
| |
| function checkWin() { |
| const wins = [ |
| [0,1,2],[3,4,5],[6,7,8], |
| [0,3,6],[1,4,7],[2,5,8], |
| [0,4,8],[2,4,6] |
| ]; |
| return wins.some(combo => combo.every(i => boardState[i] === currentPlayer)); |
| } |
| |
| function endGame(msg) { |
| bannerEl.innerText = `🎉 ${msg}`; |
| bannerEl.classList.remove('hidden'); |
| document.querySelectorAll('#board button').forEach(b => b.disabled = true); |
| } |
| |
| hintBtn.addEventListener('click', () => panelHint.classList.toggle('hidden')); |
| restartBtn.addEventListener('click', initGame); |
| initGame(); |
| </script> |
| </body> |
| </html> |
|
|