| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| <title>Evaluating Agentic Workflows</title> |
| <style> |
| :root { |
| --navy: #102a43; |
| --blue: #ea4b71; |
| --blue-soft: #ffe4ec; |
| --cyan: #ff6d5a; |
| --teal: #b9385d; |
| --red: #dc2626; |
| --ink: #243b53; |
| --muted: #627d98; |
| --line: #bcccdc; |
| --panel: #ffffff; |
| --soft: #f0f7ff; |
| --success: #166534; |
| --success-bg: #dcfce7; |
| --warning: #92400e; |
| --warning-bg: #fef3c7; |
| } |
| * { box-sizing: border-box; } |
| body { |
| margin: 0; |
| min-height: 100vh; |
| padding: 24px; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; |
| color: var(--ink); |
| background: |
| radial-gradient(circle at 15% 15%, rgba(37, 99, 235, 0.16), transparent 32%), |
| radial-gradient(circle at 85% 85%, rgba(15, 118, 110, 0.15), transparent 34%), |
| linear-gradient(135deg, #fff3f6 0%, #ecfeff 52%, #f0fdfa 100%); |
| } |
| .game-shell { |
| width: min(1120px, 100%); |
| padding: 28px; |
| border: 1px solid rgba(188, 204, 220, 0.8); |
| border-radius: 24px; |
| background: rgba(255, 255, 255, 0.96); |
| box-shadow: 0 24px 70px rgba(16, 42, 67, 0.18); |
| } |
| h1 { |
| margin: 0; |
| text-align: center; |
| color: var(--navy); |
| font-size: clamp(2rem, 4vw, 3.25rem); |
| line-height: 1.05; |
| letter-spacing: -0.04em; |
| } |
| .subtitle { |
| max-width: 760px; |
| margin: 12px auto 5px; |
| text-align: center; |
| color: var(--muted); |
| font-size: 1rem; |
| } |
| .topics { |
| margin: 0 auto 22px; |
| text-align: center; |
| color: var(--teal); |
| font-size: 0.88rem; |
| font-weight: 750; |
| letter-spacing: 0.03em; |
| } |
| .banner { |
| display: none; |
| margin-bottom: 18px; |
| padding: 13px 16px; |
| border: 1px solid #86efac; |
| border-radius: 12px; |
| color: var(--success); |
| background: var(--success-bg); |
| text-align: center; |
| font-weight: 800; |
| } |
| .banner.show { display: block; } |
| .game-layout { |
| display: grid; |
| grid-template-columns: minmax(315px, 0.88fr) minmax(390px, 1.12fr); |
| gap: 24px; |
| align-items: start; |
| } |
| .board-wrap, |
| .question-panel { |
| border: 1px solid var(--line); |
| border-radius: 18px; |
| background: var(--panel); |
| box-shadow: 0 10px 30px rgba(16, 42, 67, 0.08); |
| } |
| .board-wrap { padding: 18px; } |
| .turn-pill { |
| display: flex; |
| align-items: center; |
| justify-content: space-between; |
| gap: 12px; |
| margin-bottom: 14px; |
| padding: 10px 12px; |
| border-radius: 12px; |
| background: var(--soft); |
| color: var(--navy); |
| font-weight: 750; |
| } |
| .turn-mark { |
| display: inline-grid; |
| place-items: center; |
| width: 34px; |
| height: 34px; |
| border-radius: 10px; |
| color: #fff; |
| background: var(--blue); |
| font-size: 1.2rem; |
| font-weight: 900; |
| } |
| .turn-mark.o { background: var(--red); } |
| .board { |
| display: grid; |
| grid-template-columns: repeat(3, 1fr); |
| gap: 10px; |
| padding: 12px; |
| border: 3px solid #f7a8ba; |
| border-radius: 16px; |
| background: #fff3f6; |
| } |
| .cell { |
| aspect-ratio: 1; |
| min-width: 0; |
| border: 1px solid #f8c4d0; |
| border-radius: 14px; |
| background: #fff; |
| color: var(--blue); |
| box-shadow: 0 5px 14px rgba(37, 99, 235, 0.09); |
| cursor: pointer; |
| font-size: clamp(2.5rem, 7vw, 4rem); |
| font-weight: 900; |
| transition: transform 0.15s ease, background 0.15s ease, box-shadow 0.15s ease; |
| } |
| .cell:hover:not(:disabled), |
| .cell:focus-visible:not(:disabled) { |
| transform: translateY(-2px); |
| background: #ffe4ec; |
| box-shadow: 0 8px 18px rgba(37, 99, 235, 0.18); |
| outline: 3px solid rgba(37, 99, 235, 0.25); |
| outline-offset: 2px; |
| } |
| .cell.o { color: var(--red); } |
| .cell:disabled { cursor: default; } |
| .question-panel { |
| min-height: 455px; |
| padding: 24px; |
| display: flex; |
| flex-direction: column; |
| } |
| .question-label { |
| margin: 0 0 8px; |
| color: var(--cyan); |
| font-size: 0.8rem; |
| font-weight: 850; |
| letter-spacing: 0.12em; |
| text-transform: uppercase; |
| } |
| .question { |
| margin: 0; |
| color: var(--navy); |
| font-size: clamp(1.25rem, 2.6vw, 1.75rem); |
| line-height: 1.27; |
| } |
| .choices { |
| display: grid; |
| gap: 10px; |
| margin: 22px 0 0; |
| padding: 0; |
| list-style: none; |
| } |
| .choice-button { |
| width: 100%; |
| padding: 13px 14px; |
| border: 1px solid #f8c4d0; |
| border-radius: 12px; |
| background: #f8fbff; |
| color: var(--ink); |
| cursor: pointer; |
| text-align: left; |
| font-size: 0.98rem; |
| line-height: 1.35; |
| transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease; |
| } |
| .choice-button:hover, |
| .choice-button:focus-visible { |
| transform: translateX(2px); |
| border-color: #ea4b71; |
| background: #fff0f4; |
| outline: none; |
| } |
| .choice-letter { |
| display: inline-grid; |
| place-items: center; |
| width: 27px; |
| height: 27px; |
| margin-right: 8px; |
| border-radius: 8px; |
| background: var(--blue-soft); |
| color: var(--blue); |
| font-weight: 850; |
| } |
| .hint { |
| display: none; |
| margin: 16px 0 0; |
| padding: 12px 14px; |
| border-left: 4px solid #f59e0b; |
| border-radius: 8px; |
| color: var(--warning); |
| background: var(--warning-bg); |
| font-size: 0.92rem; |
| line-height: 1.45; |
| } |
| .hint.show { display: block; } |
| .panel-actions { |
| margin-top: auto; |
| padding-top: 18px; |
| display: flex; |
| justify-content: flex-end; |
| } |
| button.secondary, |
| button.primary { |
| border: 0; |
| border-radius: 11px; |
| cursor: pointer; |
| font-weight: 800; |
| transition: transform 0.15s ease, filter 0.15s ease; |
| } |
| button.secondary { |
| padding: 10px 13px; |
| color: var(--navy); |
| background: #e6eef7; |
| } |
| button.primary { |
| padding: 11px 17px; |
| color: #fff; |
| background: linear-gradient(135deg, var(--blue), var(--cyan)); |
| box-shadow: 0 8px 18px rgba(37, 99, 235, 0.22); |
| } |
| button.secondary:hover, |
| button.primary:hover { transform: translateY(-1px); filter: brightness(0.98); } |
| .footer-bar { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| gap: 15px; |
| margin-top: 20px; |
| } |
| .status { |
| margin: 0; |
| color: var(--muted); |
| font-size: 0.96rem; |
| font-weight: 700; |
| } |
| @media (max-width: 840px) { |
| body { padding: 12px; } |
| .game-shell { padding: 18px; border-radius: 18px; } |
| .game-layout { grid-template-columns: 1fr; } |
| .board-wrap { max-width: 500px; width: 100%; margin: 0 auto; } |
| .question-panel { min-height: 410px; } |
| } |
| @media (max-width: 480px) { |
| .footer-bar { align-items: stretch; flex-direction: column; } |
| button.primary { width: 100%; } |
| .question-panel { padding: 18px; } |
| .topics { font-size: 0.77rem; } |
| } |
| </style> |
| </head> |
| <body> |
| <main class="game-shell"> |
| <h1>Evaluating Agentic Workflows</h1> |
| <p class="subtitle">Answer correctly to claim a square. An incorrect response passes the turn and leaves the square open.</p> |
| <p class="topics">REASONING β’ ACTION β’ EXECUTION β’ RELIABILITY β’ VERIFICATION β’ PRODUCTION READINESS</p> |
|
|
| <div id="banner" class="banner" role="status" aria-live="polite"></div> |
|
|
| <section class="game-layout" aria-label="Evaluating Agentic Workflows game"> |
| <div class="board-wrap"> |
| <div class="turn-pill"> |
| <span id="turnText">Player X's turn</span> |
| <span id="turnMark" class="turn-mark" aria-hidden="true">X</span> |
| </div> |
| <div id="board" class="board" role="grid" aria-label="Tic tac toe board"></div> |
| </div> |
|
|
| <div class="question-panel"> |
| <p class="question-label">Knowledge Challenge</p> |
| <h2 id="panelQuestion" class="question">Select an open square to view a question.</h2> |
| <ul id="panelChoices" class="choices"></ul> |
| <p id="panelHint" class="hint"></p> |
| <div class="panel-actions"> |
| <button id="hintBtn" class="secondary" type="button">Show Hint</button> |
| </div> |
| </div> |
| </section> |
|
|
| <div class="footer-bar"> |
| <p id="status" class="status">A fresh set of nine questions is selected for every game.</p> |
| <button id="restartBtn" class="primary" type="button">Restart Game</button> |
| </div> |
| </main> |
|
|
| <script> |
| const questions = [ |
| { |
| question: "Why do standard metrics such as ROUGE and BLEU often fail for autonomous agents?", |
| choices: ["They focus on single-turn string similarity rather than stateful trajectories", "They require too much human review", "They can evaluate only tool arguments", "They always favor the first response"], |
| answer: 1, |
| hint: "Agents make sequences of decisions whose errors can cascade." |
| }, |
| { |
| question: "Which evaluation layer asks whether the agent's strategic plan was sound?", |
| choices: ["Reasoning", "Action", "Execution", "Infrastructure"], |
| answer: 1, |
| hint: "This layer evaluates what the agent thinks." |
| }, |
| { |
| question: "Which evaluation layer measures tool selection and argument correctness?", |
| choices: ["Action", "Reasoning", "Execution", "Presentation"], |
| answer: 1, |
| hint: "This layer evaluates what the agent uses." |
| }, |
| { |
| question: "Which evaluation layer determines whether the user's goal was actually completed?", |
| choices: ["Execution", "Reasoning", "Action", "Extraction"], |
| answer: 1, |
| hint: "This layer evaluates what the workflow achieves." |
| }, |
| { |
| question: "An agent begins with a good plan but gradually departs from it. What should evaluators detect?", |
| choices: ["Trajectory drift", "Position bias", "Chunk overlap", "String mismatch"], |
| answer: 1, |
| hint: "Compare planned and actual steps across the trajectory." |
| }, |
| { |
| question: "Why can a single early agent error be especially damaging?", |
| choices: ["It can corrupt downstream actions in a stateful loop", "It only changes the final wording", "It automatically stops all tool calls", "It affects no later decisions"], |
| answer: 1, |
| hint: "Agent steps are dependent rather than isolated samples." |
| }, |
| { |
| question: "Which metric checks whether the agent chose the correct tool and supplied valid parameters?", |
| choices: ["Tool and argument correctness", "Task completion", "Semantic similarity", "Response length"], |
| answer: 1, |
| hint: "Evaluate both the selected tool and its arguments." |
| }, |
| { |
| question: "Which metric measures unnecessary loops, redundant calls, and excess latency?", |
| choices: ["Step efficiency", "Plan adherence", "Recall", "Judge agreement"], |
| answer: 1, |
| hint: "Compare the path taken with a shorter valid path." |
| }, |
| { |
| question: "What does end-to-end task completion measure?", |
| choices: ["Whether the agent successfully achieved the user's goal", "Whether every response used the same words", "Whether the first tool call was fast", "Whether the plan contained many steps"], |
| answer: 1, |
| hint: "Focus on the final outcome." |
| }, |
| { |
| question: "Where should a failing retriever or tool integration be evaluated to isolate the root cause?", |
| choices: ["Component level", "End-to-end level only", "User-interface level", "Model-training level"], |
| answer: 1, |
| hint: "Test individual units such as retrievers, sub-agents, and tools." |
| }, |
| { |
| question: "What does trajectory-level evaluation inspect?", |
| choices: ["Planning, reasoning steps, tool calls, retries, and execution paths", "Only the final answer", "Only token count", "Only the user prompt"], |
| answer: 1, |
| hint: "Inspect the path, not merely the destination." |
| }, |
| { |
| question: "Which evaluation method is best suited to exact tool correctness?", |
| choices: ["Deterministic checks", "ROUGE alone", "Unstructured human intuition only", "Response-length scoring"], |
| answer: 1, |
| hint: "Tool names and arguments can be checked programmatically." |
| }, |
| { |
| question: "Which method is often appropriate for judging semantic task completion?", |
| choices: ["An LLM-as-a-judge with a clear rubric", "A character-count comparison", "A latency threshold alone", "Random sampling without criteria"], |
| answer: 1, |
| hint: "Meaning and intent require more than word matching." |
| }, |
| { |
| question: "What is a major advantage of LLM-as-a-judge over BLEU or ROUGE?", |
| choices: ["It can evaluate meaning and intent", "It is perfectly deterministic", "It eliminates all bias", "It needs no rubric"], |
| answer: 1, |
| hint: "Semantic evaluation can recognize valid answers with different wording." |
| }, |
| { |
| question: "What is the circularity trap in pure LLM-as-a-judge evaluation?", |
| choices: ["The judge may share the same probabilistic failure modes as the agent", "The judge cannot read natural language", "The judge always rejects long responses", "The judge can evaluate only arithmetic"], |
| answer: 1, |
| hint: "A probabilistic model is judging another probabilistic model." |
| }, |
| { |
| question: "How does a neuro-symbolic evaluation approach reduce judge circularity?", |
| choices: ["It extracts structured artifacts and verifies them with deterministic code", "It asks the same model twice", "It removes ground truth", "It scores only response style"], |
| answer: 1, |
| hint: "Combine neural extraction with symbolic verification." |
| }, |
| { |
| question: "What is the purpose of neural extraction in a neuro-symbolic evaluator?", |
| choices: ["Convert unstructured trajectories into machine-checkable structured data", "Replace all evaluation scripts", "Generate random ground truth", "Shorten every agent response"], |
| answer: 1, |
| hint: "The output may be a JSON artifact that code can inspect." |
| }, |
| { |
| question: "What makes symbolic verification auditable and repeatable?", |
| choices: ["Programmatic checks against immutable ground truth", "A judge's confidence score", "A longer natural-language explanation", "A different prompt on every run"], |
| answer: 1, |
| hint: "The same inputs should produce the same verdict." |
| }, |
| { |
| question: "A judge consistently favors whichever answer appears first. Which mitigation directly tests this bias?", |
| choices: ["Swap response order and average the results", "Increase response length", "Remove the rubric", "Use only one test case"], |
| answer: 1, |
| hint: "Position-consistency checking changes answer order." |
| }, |
| { |
| question: "Why use cross-family model judging?", |
| choices: ["A different model family can reduce self-preference bias", "It guarantees perfect factual accuracy", "It removes the need for testing", "It makes all verdicts deterministic"], |
| answer: 1, |
| hint: "For example, one model family can judge another." |
| }, |
| { |
| question: "How can evaluators avoid rewarding unnecessary verbosity?", |
| choices: ["Use a binary pass/fail rubric for the required outcome", "Award points for every extra sentence", "Ignore task requirements", "Use word count as the only metric"], |
| answer: 1, |
| hint: "Score whether requirements were met, not how long the answer was." |
| }, |
| { |
| question: "A judge and human experts agree on 80% of cases. What does that percentage describe?", |
| choices: ["Agreement with human judgment", "Tool-call precision", "Task completion rate", "Agent recall"], |
| answer: 1, |
| hint: "It compares automated verdicts with human ratings." |
| }, |
| { |
| question: "An agent correctly maps user intent to the right function. Which outcome is this?", |
| choices: ["True positive", "False positive", "False negative", "Trajectory drift"], |
| answer: 1, |
| hint: "The correct action was selected when it was needed." |
| }, |
| { |
| question: "An agent makes an unnecessary tool call that increases cost and latency. Which outcome is this?", |
| choices: ["False positive", "True positive", "False negative", "True negative"], |
| answer: 1, |
| hint: "The workflow acted when it should not have." |
| }, |
| { |
| question: "An overconfident agent skips a needed tool and guesses from memory. Which outcome is this?", |
| choices: ["False negative", "False positive", "True positive", "Position bias"], |
| answer: 1, |
| hint: "A required action was missed." |
| }, |
| { |
| question: "What does tool-selection precision emphasize?", |
| choices: ["Avoiding unnecessary or incorrect tool calls", "Catching every possible needed action regardless of cost", "Matching the final answer word for word", "Making the plan longer"], |
| answer: 1, |
| hint: "Higher precision reduces false positives, latency, and API cost." |
| }, |
| { |
| question: "What does tool-selection recall emphasize?", |
| choices: ["Executing needed tool calls so critical actions are not missed", "Eliminating every redundant action", "Reducing answer length", "Matching a reference string"], |
| answer: 1, |
| hint: "Higher recall reduces false negatives." |
| }, |
| { |
| question: "Why create a gold-standard trajectory dataset?", |
| choices: ["To provide human-mapped ideal tool selections and paths as an evaluation baseline", "To train every model from scratch", "To replace all production traces", "To guarantee zero failures"], |
| answer: 1, |
| hint: "Ideal logged trajectories anchor automated evaluation." |
| }, |
| { |
| question: "A production agent has 60% tool-selection precision. What does this most strongly suggest?", |
| choices: ["Too many selected actions are unnecessary or wrong", "The agent misses exactly 40% of required actions", "The final answers are 60% correct", "The judge agrees with humans 60% of the time"], |
| answer: 1, |
| hint: "Precision evaluates the quality of actions the agent chose." |
| }, |
| { |
| question: "Which reasoning-layer metric compares the initial plan with the actual execution?", |
| choices: ["Plan adherence", "Argument correctness", "End-to-end efficiency", "Task completion"], |
| answer: 1, |
| hint: "Look for consistency between intended and executed steps." |
| }, |
| { |
| question: "Which safety metric tests resistance to malicious instructions embedded in documents or messages?", |
| choices: ["Injection resilience", "Plan adherence", "Step efficiency", "Position consistency"], |
| answer: 1, |
| hint: "Adversarial input should not redirect the agent improperly." |
| }, |
| { |
| question: "What does argument correctness evaluate?", |
| choices: ["Whether parameter values extracted from context are accurate", "Whether the plan is creative", "Whether the response is concise", "Whether the model is from another family"], |
| answer: 1, |
| hint: "Check the values passed into the tool call." |
| }, |
| { |
| question: "What should execution-path evaluation verify?", |
| choices: ["Correct arguments and valid, non-looping paths", "Only polished prose", "Only the number of tools available", "Only model temperature"], |
| answer: 1, |
| hint: "Execution should be correct and should not get stuck." |
| }, |
| { |
| question: "What is the first step in building a production evaluation harness?", |
| choices: ["Define success criteria and representative test cases", "Automate deployment immediately", "Remove all traces", "Choose the longest benchmark"], |
| answer: 1, |
| hint: "Start with ground truth, happy paths, and adversarial scenarios." |
| }, |
| { |
| question: "Why instrument an agentic workflow with tracing?", |
| choices: ["To capture intermediate decisions and locate retrieval or execution failures", "To hide tool calls from evaluators", "To eliminate test cases", "To make outputs deterministic by itself"], |
| answer: 1, |
| hint: "Tracing reveals where in the trajectory a failure occurred." |
| }, |
| { |
| question: "What is the purpose of automated regression gates in CI/CD?", |
| choices: ["Block deployment when quality falls below defined thresholds", "Guarantee that no future bug is possible", "Replace production monitoring", "Increase the number of agent steps"], |
| answer: 1, |
| hint: "New changes should not silently degrade established quality." |
| }, |
| { |
| question: "Manual 'vibe checks' become unreliable at scale primarily because they are:", |
| choices: ["Slow, inconsistent, and high-latency", "Fully deterministic", "Limited to arithmetic", "Unable to display text"], |
| answer: 1, |
| hint: "Human inspection does not scale well across many trajectories." |
| }, |
| { |
| question: "Which evaluation design best supports root-cause analysis?", |
| choices: ["Combine component, trajectory, and end-to-end measurements", "Use only a final satisfaction score", "Inspect one successful run", "Measure latency alone"], |
| answer: 1, |
| hint: "Different levels reveal whether the failure began in a unit, path, or outcome." |
| }, |
| { |
| question: "Why should agent evaluation include both quality and efficiency?", |
| choices: ["A correct outcome may still use costly loops or redundant calls", "Efficiency automatically proves correctness", "Quality matters only during development", "Fast agents cannot hallucinate"], |
| answer: 1, |
| hint: "Production readiness includes outcome quality, latency, and cost." |
| }, |
| { |
| question: "Which evaluation program is strongest for production readiness?", |
| choices: ["Ground truth, tracing, layered metrics, adversarial tests, and regression gates", "One demo reviewed informally", "BLEU scores without trajectory data", "A judge with no rubric or bias checks"], |
| answer: 1, |
| hint: "Reliable evaluation combines evidence across the full workflow lifecycle." |
| } |
| ]; |
| let currentPlayer; |
| let boardState; |
| let cellQuestions; |
| let selectedCell; |
| let gameOver; |
| 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"); |
| const turnText = document.getElementById("turnText"); |
| const turnMark = document.getElementById("turnMark"); |
| function shuffle(array) { |
| for (let i = array.length - 1; i > 0; i -= 1) { |
| const j = Math.floor(Math.random() * (i + 1)); |
| [array[i], array[j]] = [array[j], array[i]]; |
| } |
| return array; |
| } |
| function buildBalancedPositions(questionCount, choiceCount) { |
| const base = Math.floor(questionCount / choiceCount); |
| const remainder = questionCount % choiceCount; |
| const positions = []; |
| for (let position = 1; position <= choiceCount; position += 1) { |
| for (let count = 0; count < base; count += 1) positions.push(position); |
| } |
| const extras = shuffle( |
| Array.from({ length: choiceCount }, (_, index) => index + 1) |
| ).slice(0, remainder); |
| return shuffle(positions.concat(extras)); |
| } |
| function prepareQuestion(question, targetCorrectPosition) { |
| const originalCorrectIndex = question.answer - 1; |
| const correctChoice = question.choices[originalCorrectIndex]; |
| const incorrectChoices = shuffle( |
| question.choices.filter((_, index) => index !== originalCorrectIndex) |
| ); |
| const newChoices = Array(question.choices.length).fill(null); |
| newChoices[targetCorrectPosition - 1] = correctChoice; |
| let wrongIndex = 0; |
| for (let index = 0; index < newChoices.length; index += 1) { |
| if (newChoices[index] === null) { |
| newChoices[index] = incorrectChoices[wrongIndex]; |
| wrongIndex += 1; |
| } |
| } |
| return { |
| question: question.question, |
| choices: newChoices, |
| answer: targetCorrectPosition, |
| hint: question.hint |
| }; |
| } |
| function updateTurnDisplay() { |
| turnText.textContent = `Player ${currentPlayer}'s turn`; |
| turnMark.textContent = currentPlayer; |
| turnMark.classList.toggle("o", currentPlayer === "O"); |
| } |
| function initGame() { |
| currentPlayer = "X"; |
| boardState = Array(9).fill(""); |
| selectedCell = null; |
| gameOver = false; |
| const selectedQuestions = shuffle([...questions]).slice(0, 9); |
| const correctPositions = buildBalancedPositions(9, 4); |
| cellQuestions = selectedQuestions.map((question, index) => |
| prepareQuestion(question, correctPositions[index]) |
| ); |
| bannerEl.classList.remove("show"); |
| bannerEl.textContent = ""; |
| statusEl.textContent = "A fresh set of nine questions is selected for every game."; |
| panelQuestion.textContent = "Select an open square to view a question."; |
| panelChoices.innerHTML = ""; |
| panelHint.classList.remove("show"); |
| panelHint.textContent = ""; |
| hintBtn.textContent = "Show Hint"; |
| updateTurnDisplay(); |
| renderBoard(); |
| } |
| function renderBoard() { |
| boardEl.innerHTML = ""; |
| boardState.forEach((mark, index) => { |
| const button = document.createElement("button"); |
| button.type = "button"; |
| button.className = `cell${mark === "O" ? " o" : ""}`; |
| button.textContent = mark; |
| button.disabled = gameOver || mark !== ""; |
| button.setAttribute("role", "gridcell"); |
| button.setAttribute( |
| "aria-label", |
| mark ? `Square ${index + 1}, claimed by ${mark}` : `Square ${index + 1}, open` |
| ); |
| button.addEventListener("click", () => openQuestion(index)); |
| boardEl.appendChild(button); |
| }); |
| } |
| function openQuestion(index) { |
| if (gameOver || boardState[index] !== "") return; |
| selectedCell = index; |
| const question = cellQuestions[index]; |
| panelQuestion.textContent = question.question; |
| panelChoices.innerHTML = ""; |
| panelHint.textContent = question.hint; |
| panelHint.classList.remove("show"); |
| hintBtn.textContent = "Show Hint"; |
| question.choices.forEach((choice, choiceIndex) => { |
| const listItem = document.createElement("li"); |
| const button = document.createElement("button"); |
| const letter = String.fromCharCode(65 + choiceIndex); |
| button.type = "button"; |
| button.className = "choice-button"; |
| button.innerHTML = `<span class="choice-letter">${letter}</span>${choice}`; |
| button.addEventListener("click", () => handleAnswer(choiceIndex + 1)); |
| listItem.appendChild(button); |
| panelChoices.appendChild(listItem); |
| }); |
| } |
| function handleAnswer(choice) { |
| if (selectedCell === null || gameOver) return; |
| const question = cellQuestions[selectedCell]; |
| const answeringPlayer = currentPlayer; |
| if (choice === question.answer) { |
| boardState[selectedCell] = currentPlayer; |
| selectedCell = null; |
| renderBoard(); |
| if (checkWin(answeringPlayer)) { |
| endGame(`Player ${answeringPlayer} wins!`); |
| return; |
| } |
| if (boardState.every(Boolean)) { |
| endGame("Stalemate β strong match!"); |
| return; |
| } |
| currentPlayer = currentPlayer === "X" ? "O" : "X"; |
| updateTurnDisplay(); |
| panelQuestion.textContent = "Correct β square claimed. Select another open square."; |
| panelChoices.innerHTML = ""; |
| panelHint.classList.remove("show"); |
| panelHint.textContent = ""; |
| statusEl.textContent = `Player ${answeringPlayer} claimed the square. Player ${currentPlayer} is next.`; |
| } else { |
| currentPlayer = currentPlayer === "X" ? "O" : "X"; |
| selectedCell = null; |
| updateTurnDisplay(); |
| panelQuestion.textContent = "Incorrect β the square remains open. The turn passes."; |
| panelChoices.innerHTML = ""; |
| panelHint.classList.remove("show"); |
| panelHint.textContent = ""; |
| statusEl.textContent = `Player ${answeringPlayer} missed. Player ${currentPlayer} may select any open square.`; |
| } |
| } |
| function checkWin(player) { |
| const winningLines = [ |
| [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 winningLines.some(line => line.every(index => boardState[index] === player)); |
| } |
| function endGame(message) { |
| gameOver = true; |
| selectedCell = null; |
| bannerEl.textContent = `π ${message}`; |
| bannerEl.classList.add("show"); |
| panelQuestion.textContent = message; |
| panelChoices.innerHTML = ""; |
| panelHint.classList.remove("show"); |
| panelHint.textContent = ""; |
| statusEl.textContent = "Restart to draw a new set of questions."; |
| renderBoard(); |
| } |
| hintBtn.addEventListener("click", () => { |
| if (selectedCell === null || gameOver) return; |
| panelHint.classList.toggle("show"); |
| hintBtn.textContent = panelHint.classList.contains("show") ? "Hide Hint" : "Show Hint"; |
| }); |
| restartBtn.addEventListener("click", initGame); |
| initGame(); |
| </script> |
| </body> |
| </html> |
|
|