// Dataset browser (adapted from anon-submission-2026/docs/viewer.js) // Wrapped in lazy-init so it only loads when the Dataset Browser tab is opened. let _viewerInitialized = false; // Base URL for images hosted in the HF dataset repo const IMAGE_BASE_URL = "https://huggingface.co/datasets/jpeper/LudoBench_test/resolve/main/images"; // ----------------------- // Manifest loader // ----------------------- async function loadManifest() { const res = await fetch("manifest.json"); if (!res.ok) throw new Error("Failed to load manifest: " + res.status); const manifest = await res.json(); return manifest.files; } // ----------------------- // Folder handling // ----------------------- function buildFolderIndex(files) { const set = new Set(); for (const f of files) set.add(f.folder); return Array.from(set).sort(); } function populateFolderSelect(folders) { const sel = document.getElementById("folderSelect"); sel.innerHTML = ""; for (const f of folders) { const opt = document.createElement("option"); opt.textContent = f; opt.value = f; sel.appendChild(opt); } } function filterFiles(files, folder) { return files.filter(f => f.folder === folder); } function populateFileSelect(files) { const sel = document.getElementById("fileSelect"); sel.innerHTML = ""; for (const f of files) { const opt = document.createElement("option"); opt.value = f.json_path; opt.textContent = f.name; sel.appendChild(opt); } } // ----------------------- // Load one JSON annotation // ----------------------- async function loadJson(path) { const res = await fetch(path); if (!res.ok) throw new Error("Error loading " + path); return await res.json(); } // ----------------------- // Rendering question + answer // ----------------------- function normalizedAnswers(raw) { return new Set( String(raw || "") .split(/,|\/|\bor\b/i) .map(s => s.trim().toLowerCase()) .filter(Boolean) ); } function isNumber(s) { return s !== "" && !Number.isNaN(Number(s)); } function renderQuestion(data) { const card = document.getElementById("questionCard"); card.innerHTML = `
Question:
${data.Question}
`; const info = document.getElementById("answerInfo"); info.textContent = ""; info.className = "answer-info"; } function attachAnswerLogic(data) { const raw = String(data.Answer || "").trim(); const accepted = normalizedAnswers(raw); const input = document.getElementById("answerInput"); const button = document.getElementById("checkButton"); const info = document.getElementById("answerInfo"); const sol = document.getElementById("solutionText"); sol.innerHTML = `Expected: ${raw || "\u2014"}`; button.onclick = () => { const user = input.value.trim().toLowerCase(); let ok = false; if (accepted.has(user)) ok = true; else if (isNumber(user)) { for (const a of accepted) if (isNumber(a) && Math.abs(Number(a) - Number(user)) < 1e-9) ok = true; } info.textContent = ok ? "Correct!" : "Not quite. Try again."; info.className = "answer-info " + (ok ? "correct" : "wrong"); }; } // ----------------------- // Render images (local paths) // ----------------------- function renderImage(data) { const container = document.getElementById("imageContainer"); const multi = document.getElementById("multiImages"); multi.innerHTML = ""; container.classList.add("hidden"); let urls = data.game_state_url; if (!urls) return; if (!Array.isArray(urls)) urls = [urls]; const folder = data.Game.toLowerCase().replace(/\s+/g, "_"); urls.forEach(url => { const file = url.split("/").pop(); const localPath = `${IMAGE_BASE_URL}/${folder}/${file}`; const block = document.createElement("div"); block.className = "multi-img-block"; const spinner = document.createElement("div"); spinner.className = "spinner"; block.appendChild(spinner); const img = document.createElement("img"); img.style.display = "none"; img.src = localPath; img.onload = () => { spinner.style.display = "none"; img.style.display = "block"; }; img.onerror = () => { spinner.style.display = "none"; const err = document.createElement("div"); err.textContent = "Failed to load " + localPath; err.style.color = "#d44"; block.appendChild(err); }; const link = document.createElement("a"); link.href = localPath; link.target = "_blank"; link.rel = "noopener noreferrer"; link.appendChild(img); block.appendChild(link); const caption = document.createElement("div"); caption.className = "multi-img-caption"; caption.textContent = file; block.appendChild(caption); const full = document.createElement("a"); full.href = localPath; full.target = "_blank"; full.rel = "noopener noreferrer"; full.className = "full-img-link"; full.textContent = "View full image"; block.appendChild(full); multi.appendChild(block); }); container.classList.remove("hidden"); } // ----------------------- // Navigation (prev / next) // ----------------------- let GLOBAL_FILES = []; let GLOBAL_CURRENT_FOLDER = ""; let loadAndRenderRef = null; function goRelative(offset) { const fileSelect = document.getElementById("fileSelect"); const options = Array.from(fileSelect.options); if (options.length === 0) return; const values = options.map(o => o.value); const current = fileSelect.value; let idx = values.indexOf(current); if (idx === -1) return; let next = idx + offset; if (next < 0) next = values.length - 1; if (next >= values.length) next = 0; fileSelect.value = values[next]; if (loadAndRenderRef) loadAndRenderRef(values[next]); } // ----------------------- // Lazy initialization // ----------------------- async function initViewer() { if (_viewerInitialized) return; _viewerInitialized = true; document.getElementById("prevBtn").onclick = () => goRelative(-1); document.getElementById("nextBtn").onclick = () => goRelative(1); const questionCard = document.getElementById("questionCard"); try { const files = await loadManifest(); GLOBAL_FILES = files; const folders = buildFolderIndex(files); populateFolderSelect(folders); const folderSel = document.getElementById("folderSelect"); const fileSel = document.getElementById("fileSelect"); async function loadAndRender(path) { const data = await loadJson(path); renderQuestion(data); attachAnswerLogic(data); renderImage(data); } loadAndRenderRef = loadAndRender; function refresh() { GLOBAL_CURRENT_FOLDER = folderSel.value; const filtered = filterFiles(files, GLOBAL_CURRENT_FOLDER); populateFileSelect(filtered); if (filtered.length > 0) loadAndRender(filtered[0].json_path); } folderSel.onchange = refresh; fileSel.onchange = () => loadAndRender(fileSel.value); folderSel.value = "kingdomino_tier1"; refresh(); } catch (err) { console.error(err); questionCard.innerHTML = `Init error: ${err}
`; } }