Files
TaroTime/app/ui-quiz-bank.js
2026-03-07 05:17:50 -08:00

947 lines
29 KiB
JavaScript

/* ui-quiz-bank.js — Built-in quiz question bank generation */
(function () {
"use strict";
function toTitleCase(value) {
const text = String(value || "").trim().toLowerCase();
if (!text) {
return "";
}
return text.charAt(0).toUpperCase() + text.slice(1);
}
function normalizeOption(value) {
return String(value || "").trim();
}
function normalizeKey(value) {
return normalizeOption(value).toLowerCase();
}
function toUniqueOptionList(values) {
const seen = new Set();
const unique = [];
(values || []).forEach((value) => {
const formatted = normalizeOption(value);
if (!formatted) {
return;
}
const key = normalizeKey(formatted);
if (seen.has(key)) {
return;
}
seen.add(key);
unique.push(formatted);
});
return unique;
}
function resolveDifficultyValue(valueByDifficulty, difficulty = "normal") {
if (valueByDifficulty == null) {
return "";
}
if (typeof valueByDifficulty !== "object" || Array.isArray(valueByDifficulty)) {
return valueByDifficulty;
}
if (Object.prototype.hasOwnProperty.call(valueByDifficulty, difficulty)) {
return valueByDifficulty[difficulty];
}
if (Object.prototype.hasOwnProperty.call(valueByDifficulty, "normal")) {
return valueByDifficulty.normal;
}
if (Object.prototype.hasOwnProperty.call(valueByDifficulty, "easy")) {
return valueByDifficulty.easy;
}
if (Object.prototype.hasOwnProperty.call(valueByDifficulty, "hard")) {
return valueByDifficulty.hard;
}
return "";
}
function createQuestionTemplate(payload, poolValues) {
const key = String(payload?.key || "").trim();
const promptByDifficulty = payload?.promptByDifficulty ?? payload?.prompt;
const answerByDifficulty = payload?.answerByDifficulty ?? payload?.answer;
const poolByDifficulty = poolValues;
const categoryId = String(payload?.categoryId || "").trim();
const category = String(payload?.category || "Correspondence").trim();
const defaultPrompt = String(resolveDifficultyValue(promptByDifficulty, "normal") || "").trim();
const defaultAnswer = normalizeOption(resolveDifficultyValue(answerByDifficulty, "normal"));
const defaultPool = toUniqueOptionList(resolveDifficultyValue(poolByDifficulty, "normal") || []);
if (!key || !defaultPrompt || !defaultAnswer || !categoryId || !category) {
return null;
}
if (!defaultPool.some((value) => normalizeKey(value) === normalizeKey(defaultAnswer))) {
defaultPool.push(defaultAnswer);
}
const distractorCount = defaultPool.filter((value) => normalizeKey(value) !== normalizeKey(defaultAnswer)).length;
if (distractorCount < 3) {
return null;
}
return {
key,
categoryId,
category,
promptByDifficulty,
answerByDifficulty,
poolByDifficulty
};
}
function buildQuestionBank(referenceData, magickDataset, dynamicCategoryRegistry) {
const grouped = magickDataset?.grouped || {};
const alphabets = grouped.alphabets || {};
const englishLetters = Array.isArray(alphabets?.english) ? alphabets.english : [];
const hebrewLetters = Array.isArray(alphabets?.hebrew) ? alphabets.hebrew : [];
const kabbalahTree = grouped?.kabbalah?.["kabbalah-tree"] || {};
const treePaths = Array.isArray(kabbalahTree?.paths) ? kabbalahTree.paths : [];
const treeSephiroth = Array.isArray(kabbalahTree?.sephiroth) ? kabbalahTree.sephiroth : [];
const sephirotById = grouped?.kabbalah?.sephirot && typeof grouped.kabbalah.sephirot === "object"
? grouped.kabbalah.sephirot
: {};
const cube = grouped?.kabbalah?.cube && typeof grouped.kabbalah.cube === "object"
? grouped.kabbalah.cube
: {};
const cubeWalls = Array.isArray(cube?.walls) ? cube.walls : [];
const cubeEdges = Array.isArray(cube?.edges) ? cube.edges : [];
const cubeCenter = cube?.center && typeof cube.center === "object" ? cube.center : null;
const playingCardsData = grouped?.["playing-cards-52"];
const playingCards = Array.isArray(playingCardsData)
? playingCardsData
: (Array.isArray(playingCardsData?.entries) ? playingCardsData.entries : []);
const signs = Array.isArray(referenceData?.signs) ? referenceData.signs : [];
const planetsById = referenceData?.planets && typeof referenceData.planets === "object"
? referenceData.planets
: {};
const planets = Object.values(planetsById);
const decansBySign = referenceData?.decansBySign && typeof referenceData.decansBySign === "object"
? referenceData.decansBySign
: {};
const normalizeId = (value) => String(value || "").trim().toLowerCase();
const toRomanNumeral = (value) => {
const numeric = Number(value);
if (!Number.isFinite(numeric) || numeric <= 0) {
return String(value || "");
}
const intValue = Math.trunc(numeric);
const lookup = [
[1000, "M"],
[900, "CM"],
[500, "D"],
[400, "CD"],
[100, "C"],
[90, "XC"],
[50, "L"],
[40, "XL"],
[10, "X"],
[9, "IX"],
[5, "V"],
[4, "IV"],
[1, "I"]
];
let current = intValue;
let result = "";
lookup.forEach(([size, symbol]) => {
while (current >= size) {
result += symbol;
current -= size;
}
});
return result || String(intValue);
};
const labelFromId = (value) => {
const id = String(value || "").trim();
if (!id) {
return "";
}
return id
.replace(/[_-]+/g, " ")
.replace(/\s+/g, " ")
.trim()
.split(" ")
.map((part) => part ? part.charAt(0).toUpperCase() + part.slice(1) : "")
.join(" ");
};
const getPlanetLabelById = (planetId) => {
const normalized = normalizeId(planetId);
if (!normalized) {
return "";
}
const directPlanet = planetsById[normalized];
if (directPlanet?.name) {
return directPlanet.name;
}
if (normalized === "primum-mobile") {
return "Primum Mobile";
}
if (normalized === "olam-yesodot") {
return "Earth / Elements";
}
return labelFromId(normalized);
};
const hebrewById = new Map(
hebrewLetters
.filter((entry) => entry?.hebrewLetterId)
.map((entry) => [normalizeId(entry.hebrewLetterId), entry])
);
const formatHebrewLetterLabel = (entry, fallbackId = "") => {
if (entry?.name && entry?.char) {
return `${entry.name} (${entry.char})`;
}
if (entry?.name) {
return entry.name;
}
if (entry?.char) {
return entry.char;
}
return labelFromId(fallbackId);
};
const sephiraNameByNumber = new Map(
treeSephiroth
.filter((entry) => Number.isFinite(Number(entry?.number)) && entry?.name)
.map((entry) => [Math.trunc(Number(entry.number)), String(entry.name)])
);
const sephiraNameById = new Map(
treeSephiroth
.filter((entry) => entry?.sephiraId && entry?.name)
.map((entry) => [normalizeId(entry.sephiraId), String(entry.name)])
);
const getSephiraName = (numberValue, idValue) => {
const numberKey = Number(numberValue);
if (Number.isFinite(numberKey)) {
const byNumber = sephiraNameByNumber.get(Math.trunc(numberKey));
if (byNumber) {
return byNumber;
}
}
const byId = sephiraNameById.get(normalizeId(idValue));
if (byId) {
return byId;
}
if (Number.isFinite(numberKey)) {
return `Sephira ${Math.trunc(numberKey)}`;
}
return labelFromId(idValue);
};
const formatPathLetter = (path) => {
const transliteration = String(path?.hebrewLetter?.transliteration || "").trim();
const glyph = String(path?.hebrewLetter?.char || "").trim();
if (transliteration && glyph) {
return `${transliteration} (${glyph})`;
}
if (transliteration) {
return transliteration;
}
if (glyph) {
return glyph;
}
return "";
};
const flattenDecans = Object.values(decansBySign)
.flatMap((entries) => (Array.isArray(entries) ? entries : []));
const signNameById = new Map(
signs
.filter((entry) => entry?.id && entry?.name)
.map((entry) => [normalizeId(entry.id), String(entry.name)])
);
const formatDecanLabel = (decan) => {
const signName = signNameById.get(normalizeId(decan?.signId)) || labelFromId(decan?.signId);
const index = Number(decan?.index);
if (!signName || !Number.isFinite(index)) {
return "";
}
return `${signName} Decan ${toRomanNumeral(index)}`;
};
const bank = [];
const englishGematriaPool = englishLetters
.map((item) => (Number.isFinite(Number(item?.pythagorean)) ? String(item.pythagorean) : ""))
.filter(Boolean);
const hebrewNumerologyPool = hebrewLetters
.map((item) => (Number.isFinite(Number(item?.numerology)) ? String(item.numerology) : ""))
.filter(Boolean);
const hebrewNameAndCharPool = hebrewLetters
.filter((item) => item?.name && item?.char)
.map((item) => `${item.name} (${item.char})`);
const hebrewCharPool = hebrewLetters
.map((item) => item?.char)
.filter(Boolean);
const planetNamePool = planets
.map((planet) => planet?.name)
.filter(Boolean);
const planetWeekdayPool = planets
.map((planet) => planet?.weekday)
.filter(Boolean);
const zodiacElementPool = signs
.map((sign) => toTitleCase(sign?.element))
.filter(Boolean);
const zodiacTarotPool = signs
.map((sign) => sign?.tarot?.majorArcana)
.filter(Boolean);
const pathNumberPool = toUniqueOptionList(
treePaths
.map((path) => {
const pathNo = Number(path?.pathNumber);
return Number.isFinite(pathNo) ? String(Math.trunc(pathNo)) : "";
})
);
const pathLetterPool = toUniqueOptionList(treePaths.map((path) => formatPathLetter(path)));
const pathTarotPool = toUniqueOptionList(treePaths.map((path) => normalizeOption(path?.tarot?.card)));
const decanLabelPool = toUniqueOptionList(flattenDecans.map((decan) => formatDecanLabel(decan)));
const decanRulerPool = toUniqueOptionList(
flattenDecans.map((decan) => getPlanetLabelById(decan?.rulerPlanetId))
);
const cubeWallLabelPool = toUniqueOptionList(
cubeWalls.map((wall) => `${String(wall?.name || labelFromId(wall?.id)).trim()} Wall`)
);
const cubeEdgeLabelPool = toUniqueOptionList(
cubeEdges.map((edge) => `${String(edge?.name || labelFromId(edge?.id)).trim()} Edge`)
);
const cubeLocationPool = toUniqueOptionList([
...cubeWallLabelPool,
...cubeEdgeLabelPool,
"Center"
]);
const cubeHebrewLetterPool = toUniqueOptionList([
...cubeWalls.map((wall) => {
const hebrew = hebrewById.get(normalizeId(wall?.hebrewLetterId));
return formatHebrewLetterLabel(hebrew, wall?.hebrewLetterId);
}),
...cubeEdges.map((edge) => {
const hebrew = hebrewById.get(normalizeId(edge?.hebrewLetterId));
return formatHebrewLetterLabel(hebrew, edge?.hebrewLetterId);
}),
formatHebrewLetterLabel(hebrewById.get(normalizeId(cubeCenter?.hebrewLetterId)), cubeCenter?.hebrewLetterId)
]);
const playingTarotPool = toUniqueOptionList(
playingCards.map((entry) => normalizeOption(entry?.tarotCard))
);
englishLetters.forEach((entry) => {
if (!entry?.letter || !Number.isFinite(Number(entry?.pythagorean))) {
return;
}
const template = createQuestionTemplate(
{
key: `english-gematria:${entry.letter}`,
categoryId: "english-gematria",
category: "English Gematria",
promptByDifficulty: `${entry.letter} has a simple gematria value of`,
answerByDifficulty: String(entry.pythagorean)
},
englishGematriaPool
);
if (template) {
bank.push(template);
}
});
hebrewLetters.forEach((entry) => {
if (!entry?.name || !entry?.char || !Number.isFinite(Number(entry?.numerology))) {
return;
}
const template = createQuestionTemplate(
{
key: `hebrew-number:${entry.hebrewLetterId || entry.name}`,
categoryId: "hebrew-numerology",
category: "Hebrew Gematria",
promptByDifficulty: {
easy: `${entry.name} (${entry.char}) has a gematria value of`,
normal: `${entry.name} (${entry.char}) has a gematria value of`,
hard: `${entry.char} has a gematria value of`
},
answerByDifficulty: String(entry.numerology)
},
hebrewNumerologyPool
);
if (template) {
bank.push(template);
}
});
englishLetters.forEach((entry) => {
if (!entry?.letter || !entry?.hebrewLetterId) {
return;
}
const mappedHebrew = hebrewById.get(normalizeId(entry.hebrewLetterId));
if (!mappedHebrew?.name || !mappedHebrew?.char) {
return;
}
const template = createQuestionTemplate(
{
key: `english-hebrew:${entry.letter}`,
categoryId: "english-hebrew-mapping",
category: "Alphabet Mapping",
promptByDifficulty: {
easy: `${entry.letter} maps to which Hebrew letter`,
normal: `${entry.letter} maps to which Hebrew letter`,
hard: `${entry.letter} maps to which Hebrew glyph`
},
answerByDifficulty: {
easy: `${mappedHebrew.name} (${mappedHebrew.char})`,
normal: `${mappedHebrew.name} (${mappedHebrew.char})`,
hard: mappedHebrew.char
}
},
{
easy: hebrewNameAndCharPool,
normal: hebrewNameAndCharPool,
hard: hebrewCharPool
}
);
if (template) {
bank.push(template);
}
});
signs.forEach((entry) => {
if (!entry?.name || !entry?.rulingPlanetId) {
return;
}
const rulerName = planetsById[normalizeId(entry.rulingPlanetId)]?.name;
if (!rulerName) {
return;
}
const template = createQuestionTemplate(
{
key: `zodiac-ruler:${entry.id || entry.name}`,
categoryId: "zodiac-rulers",
category: "Zodiac Rulers",
promptByDifficulty: `${entry.name} is ruled by`,
answerByDifficulty: rulerName
},
planetNamePool
);
if (template) {
bank.push(template);
}
});
signs.forEach((entry) => {
if (!entry?.name || !entry?.element) {
return;
}
const template = createQuestionTemplate(
{
key: `zodiac-element:${entry.id || entry.name}`,
categoryId: "zodiac-elements",
category: "Zodiac Elements",
promptByDifficulty: `${entry.name} is`,
answerByDifficulty: toTitleCase(entry.element)
},
zodiacElementPool
);
if (template) {
bank.push(template);
}
});
planets.forEach((entry) => {
if (!entry?.name || !entry?.weekday) {
return;
}
const template = createQuestionTemplate(
{
key: `planet-weekday:${entry.id || entry.name}`,
categoryId: "planetary-weekdays",
category: "Planetary Weekdays",
promptByDifficulty: `${entry.name} corresponds to`,
answerByDifficulty: entry.weekday
},
planetWeekdayPool
);
if (template) {
bank.push(template);
}
});
signs.forEach((entry) => {
if (!entry?.name || !entry?.tarot?.majorArcana) {
return;
}
const template = createQuestionTemplate(
{
key: `zodiac-tarot:${entry.id || entry.name}`,
categoryId: "zodiac-tarot",
category: "Zodiac ↔ Tarot",
promptByDifficulty: `${entry.name} corresponds to`,
answerByDifficulty: entry.tarot.majorArcana
},
zodiacTarotPool
);
if (template) {
bank.push(template);
}
});
treePaths.forEach((path) => {
const pathNo = Number(path?.pathNumber);
if (!Number.isFinite(pathNo)) {
return;
}
const pathNumberLabel = String(Math.trunc(pathNo));
const fromNo = Number(path?.connects?.from);
const toNo = Number(path?.connects?.to);
const fromName = getSephiraName(fromNo, path?.connectIds?.from);
const toName = getSephiraName(toNo, path?.connectIds?.to);
const pathLetter = formatPathLetter(path);
const tarotCard = normalizeOption(path?.tarot?.card);
if (fromName && toName) {
const template = createQuestionTemplate(
{
key: `kabbalah-path-between:${pathNumberLabel}`,
categoryId: "kabbalah-path-between-sephirot",
category: "Kabbalah Paths",
promptByDifficulty: {
easy: `Which path is between ${fromName} and ${toName}`,
normal: `What path connects ${fromName} and ${toName}`,
hard: `${fromName}${toName} is which path`
},
answerByDifficulty: pathNumberLabel
},
pathNumberPool
);
if (template) {
bank.push(template);
}
}
if (pathLetter) {
const numberToLetterTemplate = createQuestionTemplate(
{
key: `kabbalah-path-letter:${pathNumberLabel}`,
categoryId: "kabbalah-path-letter",
category: "Kabbalah Paths",
promptByDifficulty: {
easy: `Which letter is on Path ${pathNumberLabel}`,
normal: `Path ${pathNumberLabel} carries which Hebrew letter`,
hard: `Letter on Path ${pathNumberLabel}`
},
answerByDifficulty: pathLetter
},
pathLetterPool
);
if (numberToLetterTemplate) {
bank.push(numberToLetterTemplate);
}
const letterToNumberTemplate = createQuestionTemplate(
{
key: `kabbalah-letter-path-number:${pathNumberLabel}`,
categoryId: "kabbalah-path-letter",
category: "Kabbalah Paths",
promptByDifficulty: {
easy: `${pathLetter} belongs to which path`,
normal: `${pathLetter} corresponds to Path`,
hard: `${pathLetter} is on Path`
},
answerByDifficulty: pathNumberLabel
},
pathNumberPool
);
if (letterToNumberTemplate) {
bank.push(letterToNumberTemplate);
}
}
if (tarotCard) {
const pathToTarotTemplate = createQuestionTemplate(
{
key: `kabbalah-path-tarot:${pathNumberLabel}`,
categoryId: "kabbalah-path-tarot",
category: "Kabbalah ↔ Tarot",
promptByDifficulty: {
easy: `Path ${pathNumberLabel} corresponds to which Tarot trump`,
normal: `Which Tarot trump is on Path ${pathNumberLabel}`,
hard: `Tarot trump on Path ${pathNumberLabel}`
},
answerByDifficulty: tarotCard
},
pathTarotPool
);
if (pathToTarotTemplate) {
bank.push(pathToTarotTemplate);
}
const tarotToPathTemplate = createQuestionTemplate(
{
key: `tarot-trump-path:${pathNumberLabel}`,
categoryId: "kabbalah-path-tarot",
category: "Tarot ↔ Kabbalah",
promptByDifficulty: {
easy: `${tarotCard} is on which path`,
normal: `Which path corresponds to ${tarotCard}`,
hard: `${tarotCard} corresponds to Path`
},
answerByDifficulty: pathNumberLabel
},
pathNumberPool
);
if (tarotToPathTemplate) {
bank.push(tarotToPathTemplate);
}
}
});
Object.values(sephirotById).forEach((sephira) => {
const sephiraName = String(sephira?.name?.roman || sephira?.name?.en || "").trim();
const planetLabel = getPlanetLabelById(sephira?.planetId);
if (!sephiraName || !planetLabel) {
return;
}
const template = createQuestionTemplate(
{
key: `sephirot-planet:${normalizeId(sephira?.id || sephiraName)}`,
categoryId: "sephirot-planets",
category: "Sephirot ↔ Planet",
promptByDifficulty: {
easy: `${sephiraName} corresponds to which planet`,
normal: `Planetary correspondence of ${sephiraName}`,
hard: `${sephiraName} corresponds to`
},
answerByDifficulty: planetLabel
},
toUniqueOptionList(Object.values(sephirotById).map((entry) => getPlanetLabelById(entry?.planetId)))
);
if (template) {
bank.push(template);
}
});
flattenDecans.forEach((decan) => {
const decanId = String(decan?.id || "").trim();
const card = normalizeOption(decan?.tarotMinorArcana);
const decanLabel = formatDecanLabel(decan);
const rulerLabel = getPlanetLabelById(decan?.rulerPlanetId);
if (!decanId || !card) {
return;
}
if (decanLabel) {
const template = createQuestionTemplate(
{
key: `tarot-decan-sign:${decanId}`,
categoryId: "tarot-decan-sign",
category: "Tarot Decans",
promptByDifficulty: {
easy: `${card} belongs to which decan`,
normal: `Which decan contains ${card}`,
hard: `${card} is in`
},
answerByDifficulty: decanLabel
},
decanLabelPool
);
if (template) {
bank.push(template);
}
}
if (rulerLabel) {
const template = createQuestionTemplate(
{
key: `tarot-decan-ruler:${decanId}`,
categoryId: "tarot-decan-ruler",
category: "Tarot Decans",
promptByDifficulty: {
easy: `The decan of ${card} is ruled by`,
normal: `Who rules the decan for ${card}`,
hard: `${card} decan ruler`
},
answerByDifficulty: rulerLabel
},
decanRulerPool
);
if (template) {
bank.push(template);
}
}
});
cubeWalls.forEach((wall) => {
const wallName = String(wall?.name || labelFromId(wall?.id)).trim();
const wallLabel = wallName ? `${wallName} Wall` : "";
const tarotCard = normalizeOption(wall?.associations?.tarotCard);
const hebrew = hebrewById.get(normalizeId(wall?.hebrewLetterId));
const hebrewLabel = formatHebrewLetterLabel(hebrew, wall?.hebrewLetterId);
if (tarotCard && wallLabel) {
const template = createQuestionTemplate(
{
key: `tarot-cube-wall:${normalizeId(wall?.id || wallName)}`,
categoryId: "tarot-cube-location",
category: "Tarot ↔ Cube",
promptByDifficulty: {
easy: `${tarotCard} is on which Cube wall`,
normal: `Where is ${tarotCard} on the Cube`,
hard: `${tarotCard} location on Cube`
},
answerByDifficulty: wallLabel
},
cubeLocationPool
);
if (template) {
bank.push(template);
}
}
if (wallLabel && hebrewLabel) {
const template = createQuestionTemplate(
{
key: `cube-wall-letter:${normalizeId(wall?.id || wallName)}`,
categoryId: "cube-hebrew-letter",
category: "Cube ↔ Hebrew",
promptByDifficulty: {
easy: `${wallLabel} corresponds to which Hebrew letter`,
normal: `Which Hebrew letter is on ${wallLabel}`,
hard: `${wallLabel} letter`
},
answerByDifficulty: hebrewLabel
},
cubeHebrewLetterPool
);
if (template) {
bank.push(template);
}
}
});
cubeEdges.forEach((edge) => {
const edgeName = String(edge?.name || labelFromId(edge?.id)).trim();
const edgeLabel = edgeName ? `${edgeName} Edge` : "";
const hebrew = hebrewById.get(normalizeId(edge?.hebrewLetterId));
const hebrewLabel = formatHebrewLetterLabel(hebrew, edge?.hebrewLetterId);
const tarotCard = normalizeOption(hebrew?.tarot?.card);
if (tarotCard && edgeLabel) {
const template = createQuestionTemplate(
{
key: `tarot-cube-edge:${normalizeId(edge?.id || edgeName)}`,
categoryId: "tarot-cube-location",
category: "Tarot ↔ Cube",
promptByDifficulty: {
easy: `${tarotCard} is on which Cube edge`,
normal: `Where is ${tarotCard} on the Cube edges`,
hard: `${tarotCard} edge location`
},
answerByDifficulty: edgeLabel
},
cubeLocationPool
);
if (template) {
bank.push(template);
}
}
if (edgeLabel && hebrewLabel) {
const template = createQuestionTemplate(
{
key: `cube-edge-letter:${normalizeId(edge?.id || edgeName)}`,
categoryId: "cube-hebrew-letter",
category: "Cube ↔ Hebrew",
promptByDifficulty: {
easy: `${edgeLabel} corresponds to which Hebrew letter`,
normal: `Which Hebrew letter is on ${edgeLabel}`,
hard: `${edgeLabel} letter`
},
answerByDifficulty: hebrewLabel
},
cubeHebrewLetterPool
);
if (template) {
bank.push(template);
}
}
});
if (cubeCenter) {
const centerTarot = normalizeOption(cubeCenter?.associations?.tarotCard || cubeCenter?.tarotCard);
const centerHebrew = hebrewById.get(normalizeId(cubeCenter?.hebrewLetterId));
const centerHebrewLabel = formatHebrewLetterLabel(centerHebrew, cubeCenter?.hebrewLetterId);
if (centerTarot) {
const template = createQuestionTemplate(
{
key: "tarot-cube-center",
categoryId: "tarot-cube-location",
category: "Tarot ↔ Cube",
promptByDifficulty: {
easy: `${centerTarot} is located at which Cube position`,
normal: `Where is ${centerTarot} on the Cube`,
hard: `${centerTarot} Cube location`
},
answerByDifficulty: "Center"
},
cubeLocationPool
);
if (template) {
bank.push(template);
}
}
if (centerHebrewLabel) {
const template = createQuestionTemplate(
{
key: "cube-center-letter",
categoryId: "cube-hebrew-letter",
category: "Cube ↔ Hebrew",
promptByDifficulty: {
easy: "The Cube center corresponds to which Hebrew letter",
normal: "Which Hebrew letter is at the Cube center",
hard: "Cube center letter"
},
answerByDifficulty: centerHebrewLabel
},
cubeHebrewLetterPool
);
if (template) {
bank.push(template);
}
}
}
playingCards.forEach((entry) => {
const cardId = String(entry?.id || "").trim();
const rankLabel = normalizeOption(entry?.rankLabel || entry?.rank);
const suitLabel = normalizeOption(entry?.suitLabel || labelFromId(entry?.suit));
const tarotCard = normalizeOption(entry?.tarotCard);
if (!cardId || !rankLabel || !suitLabel || !tarotCard) {
return;
}
const template = createQuestionTemplate(
{
key: `playing-card-tarot:${cardId}`,
categoryId: "playing-card-tarot",
category: "Playing Card ↔ Tarot",
promptByDifficulty: {
easy: `${rankLabel} of ${suitLabel} maps to which Tarot card`,
normal: `${rankLabel} of ${suitLabel} corresponds to`,
hard: `${rankLabel} of ${suitLabel} maps to`
},
answerByDifficulty: tarotCard
},
playingTarotPool
);
if (template) {
bank.push(template);
}
});
(dynamicCategoryRegistry || []).forEach(({ builder }) => {
try {
const dynamicTemplates = builder(referenceData, magickDataset);
if (Array.isArray(dynamicTemplates)) {
dynamicTemplates.forEach((template) => {
if (template) {
bank.push(template);
}
});
}
} catch (_error) {
// Skip broken plugins silently to preserve quiz availability.
}
});
return bank;
}
window.QuizQuestionBank = {
buildQuestionBank,
createQuestionTemplate,
normalizeKey,
normalizeOption,
toTitleCase,
toUniqueOptionList
};
})();