{
  "id": "P010",
  "slug": "quiz",
  "key": "P010-quiz",
  "title": "Quiz",
  "summary": "Take a multiple-choice quiz, get a score, review the questions you missed and retake only those until none are left; every attempt is kept in a score history - from a text menu.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P010 - Quiz\n\nA multiple-choice quiz of eight questions. Taking the quiz gives a score and\na percentage; the questions you got wrong can then be reviewed - your answer\nnext to the right one - and retaken on their own, again and again, until none\nare left. Every scored attempt goes into a score history, which is kept while\nthe program runs.\n\n- `main.eml` - the menu, the score report, the review of mistakes, and the\n  score history\n- `bank.eml` - the questions: each has a number, the question, four options\n  A to D, and the right letter\n- `quiz.eml` - asking a question, reading the answer, marking an attempt, and\n  the percentage\n\nAn attempt asks its questions in order and keeps the ones answered wrongly,\nwith the answer given. A retake asks only the last attempt's mistakes, and its\nown mistakes replace them, so a question answered right drops out. Typing `q`\nat an answer stops the attempt: a stopped attempt is not scored, goes into no\nhistory, and leaves the mistakes as they were. The percentage is worked out in\nwhole numbers and rounded half up (7/8 shows as 88%).\n\nWhat is checked: an answer is one letter, A to D in either case, or `q`;\nanything else asks the same question again.\n\nSessions: `sessions/basic.in` takes the quiz with two mistakes, reviews them,\nretakes them (one right, one wrong again), reviews and retakes the one that is\nleft until there are none, and shows the three scores;\n`sessions/bad-input.in` types an unknown choice, asks for a review, a retake\nand the scores before any quiz, types a letter that is not an option, nothing,\ntwo letters and a lower-case answer, stops a quiz (it is not scored), then\nanswers in lower case with one mistake, stops the retake, and retakes until\nnothing is left to review or retake.\n\nBuilt on the verified corpus case `simple-quiz-grader` (answers marked against\nthe right ones, with a score and a percentage).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P010 quiz: take the quiz, review the questions you missed, and retake only\n# those until none are left. Every scored attempt goes into the score\n# history, which lives while the program runs.\nimport bank\nimport quiz\n\ndef report(score, total, mistakes):\n    (\"Score: \" + str(score) + \"/\" + str(total) + \" (\" + str(quiz.percent(score, total)) + \"%).\") ^0\n    if len(mistakes) == 0:\n        \"No mistakes - well done.\" ^0\n    else:\n        (str(len(mistakes)) + \" to review.\") ^0\n\ndef review(mistakes):\n    for m in mistakes:\n        m[0] => q\n        \"\" ^0\n        (\"Q\" + str(q[0]) + \". \" + q[1]) ^0\n        (\"   you said \" + m[1] + \") \" + bank.option_text(q, m[1]) + \"; it is \" + q[3] + \") \" + bank.option_text(q, q[3]) + \".\") ^0\n\n# [kind, score, total] for every scored attempt\n[] => history\n# None until the quiz has been taken; then the last attempt's mistakes\nNone => mistakes\nTrue => running\nwhile running:\n    \"\" ^0\n    \"== Quiz ==\" ^0\n    \"1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\" ^0\n    quiz.trim(input(\"choice> \")) => choice\n    if choice == \"1\":\n        quiz.attempt(bank.questions) => r\n        if r == None:\n            \"Quiz stopped; not scored.\" ^0\n        else:\n            report(r[0], len(bank.questions), r[1])\n            history + [[\"quiz\", r[0], len(bank.questions)]] => history\n            r[1] => mistakes\n    elif choice == \"2\":\n        if mistakes == None:\n            \"Take the quiz first.\" ^0\n        elif len(mistakes) == 0:\n            \"No mistakes to review.\" ^0\n        else:\n            review(mistakes)\n    elif choice == \"3\":\n        if mistakes == None:\n            \"Take the quiz first.\" ^0\n        elif len(mistakes) == 0:\n            \"No mistakes to retake.\" ^0\n        else:\n            [m[0] for m in mistakes] => again\n            quiz.attempt(again) => r\n            if r == None:\n                \"Retake stopped; not scored.\" ^0\n            else:\n                report(r[0], len(again), r[1])\n                history + [[\"retake\", r[0], len(again)]] => history\n                r[1] => mistakes\n    elif choice == \"4\":\n        \"\" ^0\n        \"-- scores --\" ^0\n        if len(history) == 0:\n            \"  (no attempts yet)\" ^0\n        0 => n\n        for h in history:\n            n + 1 => n\n            (\"  \" + str(n) + \") \" + (\"%-7s\" % h[0]) + (\"%5s\" % (str(h[1]) + \"/\" + str(h[2]))) + (\"%6s\" % (str(quiz.percent(h[1], h[2])) + \"%\"))) ^0\n    elif choice == \"5\":\n        False => running\n    else:\n        \"Pick a number from 1 to 5.\" ^0\n\"Bye.\" ^0\n",
      "python": "import bank\nimport quiz\n\ndef report(score, total, mistakes):\n    print(\"Score: \" + str(score) + \"/\" + str(total) + \" (\" + str(quiz.percent(score, total)) + \"%).\")\n    if len(mistakes) == 0:\n        print(\"No mistakes - well done.\")\n    else:\n        print(str(len(mistakes)) + \" to review.\")\n\ndef review(mistakes):\n    for m in mistakes:\n        q = m[0]\n        print(\"\")\n        print(\"Q\" + str(q[0]) + \". \" + q[1])\n        print(\"   you said \" + m[1] + \") \" + bank.option_text(q, m[1]) + \"; it is \" + q[3] + \") \" + bank.option_text(q, q[3]) + \".\")\n\nhistory = []\nmistakes = None\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"== Quiz ==\")\n    print(\"1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\")\n    choice = quiz.trim(input(\"choice> \"))\n    if choice == \"1\":\n        r = quiz.attempt(bank.questions)\n        if r == None:\n            print(\"Quiz stopped; not scored.\")\n        else:\n            report(r[0], len(bank.questions), r[1])\n            history = history + [[\"quiz\", r[0], len(bank.questions)]]\n            mistakes = r[1]\n    elif choice == \"2\":\n        if mistakes == None:\n            print(\"Take the quiz first.\")\n        elif len(mistakes) == 0:\n            print(\"No mistakes to review.\")\n        else:\n            review(mistakes)\n    elif choice == \"3\":\n        if mistakes == None:\n            print(\"Take the quiz first.\")\n        elif len(mistakes) == 0:\n            print(\"No mistakes to retake.\")\n        else:\n            again = [m[0] for m in mistakes]\n            r = quiz.attempt(again)\n            if r == None:\n                print(\"Retake stopped; not scored.\")\n            else:\n                report(r[0], len(again), r[1])\n                history = history + [[\"retake\", r[0], len(again)]]\n                mistakes = r[1]\n    elif choice == \"4\":\n        print(\"\")\n        print(\"-- scores --\")\n        if len(history) == 0:\n            print(\"  (no attempts yet)\")\n        n = 0\n        for h in history:\n            n = n + 1\n            print(\"  \" + str(n) + \") \" + \"%-7s\" % h[0] + \"%5s\" % (str(h[1]) + \"/\" + str(h[2])) + \"%6s\" % (str(quiz.percent(h[1], h[2])) + \"%\"))\n    elif choice == \"5\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 5.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "bank.eml",
      "eml": "# P010 quiz - the question bank. A question is a list\n# [number, text, [option A, B, C, D], correct letter].\n\n[\n    [1, \"What gas do plants take in for photosynthesis?\", [\"Oxygen\", \"Carbon dioxide\", \"Nitrogen\", \"Helium\"], \"B\"],\n    [2, \"How many bones are in the adult human body?\", [\"106\", \"206\", \"306\", \"406\"], \"B\"],\n    [3, \"What is the chemical symbol for gold?\", [\"Ag\", \"Gd\", \"Au\", \"Go\"], \"C\"],\n    [4, \"Which planet is closest to the Sun?\", [\"Venus\", \"Mars\", \"Mercury\", \"Earth\"], \"C\"],\n    [5, \"What is the largest ocean on Earth?\", [\"Atlantic\", \"Indian\", \"Arctic\", \"Pacific\"], \"D\"],\n    [6, \"Which river flows through Cairo?\", [\"Nile\", \"Amazon\", \"Danube\", \"Ganges\"], \"A\"],\n    [7, \"On which continent is the Sahara desert?\", [\"Asia\", \"Africa\", \"Australia\", \"South America\"], \"B\"],\n    [8, \"What is the capital of Canada?\", [\"Toronto\", \"Vancouver\", \"Ottawa\", \"Montreal\"], \"C\"],\n] => questions\n\n\"ABCD\" => letters\n\ndef option_text(q, letter):\n    # The text of option `letter` of question q.\n    for i in [0:3]:\n        if letters[i] == letter:\n            return q[2][i]\n    return \"\"\n",
      "python": "questions = [[1, \"What gas do plants take in for photosynthesis?\", [\"Oxygen\", \"Carbon dioxide\", \"Nitrogen\", \"Helium\"], \"B\"], [2, \"How many bones are in the adult human body?\", [\"106\", \"206\", \"306\", \"406\"], \"B\"], [3, \"What is the chemical symbol for gold?\", [\"Ag\", \"Gd\", \"Au\", \"Go\"], \"C\"], [4, \"Which planet is closest to the Sun?\", [\"Venus\", \"Mars\", \"Mercury\", \"Earth\"], \"C\"], [5, \"What is the largest ocean on Earth?\", [\"Atlantic\", \"Indian\", \"Arctic\", \"Pacific\"], \"D\"], [6, \"Which river flows through Cairo?\", [\"Nile\", \"Amazon\", \"Danube\", \"Ganges\"], \"A\"], [7, \"On which continent is the Sahara desert?\", [\"Asia\", \"Africa\", \"Australia\", \"South America\"], \"B\"], [8, \"What is the capital of Canada?\", [\"Toronto\", \"Vancouver\", \"Ottawa\", \"Montreal\"], \"C\"]]\nletters = \"ABCD\"\n\ndef option_text(q, letter):\n    for i in range(0, 4):\n        if letters[i] == letter:\n            return q[2][i]\n    return \"\"\n"
    },
    {
      "name": "quiz.eml",
      "eml": "# P010 quiz - asking questions and marking them. An attempt asks a list of\n# questions in order and returns [score, mistakes], where each mistake is\n# [question, the letter given]; typing q stops the attempt, and a stopped\n# attempt is not scored.\nimport bank\n\ndef trim(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef answer_letter(typed):\n    # \"A\"..\"D\" for a typed a-d or A-D, \"q\" to stop, \"\" for anything else.\n    if typed == \"q\" or typed == \"Q\":\n        return \"q\"\n    \"abcd\" => small\n    if len(typed) == 1:\n        for i in [0:3]:\n            if typed == bank.letters[i] or typed == small[i]:\n                return bank.letters[i]\n    return \"\"\n\ndef ask(q):\n    # Shows question q and returns the letter answered, or \"q\".\n    \"\" ^0\n    (\"Q\" + str(q[0]) + \". \" + q[1]) ^0\n    for i in [0:3]:\n        (\"   \" + bank.letters[i] + \") \" + q[2][i]) ^0\n    while True:\n        answer_letter(trim(input(\"answer> \"))) => a\n        if a != \"\":\n            return a\n        \"Answer with A, B, C or D (or q to stop).\" ^0\n\ndef attempt(qs):\n    # [score, mistakes] for the questions qs, or None if the attempt stopped.\n    0 => score\n    [] => mistakes\n    for q in qs:\n        ask(q) => a\n        if a == \"q\":\n            return None\n        if a == q[3]:\n            score + 1 => score\n            \"Right.\" ^0\n        else:\n            (\"Wrong - it is \" + q[3] + \") \" + bank.option_text(q, q[3]) + \".\") ^0\n            mistakes + [[q, a]] => mistakes\n    return [score, mistakes]\n\ndef percent(score, total):\n    # score / total as a whole percentage, rounded half up.\n    return int((200 * score + total - (200 * score + total) % (2 * total)) / (2 * total))\n",
      "python": "import bank\n\ndef trim(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef answer_letter(typed):\n    if typed == \"q\" or typed == \"Q\":\n        return \"q\"\n    small = \"abcd\"\n    if len(typed) == 1:\n        for i in range(0, 4):\n            if typed == bank.letters[i] or typed == small[i]:\n                return bank.letters[i]\n    return \"\"\n\ndef ask(q):\n    print(\"\")\n    print(\"Q\" + str(q[0]) + \". \" + q[1])\n    for i in range(0, 4):\n        print(\"   \" + bank.letters[i] + \") \" + q[2][i])\n    while True:\n        a = answer_letter(trim(input(\"answer> \")))\n        if a != \"\":\n            return a\n        print(\"Answer with A, B, C or D (or q to stop).\")\n\ndef attempt(qs):\n    score = 0\n    mistakes = []\n    for q in qs:\n        a = ask(q)\n        if a == \"q\":\n            return None\n        if a == q[3]:\n            score = score + 1\n            print(\"Right.\")\n        else:\n            print(\"Wrong - it is \" + q[3] + \") \" + bank.option_text(q, q[3]) + \".\")\n            mistakes = mistakes + [[q, a]]\n    return [score, mistakes]\n\ndef percent(score, total):\n    return int((200 * score + total - (200 * score + total) % (2 * total)) / (2 * total))\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "9\n2\n3\n4\n1\ne\n\nab\nb\nq\n4\n1\nb\nb\nc\nc\nd\na\nb\na\n3\nx\nq\n4\n3\nc\n3\n2\n4\n5\n",
      "screen": "\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 9\nPick a number from 1 to 5.\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 2\nTake the quiz first.\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 3\nTake the quiz first.\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 4\n\n-- scores --\n  (no attempts yet)\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 1\n\nQ1. What gas do plants take in for photosynthesis?\n   A) Oxygen\n   B) Carbon dioxide\n   C) Nitrogen\n   D) Helium\nanswer> e\nAnswer with A, B, C or D (or q to stop).\nanswer> \nAnswer with A, B, C or D (or q to stop).\nanswer> ab\nAnswer with A, B, C or D (or q to stop).\nanswer> b\nRight.\n\nQ2. How many bones are in the adult human body?\n   A) 106\n   B) 206\n   C) 306\n   D) 406\nanswer> q\nQuiz stopped; not scored.\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 4\n\n-- scores --\n  (no attempts yet)\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 1\n\nQ1. What gas do plants take in for photosynthesis?\n   A) Oxygen\n   B) Carbon dioxide\n   C) Nitrogen\n   D) Helium\nanswer> b\nRight.\n\nQ2. How many bones are in the adult human body?\n   A) 106\n   B) 206\n   C) 306\n   D) 406\nanswer> b\nRight.\n\nQ3. What is the chemical symbol for gold?\n   A) Ag\n   B) Gd\n   C) Au\n   D) Go\nanswer> c\nRight.\n\nQ4. Which planet is closest to the Sun?\n   A) Venus\n   B) Mars\n   C) Mercury\n   D) Earth\nanswer> c\nRight.\n\nQ5. What is the largest ocean on Earth?\n   A) Atlantic\n   B) Indian\n   C) Arctic\n   D) Pacific\nanswer> d\nRight.\n\nQ6. Which river flows through Cairo?\n   A) Nile\n   B) Amazon\n   C) Danube\n   D) Ganges\nanswer> a\nRight.\n\nQ7. On which continent is the Sahara desert?\n   A) Asia\n   B) Africa\n   C) Australia\n   D) South America\nanswer> b\nRight.\n\nQ8. What is the capital of Canada?\n   A) Toronto\n   B) Vancouver\n   C) Ottawa\n   D) Montreal\nanswer> a\nWrong - it is C) Ottawa.\nScore: 7/8 (88%).\n1 to review.\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 3\n\nQ8. What is the capital of Canada?\n   A) Toronto\n   B) Vancouver\n   C) Ottawa\n   D) Montreal\nanswer> x\nAnswer with A, B, C or D (or q to stop).\nanswer> q\nRetake stopped; not scored.\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 4\n\n-- scores --\n  1) quiz     7/8   88%\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 3\n\nQ8. What is the capital of Canada?\n   A) Toronto\n   B) Vancouver\n   C) Ottawa\n   D) Montreal\nanswer> c\nRight.\nScore: 1/1 (100%).\nNo mistakes - well done.\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 3\nNo mistakes to retake.\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 2\nNo mistakes to review.\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 4\n\n-- scores --\n  1) quiz     7/8   88%\n  2) retake   1/1  100%\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\nB\nB\nA\nC\nD\nA\nA\nC\n2\n3\nC\nC\n2\n3\nB\n2\n4\n5\n",
      "screen": "\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 1\n\nQ1. What gas do plants take in for photosynthesis?\n   A) Oxygen\n   B) Carbon dioxide\n   C) Nitrogen\n   D) Helium\nanswer> B\nRight.\n\nQ2. How many bones are in the adult human body?\n   A) 106\n   B) 206\n   C) 306\n   D) 406\nanswer> B\nRight.\n\nQ3. What is the chemical symbol for gold?\n   A) Ag\n   B) Gd\n   C) Au\n   D) Go\nanswer> A\nWrong - it is C) Au.\n\nQ4. Which planet is closest to the Sun?\n   A) Venus\n   B) Mars\n   C) Mercury\n   D) Earth\nanswer> C\nRight.\n\nQ5. What is the largest ocean on Earth?\n   A) Atlantic\n   B) Indian\n   C) Arctic\n   D) Pacific\nanswer> D\nRight.\n\nQ6. Which river flows through Cairo?\n   A) Nile\n   B) Amazon\n   C) Danube\n   D) Ganges\nanswer> A\nRight.\n\nQ7. On which continent is the Sahara desert?\n   A) Asia\n   B) Africa\n   C) Australia\n   D) South America\nanswer> A\nWrong - it is B) Africa.\n\nQ8. What is the capital of Canada?\n   A) Toronto\n   B) Vancouver\n   C) Ottawa\n   D) Montreal\nanswer> C\nRight.\nScore: 6/8 (75%).\n2 to review.\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 2\n\nQ3. What is the chemical symbol for gold?\n   you said A) Ag; it is C) Au.\n\nQ7. On which continent is the Sahara desert?\n   you said A) Asia; it is B) Africa.\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 3\n\nQ3. What is the chemical symbol for gold?\n   A) Ag\n   B) Gd\n   C) Au\n   D) Go\nanswer> C\nRight.\n\nQ7. On which continent is the Sahara desert?\n   A) Asia\n   B) Africa\n   C) Australia\n   D) South America\nanswer> C\nWrong - it is B) Africa.\nScore: 1/2 (50%).\n1 to review.\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 2\n\nQ7. On which continent is the Sahara desert?\n   you said C) Australia; it is B) Africa.\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 3\n\nQ7. On which continent is the Sahara desert?\n   A) Asia\n   B) Africa\n   C) Australia\n   D) South America\nanswer> B\nRight.\nScore: 1/1 (100%).\nNo mistakes - well done.\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 2\nNo mistakes to review.\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 4\n\n-- scores --\n  1) quiz     6/8   75%\n  2) retake   1/2   50%\n  3) retake   1/1  100%\n\n== Quiz ==\n1) take the quiz  2) review mistakes  3) retake mistakes  4) scores  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "simple-quiz-grader",
      "caseId": "041-simple-quiz-grader",
      "title": "Simple quiz grader"
    }
  ],
  "updated": "2026-10-03"
}
