2
\$\begingroup\$

I made a quiz programme for my GCSE Computer Science NEA. I just want to make sure it is OK to submit.

There are also 5 files: Usernames, Passwords, Song Names, Artist Names and High Scores.

Any feedback is appreciated, positive or negative!

import random # Imports the random module
def readData(): # Load Files and Log In
    with open("Song Names.txt", "r") as f:# Opens the song file
        songNames = [line.rstrip('\n') for line in f]# Puts the song file into a list
        f.close()
    with open("Artist Names.txt", "r") as f:# Opens the artists file
        artistNames = [line.rstrip('\n') for line in f]# Puts the artists file into a list
        f.close()
    with open("Usernames.txt", "r") as f:# Opens the username file
        usernames = [line.rstrip('\n') for line in f]# Puts the username file into a list
        f.close()
    with open("Passwords.txt", "r") as f:# Opens the username file
        passwords = [line.rstrip('\n') for line in f]# Puts the username file into a list
        f.close()
    return songNames, artistNames, usernames, passwords # Returns the newly created list

def login(usernames, passwords):
    global enterUsername
    enterUsername = input("Enter username\n> ")# Prompts the user to enter their username
    if enterUsername in usernames: # If the username is in usernames...
        enterPassword = str(input("Enter password\n> ")) # Prompts the user to enter their password
        usernamePos = usernames.index(enterUsername) # Finds the position ofthe username
        if enterPassword in passwords: # If the password is in passwords...
            passwordPos = passwords.index(enterPassword) # Finds the position of the entered password
            if usernamePos == passwordPos: # If the password and the username is in the same position
                print ("You are logged in", enterUsername) # Log in!
                return True
            else: # If the password is not in the same position
                print("You entered the wrong password...") # Inform the user about their mistake
        else: # If the password is not in passwords...
            print("That password is not in the file...")# Inform the user about their mistake
    else: # If the username is not in usernames
        print("You're not allowed to play the game...") # Inform the user about their mistake
    return False # Tell the programme that the user did not log in

def chooseSong(songNames, artistNames): # Sets up the song requirew
    secretSong = random.choice(songNames) # Generates a random song
    sSP = songNames.index(secretSong) # Finds the position of the secret song
    secretArtist = artistNames[sSP] # Gets the correct artist from the song
    return secretSong, secretArtist # Returns the song or artist

def loadGame(secretSong, secretArtist): # Loads the game
    word_guessed = [] # return secretSong
    print ("The artist is ", secretArtist, "and the song is", secretSong) # Print the artists nam
    for letter in secretSong: # Prints the song name (censored, of course)
        if letter == " ": # If the ltetter is a space...
            word_guessed.append(" / ")  # Print a dash       
        else: # Otherwise...
            word_guessed.append("-") # Print a line
    for letter in word_guessed: 
        print (letter, end = " ")
    return secretSong # Returns the secret song

def playGame(secretSong, score): # Plays the game
    tries = 0 # Sets the amount of tries to 0
    while True:
        userGuess = str(input("\nWhat is the song name?\n> ")) # Gives the user the oppertunity to guess the song name
        if userGuess == secretSong: # If the user guesses correctly...
            print("Correct!") # Prints that they are correct
            if tries == 0: # If they got the song name correct on their first try
                score = score + 3 # Add 3 points to thesongNames = [line.rstrip('\n') for line in f] score
                print("You earnt 3 points! You have", score, "points in total.") # States that the user has got 3 points, and states the amount of points the user has in total
                time.sleep(1) # Wait a second
                return score, True # Return the score and True, to run the game again 
            elif tries == 1: # If they got the song name correct on their second try
                score = score + 1 # Add 1 point to the score
                print("You earnt 1 point! You have", score, "points in total.") # States that the user has got 1 point, and states the amount of points the user has in total
                time.sleep(1) # Wait a second
                return score, True # Return the score and True, to run the game again 
        else: # If the user guessed the song wrong
            tries = tries + 1 # You add a try to the programme
            if tries == 2: # while the tries are equal to 2
                print ("Game over! You got", score, "points!") # Prints the game over screen, showing the points they earned in the game (if any)
                time.sleep(3) # Waits three seconds
                return score, False # Returns False, to display the high scores
                break # Breaks from the while True loop
                print("You have 1 try left.") # Prints that the user has 1 try left
            else:
                print("Incorrect!") # Tells the user that they were wrong
                time.sleep(0.5) # Wait half a second 
                print("You have 1 try left.") # Prints that the user has 1 try left
