Country Flags Quiz

Games

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! 🙂

Build
Build on this example by creating an editable copy of this form.
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;
};