Generate a quiz based on a list of countries in JSON that's fetched at runtime.
The user picks a difficulty level and the number of rounds they want to play. The form then fetches a list of countries from a JSON file, picks a number of them randomly, and takes the user through a quiz. It keeps track of right/wrong answers and presents a summary at the end.
This was mainly intended as an example to show how flexible this form-builder can be, but it's legitimately turned out to be a great way to learn about country flags! 🙂
let metadataUrl = "https://prod.static.formulate.dev/docs/flags/flags.json";
let difficulties = [
{ value: 8, label: "Hard" },
{ value: 6, label: "Medium" },
{ value: 4, label: "Easy" },
{ value: 2, label: "Novice" },
];
let image = (code) => `<img src="${imageUrl(code)}" width="50" style="display: inline;" />`;
let imageUrl = (code) => `https://prod.static.formulate.dev/docs/flags/${code.toLowerCase()}.png`;
export default async function () {
let { text } = await form.fetch(metadataUrl);
let data = JSON.parse(text);
// 1. Collect data
let name = await form.short("Hi, welcome to the flags game! First off, what's your name?", {
banner: { full: true, imageUrl: "https://images.unsplash.com/photo-1592487501226-7ed5e5dc80f2?fm=jpg&w=1080" },
});
let { value: optionsPerQuestion } = await form.multi(
`Amazing, it's lovely to see you ${form.interp(name)}! What difficulty do you want to play at?`,
difficulties,
{ single: true }
);
let { value: totalQuestions } = await form.multi(
`Perfect, and how many rounds do you want to play?`,
[3, 5, 7, 10],
{ hideNumbers: true, single: true }
);
// 2. Play the quiz
let score = 0;
let right = [];
let wrong = [];
let countries = _.sampleSize(data, totalQuestions * optionsPerQuestion);
for (let choices of _.chunk(countries, optionsPerQuestion)) {
let options = choices.map(c => ({ label: "", value: c.Code, image: imageUrl(c.Code) }));
let correct = _.sample(choices);
let {value: answer} = await form.multi(
`Which flag belongs to ${form.interp(correct.Name)}?`,
options,
{ single: true, ignore: true }
);
if (answer === correct.Code) {
score++;
right.push({ answer, correct });
} else {
wrong.push({ answer, correct });
}
}
// 3. Display results
await form.hidden("Right answers", right.length);
await form.statement(
`All done! You got ${score} right out of ${totalQuestions}.`,
{ description: buildSummary(right, wrong) }
);
}
let buildSummary = (right, wrong) => {
let summary = "";
if (right.length > 0) {
summary += "## Right Answers\n";
right.forEach(({ correct }) => {
summary += `- ${correct.Name}: ${image(correct.Code)}\n`;
});
}
summary += "\n\n";
if (wrong.length > 0) {
summary += "## Wrong Answers\n";
wrong.forEach(({ answer, correct }) => {
summary += `- ${correct.Name}: ${image(
correct.Code
)} was right, but you picked ${image(answer)}\n`;
});
}
return summary;
};