A terminal quiz game whose questions, answer keys and match history all live in CSV files that the player edits from inside the game.
This is the final project for CS50P, Harvard's Introduction to Programming with Python. The brief fixes the shape of the code: one project.py containing a main function and at least three further functions defined at the same level, never nested and never wrapped in a class, with a test_project.py beside it holding pytest functions named after what they test. Everything here therefore sits in a single flat module of 572 lines and 20 functions.
The problem worth solving inside that constraint was ownership of the questions. A quiz with its questions written into the source is played once and then abandoned, because adding to it means editing Python. So the question banks are CSV files, and every operation on the trivia bank, writing a fresh one, appending to an existing one and deleting individual entries, is reachable from the menu that appears after a round. The player never opens the files.
Three modes are on offer. Simple and hard both read questions.csv and differ only in the value of its difficulty column, so one file feeds both. Multiple choice reads multi_choice.csv, where each row keeps its answers as a comma separated string and names which one is correct; between three and six answers are accepted per question, and the correct one has to match one of them before the entry is saved. Answers are shuffled once when written and again each time the question is displayed, and the player can either type an answer out or type the number next to it.
A round ends when the bank is exhausted. The score is printed, and a menu offers a retry, a table of the rounds played so far, a quit, or one of three edits to questions.csv: replacing it, appending to it, or deleting individual entries. Quitting writes every question of every round in the session to history.csv. From the opening menu that stored history can be printed or cleared without starting a game. Tables are rendered with tabulate, and multiple rounds are separated by an end of game marker row.
Every prompt matches on prefixes rather than whole words, so s, sim and simple all pick the simple quiz, and the post round menu takes either the word or its number. Questions are also normalised before they are stored: capitalised, given a question mark if they lack one, stripped of anything outside letters, digits, spaces, ? and °, and deduplicated. The answer check is a string comparison, so removing stray punctuation on the way in is what stops a correct answer being marked wrong on the way out.
| Component | Detail |
|---|---|
| Python | 3.11 |
| tabulate | Renders the history and round tables. Unpinned |
| pytest | Test runner for test_project.py, with pytest-mock for the mocked tests |
| Standard library | csv for all storage, re for prefix matching and normalisation, random for shuffling, os and sys |
git clone https://github.com/Miclah/CS50-Python-Final-Project.git
cd CS50-Python-Final-Project
pip install tabulate
python project.pyAll file paths are relative, so run it from the repository root or it will create its CSV files wherever you happen to be. questions.csv ships with one question and multi_choice.csv with two, which is enough to see a round end but not to play seriously; if a bank is empty the game prompts you to fill it before continuing. Tests live in test_project.py and run with pytest.
The answer check is case sensitive, so paris is marked wrong against Paris. history.csv is opened for writing rather than appending when a session ends, so only the most recent session survives. dependancies.txt lists imported module names, including the standard library ones, rather than pinned packages, so it is not something pip can install from.
MIT. See LICENSE.