Skip to content
This repository was archived by the owner on Jun 10, 2026. It is now read-only.

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

147 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

How to use

Step 1: Download python 3.12 at python.org/downloads
Step 2: Download this repo as a zip
Step 3: Double click the zip file in your downloads
Step 4: Open your terminal
Step 5: Type "cd " and then drag the folder into the terminal
Step 6: Type python3 main.py

File Structure

In this project we're using the classic setup where in the root dir it's relatively minimalistic and main.py calls all the functions inside a src dir. Usually in this setup if this was more advanced we would have a data dir.

|--> main.py 
|--> .gitignore 
|--> IPOmodel.drawio.png 
|--> journal.md 
|--> requirements.txt 
|--> readme.md 
|--> src/

Inside src it's once again relatively minimalistic. An init file to ensure no import issues, the logo code, and the games dir where the minigames and analysis reside.

|--> __init__.py 
|--> logo.py 
|--> games/

We moved the utils file from inside src to inside games because it made imports much easier. The dir contains every group members minigames, the analysis code, and the utility code. Another __init__.py file was added here to prevent any import errors.

|--> __init__.py
|--> hangman.py
|--> flag_quiz.py
|--> hard_flag_quiz.py
|--> score_analysis.py
|--> utils.py

Games

We made three games and a code to analyze those games. The games where: hangman, a flag quiz, and a geography quiz. After the user is done playing games they can analyze their results. What starting letters where the easiest for them to remeber. What attributes did the countries they got wrong have in common. Etc.

Utils

In utils a variety of helpful functions are included that are used in most of the other files in this project. In utils the main functions used are ask_question and slow. Ask question calls a giant curses menu function that creates a beautiful interactive UI for the user to use.

def ask_question(prompt, options):
    return curses.wrapper(lambda stdscr: menu(stdscr, options, header=prompt))

The slow function prints out in an animated and cool way, information instead of a boring print statement

def slow(msg):
    for c in msg:
        print(c, end='', flush=True)
        time.sleep(0.01)
    print()
    time.sleep(0.5)

Also inside utils are two classes: a TurtleHandler class and a ResponseClassifcation class. TurtleHandler contains two variables that are used to globalize turtle so that it works when called from main.

def __init__(self):
    self.screen = turtle.Screen()
    self.pen = turtle.Turtle()

For ResponseClassifcation there is a save_result function that processes inputs and saves them in the correct format that will be used in src/games/score_analysis.

def save_result(self, country, mode, result, question):
    country.lower()
    mode.lower()
    result.lower()
    question.capitalize()
    self.scores[country][result][mode].append(question)

To prevent errors in the init method I defined self.scores as:

self.scores = defaultdict(
    lambda: {
        "wrong": defaultdict(list),
        "right": defaultdict(list),
    }
)

Hangman

In the hangman.py there is a main function that takes a scoring system as an input. The scoring system tracks the global score of the user and related information about the question they answered. To begin the function initializes the variables wins, losses, and name. Once intialized, the main function calls playHangman.

The playHangman function welcomes the user and then has a main loop that it runs until the user decides they don't want to play another round. Once the user is finished playing, playHangman returns the wins and losses to display some quick stats in main.

playAgain = True
while playAgain:
    # ... omited for brevity
    result = runSingleRound(secretWord, maxMistakes, difficulty, score_system)
    # ... omited for brevity
    playAgain = askPlayAgain()
return wins, losses

The user picks a difficulty through chooseDifficulty.

prompt = "Choose difficulty:"
options = ['Easy', 'Medium', 'Hard', 'Secret']
choice = ask_question(prompt, options)
return choice.lower()

Once the user picks a difficulty the rest of the params for runSingleRound can be calculated.

difficulty = chooseDifficulty()
secretWord = pickWord(difficulty)
maxMistakes = getMaxMistakes(difficulty)

The pickWord function has arrays of easyCountries, mediumCountries, and hardCountries. A country will randomly be selected from the array that corresponds to the difficulty.

