Why should I "use inline comments sparingly"? - python

Reading PEP8 (https://www.python.org/dev/peps/pep-0008/#comments), I see that programmers should "use inline comments sparingly". No reason for this is provided, though.
I wonder why. I really find it much easier to comprehend a program by reading the code with the comments inline, so that I'm not interrupted by comments when I'm actually trying to read and understand the code.
The best case would be to have comments like we do in Word, matched to a certain point in code, but visible only in a side panel. I don't know if there's an IDE with such features.
That being said, the questions:
Is there a technical reason, such as loss in performance, to prefer making annotations in block, instead of inline?
Will people reading my code be mad about the many inline comments or that's not a big deal, provided that the comments are actually useful.
Is there an IDE with a better commenting environment? Maybe a side panel, maybe a .txt file that is bonded with the .py file and show these comments through its UI. Idk, anything.
Reading PEP, Python documentation and tried googling it up. No useful answers.
(Sorry if too bad. Kind of a beginner in Python 3.)
guessed_char = len(secret_word)*["_ "] # Defines varLIST of STRs, masked version of
# secret_word, and also the progression
# shown to the player
answer = len(secret_word)*[""] # Defines varLIST of STRs, that will be checked
# against secret_word (varSTR) through
# "".join(answer)
guessed_attempts = [] # Defines varLIST of STRs, that will store
# every letter guessed by the player.
enforcou = False # Defines varBOOL, turns True if attempts
# (varINT) reaches the limit. Ends the game
# with a loss.
acertou = False # Defines varBOOL, turns True if word is
# found by player. Ends the game with a win.
pontos = int(100) # Defines varINT, counts points that will be
# deduced at every failend attempt.Every
# change to this value must be followed by
# changes in other parts of this code
# Asks player for difficulty level
print("Qual nível de dificuldade?\n 1 - 3 tentativas\n 2- 5 tentativas\n 3 - 10 tentativas")
dificuldade = int(input())
if (dificuldade == 1):
max_attempts = 3 # Define varINT, total number of attempts
elif (dificuldade == 2):
max_attempts = 5 # Define varINT, total number of attempts
else:
print("Vamos jogar no fácil...")
max_attempts = 10 # Define varINT, total number of attempts
attempts = 1 # Define varINT, counts the number of attempts
print("".join(guessed_char)) # Prints the masked version of secret_word
# (varSTR) that the player is trying to guess
while (not enforcou and not acertou): # Conditions for ending the game.
print(f"Tentativa {attempts} de {max_attempts}") # Tells player the attempt number
guess = input("Qual letra? ").lower() # Asks for a guess (varSTR)
guessed_attempts.append(guess) # Adds the guess to the guess' storage
if (guess in secret_word): # If the guess is correct
index = 0 # Defines varINT for storing indexes
for char in secret_word.lower(): # iterate through every character in
# secret_word (varSTR)
if (guess == char): # If the guess matches the character
guessed_char[index] = f"{guess} " # Replaces dummy "_ " in
# guesed_char (varLIST)
# revealling guessed characters
# in the masked progresion shown
# to the player
answer[index] = f"{guess}" # Replaces dummy with answer
print(f"Encontrei a letra {char} na posiçao {index}") # Prints that something was correct
print("".join(guessed_char)) # Prints the update masked word
pontos += round(100/max_attempts) # Adds points
if("".join(answer) == secret_word): # Checks if the player won
acertou = True # End while cicle if player won
print(f"Você ganhou! A palavra era '{secret_word}'!") #Prints message telling player he/she won
index += 1 # Adds 1 to varINT index, so everything is on track
else: # If guess is not correct
pontos -= round(100/max_attempts) # Subtracts points
print("Que pena, você errou...") #Tells player he missed
print("".join(guessed_char)) # Print masked word
# Tells player the guesses he already tried, with correct grammar for linstings in portuguese
print("Você já tentou as letras ", ", ".join(str(e) for e in guessed_attempts[:-1]), " e ", guessed_attempts[-1], ".", sep="")
attempts += 1 # Adds 1 to varINT attempts, so the game get closer to end due to loss
enforcou = attempts == max_attempts+1 # Defines coditions for losing the game
if (enforcou): # If losing conditions are met
print(f"Que pena, você perdeu... A palavra era '{secret_word}'...") # Print loss message and the word
print(f"Você fez {pontos} pontos.") # Prints the number of points, regardless of winning or losing.
print ("Fim do jogo") # Tells the player the game ended.

I think a simple reasoning against inline comments is maximum line length is 79 characters https://www.python.org/dev/peps/pep-0008/#id17, so you not want to wrap your code around too much to fit inline comments.
Also you have other ways to add comments like docstrings (https://www.python.org/dev/peps/pep-0257/) which might cover what the variables do.

Related

Multiple changes to the same variable within different if statements

SOLVED: I read through my code, it was a 'bug'. When I copied the dice roll method from the 'player character', since it uses the same mechanics for the enemies, I set the damage to 0 if it rolls with one die on accident.
Beginner here. (Python crash course halfway of chapter 9)
I am trying to build a simple turn based text game to practice (classes,if statement, modifying dictionaries/lists etc).
I will copy two snippets from my code, so you can understand my problem better.
(I'm really sorry that I can't give a short description, my best try was the title, but that still doesn't make it good enough. If you want an abridged tldr, go to the bottom with the bold texts.)
First, I have two characters, that you can choose from as an if-elif-else statement.
I used the same "player_xy" (xy being like health, damage etc) for the two characters, but assigning different values to them based on the player's choice. (My reasoning being is so I only have to reference the same variable in the code later in the battle system, making my job easier.)
(The variables fighter_max_hp.. etc are defined earlier, but it doesn't matter (tried moving it to before/inside the if statements.)
while select_repeat == True:
print("Type 'f' for fighter , 'm' for mage, or 'q' to quit!")
character = input("TYPE: ")
#player chooses fighter
if character == 'f':
player_max_hp = fighter_max_hp
player_max_mana = fighter_max_mana
#this goes on for a while, setting up all the stats
#player chooses mage
elif character == 'm':
player_max_hp = mage_max_hp
player_max_mana = mage_max_mana
#this goes on for a while, setting up all the stats
#player chooses to quit
elif character == 'q':
select_repeat = False
#invalid input
else:
print("\nPlease choose a valid option!")
Later in the code, I have a part where a randomizer sets up enemies to fight.
I used the same "enemy_xy" (xy being like health, damage etc) for the enemies. (My reasoning was the same here as for the characters.)
(Same, as with the player variables (tried moving it to before/inside the if statements.)
while enemy_select == True:
#game chooses an enemy to fight!
min = 1
max = 3
enemy_chooser = int(random.randint(min, max))
if enemy_chooser == 1:
#choose werewolf
enemy_hp = werewolf_hp
enemy_dice = werewolf_dice
#this goes on for a while, setting up all the stats
if enemy_chooser == 2:
#choose lesser mimic
enemy_hp = int(player_max_hp / 2)
enemy_dice = player_dice
elif enemy_chooser == 3:
#choose zombie
enemy_hp = zombie_hp
enemy_dice = zombie_dice
#this goes on for a while, setting up all the stats
Keep in mind, all of these enemies use the same "enemy_hp", "enemy_dice" etc. variables, within the same battle system, just assigned as "enemy_hp = werewolf_hp" or "enemy_hp = "zombie_hp".
The fight happens, and:
If your enemy is the werewolf:
you deal damage to it
you receive damage from it
you can kill it
you can get killed by it
If your enemy is the lesser mimic:
you deal damage to it
you can ONLY receive damage from it if you are a fighter (mage's hp doesn't decrease)
you can kill it
you can ONLY get killed by it if you are a fighter (obviously, since it doesn't deal damage to mage hp)
If your enemy is the zombie:
you deal damage to it
you CAN NOT receive damage from it (not the fighter, or the mage)
you can kill it
you can not get killed by it (obviously, since no damage)
Otherwise, it prints out the different variable values as assigned (different stats for each monster) as expected, and it uses correct calculations to deal damage.. it just can't in the two cases mentioned above.
Now comes the main part of my question...
If I change the variables like this:
elif enemy_chooser == 2:
#choose zombie
enemy_hp = werewolf_hp ##CHANGE
enemy_dice = werewolf_dice ##CHANGE
#this goes on for a while, setting up all the stats
Then the zombie can finally deal damage to the player (with the werewolf's stats).
It's as if because the lines
enemy_hp = werewolf_hp
enemy_dice = werewolf_dice
#etc
are written earlier than:
enemy_hp = zombie_hp
enemy_dice = zombie_dice
#etc
it somehow effects the variable (regardless or not if the "if" statement is true).
because werewolf_xy was defined earlier than zombie_xy
#enemy werewolf defined first in the code
werewolf_hp = 20
werewolf_dice = 2
#etc
#enemy zombie defined right after
zombie_hp = 35
zombie_dice = 1
#etc
Same happens with the fighter and mage selection.
Somehow the player_hp = xy_hp only works if xy = fighter, because the fighters variables are defined earlier in the code, and thus making the "lesser mimic" deal damage only to the fighter.
My question is "simply".. why?
I tried everything in my power, to no avail.
As you have seen, I could identify what causes the problem (and thus I >could< potentionally work around it), but I still don't know why Python does what it does, and that bothers me.
Any help or input from more experienced users would be greatly appreciated.
Thank you in advance!
Tankerka
You have a bug.
There's not enough details in this (long!) narrative to identify the bug.
Here's how you fix it:
breakpoint()
Put that near the top of your code,
and use n next, plus p print var,
to see what your code is doing.
It is quicker and more flexible than print( ... ).
Read up on that pair of commands here:
https://docs.python.org/3/library/pdb.html
Separate item: refactor your code as you go along.
You're starting to have enough if / elif logic
that it won't all fit in a single screenful
with no scrolling.
That suggests that it's a good time to use def
to break out a helper function.
You might def get_enemy_hp( ... ):, for example,
and also define get_enemy_dice().
Other things you might choose to study:
a class can be a good way to organize the variables you're defining -- embrace the self syntax!
a dict could map from enemy type to hp, or to dice
The nice thing about helper functions is they're
the perfect target for unit tests.
It takes a minute to write a test, but it winds up saving you time.
https://docs.python.org/3/library/unittest.html
When you identify and fix the problem, let us know!
https://stackoverflow.com/help/self-answer

Index Out Of Range When Artificially Limited

when I run this program, sometimes I receive an error.This error however is not possible as I am using an 8x8 grid and I limit the inputs so that they can only be numbers from 0-7, to obey the fact that list indexes start at 0.
The user must input coordinates (1-8),(A-H) and the program will check to see if those coordinates are correct, by systematically going through the CompShips list and repeatedly comparing those coordinates to ones given by the user. If the cords match, then a message will appear and a "Z" will change to an "X" on those coordinates, indicating a HIT. If the guess does not match, a "Z" will change to an "M" on those coordinates indicating a MISS.
CompShips=[[1,0],[1,1],[2,2],[2,3],[2,4],[3,0],[3,1],[3,2],[5,4],[5,5],[5,6],[5,7],[1,7],[2,7],[3,7],[4,7],[5,7]]
FRow1=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow2=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow3=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow4=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow5=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow6=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow7=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow8=["Z","Z","Z","Z","Z","Z","Z","Z",]
def PrintFireBoard():
print(Index)
print(FRow1)
print(FRow2)
print(FRow3)
print(FRow4)
print(FRow5)
print(FRow6)
print(FRow7)
print(FRow8)
FireBoard=[FRow1,FRow2,FRow3,FRow4,FRow5,FRow6,FRow7,FRow8]
while len(CompShips) !=0 or CompSuccess==17:
FireRow=input("Please Choose The Row That You Wish To Fire Upon (1-8) ")
FireIndex=input("Please Choose The Column That You Wish To Fire Upon (A-H) ")
#As Lists start at 0
FireRow=int(FireRow)-1
if FireIndex==("A"):
FireIndex=0
elif FireIndex==("B"):
FireIndex=1
elif FireIndex==("C"):
FireIndex=2
elif FireIndex==("D"):
FireIndex=3
elif FireIndex==("E"):
FireIndex=4
elif FireIndex==("F"):
FireIndex=5
elif FireIndex==("G"):
FireIndex=6
elif FireIndex==("H"):
FireIndex=7
Guess=[FireRow,FireIndex]
#Check To See If Correct
UserSuccess=0
for i in CompShips:
if Guess==i:
CompShips.remove(Guess)
UserSuccess=1
else:
pass
if UserSuccess==1:
print("HIT")
print(FireRow)
print(FireIndex)
FireBoard[[FireRow][FireIndex]]=("H")
PrintFireBoard()
else:
print("MISS")
print(FireRow)
print(FireIndex)
FireBoard[[FireRow][FireIndex]]=("M")
PrintFireBoard()
I receive the error:
IndexError: string index out of range
Looks like these two lines
FireBoard[[FireRow][FireIndex]]=("H")
FireBoard[[FireRow][FireIndex]]=("M")
should be
FireBoard[FireRow][FireIndex]="H"
FireBoard[FireRow][FireIndex]="M"
Explanation: In your old code, FireBoard[[FireRow][FireIndex]]=("H")
[FireRow][FireIndex] means, given a list [FireRow] (which contains just one element), get the FireIndex-th element. This is not what you're trying to do.
For example [3][0] returns 3, and [3][1] gives IndexError.
Take a look at How to define a two-dimensional array in Python
Also note that ("H") is the same as the string "H". There is no need to add parentheses.
Here is a much cleaner code!
CompShips=[[1,0],[1,1],[2,2],[2,3],
[2,4],[3,0],[3,1],[3,2],
[5,4],[5,5],[5,6],[5,7],
[1,7],[2,7],[3,7],[4,7],
[5,7]]
FRow=[["Z"]*8]*8 #1 More Pythonic
def PrintFireBoard():
#print(Index)
for i in range(0,8):
print(FRow[i])
FireBoard=FRow[:] #NOTE THIS ONE!!!
mydict = {}
for i,key in enumerate(["A","B","C","D","E","F","G","H"]): #2 More Pythonic
mydict[key] = i
while len(CompShips) !=0 or CompSuccess==17:
FireRow=input("Please Choose The Row That You Wish To Fire Upon (1-8) ")
FireIndex=input("Please Choose The Column That You Wish To Fire Upon (A-H) ")
FireRow=int(FireRow)-1
FireIndex = mydict[FireIndex]
Guess=[FireRow,FireIndex]
print(Guess)
UserSuccess=0
for i in CompShips:
if Guess==i:
CompShips.remove(Guess)
UserSuccess=1
else:
pass
if UserSuccess==1:
print("HIT")
print(FireRow,FireIndex)
FireBoard[FireRow][FireIndex]="H" #3 your problem here
PrintFireBoard()
else:
print("MISS")
print(FireRow,FireIndex)
FireBoard[FireRow][FireIndex]="M"
PrintFireBoard()
1) As explained in the comments that's just a more nicer way to create a list of lists!. Remember DRY principle! Do Not Repeat yourself!
2) Instead of having all that if else to convert the 'A' to 0. You can use a dictionary lookup instead!
3) Your problem seems to be here! correct this to FireBoard[FireRow][FireIndex]="H"
PS: NOTE THIS ONE!!!: I'm not just making FireBoard as an alias to FRow! I'm copying it into a FireBoard as a new list! There's a subtle difference read about it here. I'm doing this incase you don't want your original FRow list to be modified!
The indentation in your question was off. I think that all the code from
Guess=[FireRow,FireIndex]
until the end should be preceded by 4 spaces.
I've removed print(Index) since it was not defined.
To access FireBoard use:
FireBoard[FireRow][FireIndex]
Instead of
FireBoard[[FireRow][FireIndex]]
This should be working
CompShips=[[1,0],[1,1],[2,2],[2,3],[2,4],[3,0],[3,1],[3,2],[5,4],
[5,5],[5,6],[5,7],[1,7],[2,7],[3,7],[4,7],[5,7]]
FRow1=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow2=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow3=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow4=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow5=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow6=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow7=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow8=["Z","Z","Z","Z","Z","Z","Z","Z",]
def PrintFireBoard():
print(FRow1)
print(FRow2)
print(FRow3)
print(FRow4)
print(FRow5)
print(FRow6)
print(FRow7)
print(FRow8)
FireBoard=[FRow1,FRow2,FRow3,FRow4,FRow5,FRow6,FRow7,FRow8]
while len(CompShips) !=0 or CompSuccess==17:
FireRow=input("Please Choose The Row That You Wish To Fire Upon (1-8) ")
FireIndex=input("Please Choose The Column That You Wish To Fire Upon (A-H) ")
#As Lists start at 0
FireRow=int(FireRow)-1
if FireIndex==("A"):
FireIndex=0
elif FireIndex==("B"):
FireIndex=1
elif FireIndex==("C"):
FireIndex=2
elif FireIndex==("D"):
FireIndex=3
elif FireIndex==("E"):
FireIndex=4
elif FireIndex==("F"):
FireIndex=5
elif FireIndex==("G"):
FireIndex=6
elif FireIndex==("H"):
FireIndex=7
Guess=[FireRow,FireIndex]
#Check To See If Correct
UserSuccess=0
for i in CompShips:
if Guess==i:
CompShips.remove(Guess)
UserSuccess=1
else:
pass
if UserSuccess==1:
print("HIT")
print(FireRow)
print(FireIndex)
FireBoard[FireRow][FireIndex]=("H")
PrintFireBoard()
else:
print("MISS")
print(FireRow)
print(FireIndex)
FireBoard[FireRow][FireIndex]=("M")
PrintFireBoard()

Creating Decision Tree for Simple Game

I am currently a new student learning python. This is my first real experience doing much computer coding. For my project I must create a fill in the blank quiz with three different levels of difficulty. Once the user chooses a difficulty the game should print a different paragraph based on the difficulty. Each section of the game works fine but I am having trouble creating the "difficulty chooser." No matter the difficulty I choose, the game rolls through the easy, medium, and the hard level in order and then crashes.
Below I have included the introductory text and the difficulty chooser. I would love some help. I am sure there are really obvious things I don't see. Thank you!
def introduction():
print '''Welcome to Kevin's European Geography Quizzes.
Test your knowledge of European geography. \n'''
difficulty = raw_input('''Do you want to play an easy, medium, or hard game?
Please type the number 1 for easy, 2 for medium, or 3 for hard.\n''' )
game_chooser(difficulty)
def game_chooser(difficulty):
cursor = 0
difficulty_choice = [easy_game(), medium_game(), hard_game()]
#each element of the above list links to a procedure and starts one of the
#mini-games.
while cursor < len(difficulty_choice):
if difficulty != cursor:
cursor += 1
else:
difficulty_choice[cursor]
break
You can do with if else if you only want to print something but if you have separate code block for each level then define a function for each level and use this pattern :
You can define the function blocks and call them basis on user input something like:
# define the function blocks
def hard():
print ("Hard mode code goes here.\n")
def medium():
print ("medium mode code goes here\n")
def easy():
print ("easy mode code goes here\n")
def lazy():
print ("i don't want to play\n")
# Now map the function to user input
difficulty_choice = {0 : hard,
1 : medium,
4 : lazy,
9 : easy,
}
user_input=int(input("which mode do you want to choose : \n press 0 for hard \n press 1 for medium \n press 4 for lazy \n press 9 for easy "))
difficulty_choice[user_input]()
Then invocation of function block will be:
difficulty_choice[num]()
Add a conditional for the input.
if difficulty == 'easy':
print("here is an easy game")
elif difficulty == 'medium':
print('here is a medium game')
elif difficulty == 'hard':
print('here is hard')
else:
print('Please enter valid input (easy, medium, hard)')
Under each if statement put your game code.
The reason your code goes through all the difficulties is because of this line:
difficulty_choice = [easy_game(), medium_game(), hard_game()]
When Python sees something like easy_game(), it calls the easy_game function and replaces it with the result. You don't want to call the function yet though, so you can take off the parenthesis to store just the function instead:
difficulty_choice = [easy_game, medium_game, hard_game]
This will mean you have to call the function after you take it out of the array.
As for the crash, when you use raw_input() you get a string back. That means when you type in the 1 to decide for an easy game, you get the character 1, which is represented by the number 49. That's why your code goes through everything and crashes: Your 1 is really a 49. In fact, if you type 1 < '1' into the interpreter, you'll get True back.
To fix that, you can pass the result of raw_input() to the int() function, which will parse it and give you the proper integer (or throw an exception if it can't be parsed). The last line of introduction would then look like game_chooser(int(difficulty)).
You could also skip most of the code of game_chooser by just indexing into the array (that's what they're for, after all):
def game_chooser(difficulty):
# the lack of parens here means you get the function itself, not what it returns
difficulty_choice = [easy_game, medium_game, hard_game]
#each element of the above list links to a procedure and starts one of the
#mini-games.
# note the parens to actually call the retrieved function now
difficulty_choice[difficulty]()

Can variables retain previous values in a while loop in python? [duplicate]

This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 6 years ago.
My question is when we use a variable in a while loop before changing the variable we have it assigned to (i.e on the Right hand side of the equal to) why does this new variable we supposedly assigned the next variables previous value to change?
I realise the phrasing of my question isn't entirely spot on, so in laymans terms, in my program i'm writing a variable called predispto disp before I change the value of displater in the while loop. Here i'm assuming all the code in python runs from top to bottom.
So here's an example of what value predisp holds
so if disp = ['_','_']
predisp = ['_','_']
which is fine.
But the moment I enter a letter as part of my hangman guess
the value of disp becomes ['u','_']
but the problem is predisp also becomes ['u','_'] which is not what I want. I want it to always have the previous value of disp before it undergoes any changes. I'm new to python so I don't really understand how all the variables work, i'm more used to them in C++. Here's the code (it's for a simple hangman game i'm writing).
# Created by Zur-en-Arrh
import random # Useful to select a topic from the file.
# Functions
def same_letter(user_letter, word_to_guess):
if user_letter == word_to_guess:
return True
else:
return False
def wrong_guess(prevdisp,currdisp):
if prevdisp == currdisp:
return True
else:
return False
# Dealing with the file.
filename = input("Which file do you want to play with ")
topics = str(open(filename, 'r').read())
list_of_topics = topics.split() # This is the list that contains the topics randomly selected from the file.
guess_me = list(list_of_topics[random.randint(0, len(list_of_topics) - 1)]) # This is what the user will need to figure out.
# Printing out the Dashes for the user.
disp = []
for i in range(0, len(guess_me)):
disp.append("_")
# This is just the declaration of the number of wrong guesses. This'll always be 0 at the start of the game.
wrong_guesses = 0
# While loop for game. Also note in hangman, you're only allowed 5 wrong guesses till the body is complete.
while wrong_guesses < 6:
print(' '.join(disp)) # Prints the game in an acceptable format to the user.
predisp = disp
if disp == guess_me: # end the game when the user wins.
break
user_guess = str(input("Which letter do you think will there be? "))
for i in range(len(guess_me)):
if same_letter(user_guess, guess_me[i]):
disp[i] = user_guess
print(predisp)
if wrong_guess(predisp, disp):
wrong_guesses += 1
if wrong_guesses == 6:
print("You got hung! Better luck next time")
break
if wrong_guesses < 6:
print("Well Done you won the game!")
In Python, variables are references to objects:
disp = []
creates a new list object and makes it accessible by the name disp. What it really does is set disp to point to the newly created list object. The assignment statement
predisp = disp
does the same thing, i.e. it sets predisp to reference the same list object as disp. Thus any change applied to the object that disp points to is also visible in the object that predisp points to - it's the very same object.
One way to avoid this is to create a copy on assignment:
predisp = disp[:]
This can be easily verified by using the id function:
disp = ['_'] * 3
predisp = disp
id(disp), id(predisp)
# same object ids for both variables
=> (4303250784, 4303250784)
predisp = disp[:]
id(disp), id(predisp)
# different object ids
=> (4303250784, 4303043832)

Python program doesn't quit; import random causing errors

Forgive me if this comes out a bit scatter-brained, I'm not exaggerating when I say I've been working on this program for over 13 hours now and I am seriously sleep deprived. This is my 4th revision and I honestly don't know what to do anymore, so if anyone can help me, it would be greatly appreciated. My introduction to programming teacher wanted us to make a "flash card" study program from his template. I am using Idle 3.3.3 on a windows 7 machine.
#Flash Cards
#Uses parallel arrays to store flash card data read from file
#Quizzes user by displaying fact and asking them to give answer
import random
def main():
answer = [] #array to store answer for each card
fact = [] #array to store fact/definition for each card
totalTried = 0 #stores number of cards attempted
totalRight = 0 #stores number of correct guesses
loadCards(answer, fact) #call loadcards() and pass it both arrays
numCards = len(answer) #find number of cards loaded
keepGoing = "y"
while keepGoing == "y" or keepGoing == "Y":
#Enter your code below this line
# 2a. Pick random integer between 0 and numCards and store the
# number in a variable named randomPick.
randomPick = random.randint (0, numCards)
# 2b. Add one to the totalTried accumulator variable.
totalTried = totalTried + 1
# 2c. Print element randomPick of the fact array. This shows the
# user the fact/definition for this flashcard.
print (fact [randomPick] )
# 2d. Prompt the user to input their guess and store the string they
# enter in a variable named "userAnswer"
userAnswer = input ('What is your answer?' )
# 2e. Compare the user's guess -userAnswer- to element
# -randomPick- of the answer array.
if userAnswer == (answer [randomPick]):
# 2e-1 If the two strings are equal, tell the user they
# guessed correctly and add 1 to the totalRight
# accumulator variable.
print ('That is correct.')
totalRight == totalRight + 1
# 2e2. If the two strings are not equal, tell the user they guessed
# wrong and display the correct answer from the answer array.
else:
print ('That is incorrect.')
print (answer [randomPick])
#2f. Prompt the user the user to see if they want to continue and
#store their response in the keepGoing variable.
keepGoing = input ('Would you like to continue?')
#Enter your code above this line
print("You got", totalRight, "right out of", totalTried, "attempted.")
def loadCards(answer, fact):
#Enter your code below this line
# 1a. Open flashcards.txt in read mode & assign it var name "infile"
infile = open('flashcards.txt', 'r')
# 1b. Read 1st line from file and store in var. name "line1"
line1 = infile.readline ()
# 1c. Use while loop to make sure EoF has not been reached.
while line1 != '':
# 1c1. Strip newline escape sequence (\n)from variable's value.
line1 = line1.rstrip ('\n')
# 1c2. Append string to answer array.
answer.append (line1)
# 1c3. Read next line from file and store in var. name "line2"
line2 = infile.readline ()
# 1c4. Strip newline escape sequence (\n) from variable's value.
line2 = line2.rstrip ('\n')
# 1c5. Append the string to the fact array.
fact.append (line2)
# 1c6. Read next line from file and store it in var. name "line3".
line3 = infile.readline ()
# 1d. Close file.
infile.close()
#Enter your code above this line
main()
When I run the program nothing actually happens, but when I try to close the shell window afterwards, it tells me that the program is still running and asks if I want to kill it.
Debugger shows me no information when I try to check it, also.
However, if I copy the code into the shell and run it from there, I get "SyntaxError: multiple statements found while compiling a single statement". Neither file has changed, but earlier it was telling me there was a problem with "import random".
Thanks in advance for any help.
I took a quick look and it mostly seems okay to me. I changed input() to raw_input() (two of them in your code) and noticed you had a double equals when you probably meant a single one
line 36:
totalRight == totalRight + 1
changed to
totalRight = totalRight + 1
which fixes your correct answer counter and line 68:
line3 = infile.readline ()
changed to
line1 = infile.readline ()
else it gets caught in your while loop forever. And I just copied line 54:
line1 = infile.readline ()
and pasted it so it is there twice to add another readline() call, just a lazy way of skipping the first line in your text file, since it seems to be a comment and not part of the answers and questions. You probably don't want to do that and just remove the comment from your text file. =b
With those changes, it seems to work fine for me.
Since this is for a class (and I can't only comment, I can just answer) I want to add that there actually is such a thing as too many comments
These comments (and to be honest, most of your comments) are distracting and unnecessary
answer = [] #array to store answer for each card
fact = [] #array to store fact/definition for each card
totalTried = 0 #stores number of cards attempted
totalRight = 0 #stores number of correct guesses
loadCards(answer, fact) #call loadcards() and pass it both arrays
numCards = len(answer) #find number of cards loaded
Also, the whole point of putting your program inside of a function called main is so you can run that function only if you are calling that file directly and you should probably put
if __name__ == '__main__':
main()
at the bottom of your code instead of just
main()
Use of input() is generally considered dangerous (unless you're using Python3 or later where it is the same as raw_input()) due to the fact that it evaluates the input. You should handle the type yourself with something like, if you want an integer,
foo = int(raw_input('Input a number: '))
(Note that the return of raw_input is a string, so if you want a string you don't have to do anything)

Categories