def highScore(score, enterUsername):
    sTF1 = (enterUsername + " scored ") # Save To File part 1
    sTF2 = (str(score + " points.")) # Save To File part 2
    sTF = str("\n" + sTF1 + sTF2) # Save To File
    with open("Scores.txt", "r") as f: # Opens the score file
        highScores = [line.rstrip('\n') for line in f]
        f.close()# Writes the score to the high score file
    with open("Scores.txt", "a") as f:
        f.write(sTF)
        f.close()
    openScores = input("Would you like to see the score? Y/N\n> ")  # Gives the user the option to view the scores
    if openScores.lower() == ("y" or "yes"):
        print (highScores)
        time.sleep(5)
        quit()
    else:
        quit()

def main():
    songNames, artistNames, usernames, passwords = readData()# Reads the data from text files
    score = 0 # Set the score to 0
    success = login(usernames, passwords)# prompts the user to login
    if success == True: # If they log in successfully
        while True: 
            print ('''Rules of the game!
1) The name of the song is in all capitals
2) There must be no space after your entry
3) Have fun!''')
            secretSong, artist = chooseSong(songNames, artistNames) # Chooses a random song
            loadGame(secretSong, artist) # Creates the censored song name
            score, win = playGame(secretSong, score) # Plays the game
            if win == False: # If you lose the game
                highScore(score, enterUsername)
                break
main() #Plays the game!
\$\endgroup\$
  • 2
    \$\begingroup\$ As this is for an assignment, don't forget that when you submit it, you must acknowledge any help received. \$\endgroup\$ – Toby Speight Nov 15 '18 at 11:20
  • 3
    \$\begingroup\$ To clarify Toby's point: not doing so would be academic misconduct in many schools and universities. People have got failing marks or dismissals for that. \$\endgroup\$ – Mast Nov 15 '18 at 12:27
3
\$\begingroup\$

A few general comments first:

  • Python has an official style-guide, PEP8. It recommends using lower_case for both functions and variables. It also recommends no space between print and (.
  • Don't write obvious comments like this (unless your class requires each line to be commented, in that case realize that that is a stupid requirement and the reason it is stupid is that you end up writing comments like this):

    if enterUsername in usernames: # If the username is in usernames...
    if letter == " ": # If the ltetter is a space...
    else: # Otherwise...
    

Now, to get to the more salient points. You are doing this pattern quite often:

  1. Find the index of an element / choose a random element from list 1
  2. Find the matching element in list 2.

This is both slow (list.index is \$\mathcal{O}(n)\$) and can easily produce wrong results. Consider e.g. this example:

song_names = ["The Man Who Sold The World", "The Man Who Sold The World"]
artist_names = ["David Bowie", "Nirvana"]

If you randomly choose the second song, you will still always get the first artist, since list.index always returns the first match.

Instead either make your data a list of tuples of song titles and artists:

def random_song(song_names, artist_names)
    songs = list(zip(song_names, artist_names))
    return random.choice(songs)

Or choose a random index in the first place:

def random_song(song_names, artist_names)
    i = random.randrange(len(song_names))
    return song_names[i], artist_names[i]

Instead of iterating over word_guessed, just str.join the list:

def load_game(secret_song, secret_artist):
    """Starts a new game with the given secrets."""
    print ("The artist is {secret_artist} and the song is {secretSong}")
    word_guessed = [" / " if letter == " " else "-" for letter in secret_song]
    print(" ".join(word_guessed))
    return secret_ong

Here I also used the new f-string (Python 3.6+) and a list comprehension with a ternary operator used inside.

You should put the call to main under a if __name__ == "__main__": guard to allow importing from this script from another script.

\$\endgroup\$
3
\$\begingroup\$

When you are using with statement call to close is redundant. Once the flow comes out of with expression, closing the file is taken care. Check out the pep for more details on this.

Avoid global variables

We are first reviewing if the username is in our list and then trying to get its index. A quick way to do it would be to use exception handling,

try:
    usernamePos = usernames.index(username)
except ValueError:
    # username is not in the list
    print("you are not allowed.")

Use doc strings to document the functions, than adding comment to the right side of the function definition. for eg.

def readData():
    """Load Files and Log In."""

Tricky use of openScores.lower() == ("y" or "yes"). Try 'y' or 'yes' in python interpreter to understand this.

When dealing with boolean it is replication to specify it in the statement. it is sufficient to say if success as it is already an evaluated boolean.

\$\endgroup\$

protected by Vogel612 Jun 27 at 10:56

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.