easyCountries = [# E.g Canada]

mediumCountries = [# E.g "poland"]

hardCountries = [# E.g "luxembourg"]

if difficulty == "easy":
    return random.choice(easyCountries)
if difficulty == "medium":
    return random.choice(mediumCountries)
if difficulty == "hard":
    return random.choice(hardCountries)

return "antidisestablishmentarianism"

And the max mistakes is another chain of if statements.

if difficulty == "easy":
    return 8
if difficulty == "medium":
    return 7
if difficulty == "hard":
    return 6
return 5

Once all the params have been found runSingleRound is called. Inside it, the function iteratively prints the current hangman and gets the user input along with printing the basic info.

printHangman(mistakes)
displayCurrentWord(secretWord, correct)

# ... Prints the stats using the slow function. Ommited for brevity

# ... checks if guess is valid

# If all passed
if guess in secretWord:
    correct.append(guess)
else:
    wrong.append(guess)

At the end of the input vetting and processing there are some checks at the end of the loop to see if the round is over.

if all(l in correct for l in secretWord):
    printHangman(mistakes)
    displayCurrentWord(secretWord, correct)
    # ... print out Congrats
    # ... save to score system
    return "win"

if mistakes >= maxMistakes:
    printHangman(mistakes)
    # ... print You lost
    # ... save to score system
    # return "loss"

printHangman is simply a function containing an array of hangman states where the state that corresponds with the num of mistakes is printed.

slow(stages[i])

askPlayAgain simply uses the utils ask_question function and returns the mapped response.

prompt = "Play again?" 
map = {'Yes': True, 'No': False}
options = ['Yes', 'No']
command = ask_question(prompt, options)
return map[command]

Flag Quiz

Inside flag quiz, the structure originally was all functions but some of the code wasn't in functions. We shifted it to classes so that we could store varaibles cross functions easier.

def __init__(self, turtle_info, score_system):   
    self.flags = { # Name >> Method E.g: "France": self.draw_france}
    self.screen = turtle_info.screen
    self.pen = turtle_info.pen
    self.score_system = score_system
    # ... Do a bunch of turtle drawing stuff

Each function gives turtle instructions to draw each flag.

def draw_belgium(self):
    colors = ["black", "yellow", "red"]
    for c in colors:
        self.pen.color(c)
        self.pen.begin_fill()
        for _ in range(2):
            self.pen.forward(100)
            self.pen.right(90)
            self.pen.forward(200)
            self.pen.right(90)
        self.pen.end_fill()
        self.pen.forward(100)

We have a play_flag_game function that initializes this class every time we want to play the flag game.

def play_flag_game(score_system, turtle_info):
    Flags(turtle_info, score_system).main()

We also have a main function to run everything and save the score results.

def main(self):
    for _ in range(rounds):
        # ... turtle stuff
        country = random.choice(list(self.flags.keys()))
        self.flags[country]()
        # ... print question
        # ... save result and display the result
    # ... display info

Geography Quiz

In Geography quiz the user picks between 3 options the in play function once called. The quiz_round function is called whenever once the user picks a difficulty. In quiz_round it loops through all the questions asking the user each time. For each correct answer, three random wrong answers are picked.

Response Analysis

Whenever the user want some analytics on how they did, they can navigate to the Response Analysis options. There the file is pretty long and all of it is necessary so I'll keep this brief. There is a main function inside score_analysis that initalizes an analyzer class and then calls launch_explorer. In launch explorer, a navigator class is initalized and with curses it calles the run function in the navigation class. In the navigation class, the user can navigate through 15 options of different analytics for the questions they answered learning their weakneses and strengths and unique insights. Insights like captial letters etc. A breif example of how this works can be run when the file is not run from a sub-file.

if __name__ == "__main__":
    from collections import defaultdict
    # Create test data
    rc = ResponseClassification()
    rc.save_result("Canada","easy","right", "What is photosynthesis?")
    rc.save_result("Canada", "hard", "wrong", "Explain quantum entanglement")
    rc.save_result("USA", "medium", "right", "What is the capital of France?")
    rc.save_result("USA", "easy", "wrong", "2 + 2 = ?")
    rc.save_result("Brazil", "hard", "right", "Analyze the French Revolution")
    rc.save_result("Brazil", "medium", "wrong", "Describe DNA structure")
    
    # Initialize analyzer (AUTO-POPULATES all 16 categories!)
    analyzer = ResponseAnalyzer(rc)
    
    print("✅ ResponseAnalyzer initialized with auto-population!")
    insights = analyzer.generate_insights()
    print(f"   Total insights generated: {len(insights)}")
    print(f"   Questions classified: {sum(len(items) for cat in analyzer.cap_letter_dist.values() for items in cat.values())}")
    
    # Launch interactive explorer
    launch_explorer(analyzer)

Here we see data is saved to the analyzer and from that the insights are created and we can navigate through them through the launch explorer function.

Navigation

Using curses users can navigate through windows picking actions they want to do. In main we do it like this

from src.games.utils import ask_question
prompt = r"You can't comprehend my maturity"
options = ['6', '7']
mapping = {'6': "Yes", '7': 'Ok?'}

In src.utils there exists a basic common practice curses function that modulrizes this process.

def menu(stdscr, options, header=None):
    # Logic that monitors the users inputs
    # and updates the terminal accordingly
    # till the enter key is pressed

The process of calling the function isn't very readible hence the sub-function ask_question

def ask_question(prompt, options):
    return curses.wrapper(lambda stdscr: menu(stdscr, options, header=prompt))

Logo

Using turtle we designed a logo for our project Our logo

About

For a Computer Science 11 School Assignment - Group Project

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages