I am beginning to learn Python and started experimenting with an example code block. I edited it a few times, and on the last edit that I did, I added an optional random password generator. Then I decided that it would make more sense to put the password generator into a separate document, so I copied the necessary code and made a new document. After editing it however, I cannot generate an even number of digits in the password.
Pastebin
Copy of Faulty Code (Pastebin)
import math
import random
alpha = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
print('Would you like a random password suggestion generator', 'Yes or No')
permissionRandomGenerator = input().lower()
print('How long do you want your password?')
lengthRandomGenerator = int(input())
if permissionRandomGenerator == 'yes':
def randInt():
return math.floor(random.random()*10)
def randChar():
return alpha[math.floor(random.random()*27)]
randPasswordList = []
listInsert = 0
def changeCase(f):
g = round(random.random())
if g == 0:
return f.lower()
elif g == 1:
return f.upper()
while listInsert < lengthRandomGenerator:
randPasswordList.insert(listInsert, randInt())
listInsert = listInsert + 1
if listInsert >= lengthRandomGenerator:
break
randPasswordList.insert(listInsert, randChar())
randPasswordList[listInsert] = changeCase(randPasswordList[listInsert])
listInsert = listInsert + 1
continue
listInsert = 0
printList = 0
if lengthRandomGenerator <= 0:
print('It has to be longer than that')
elif lengthRandomGenerator >= 25:
print('I can\'t generate a password that long')
elif math.isnan(lengthRandomGenerator):
print('error: not valid data type')
else:
while printList < (len(randPasswordList)-1):
printItem = randPasswordList[printList]
print(printItem)
printList = printList + 1
printList = 0
randPasswordList = []
elif permissionRandomGenerator == 'no':
print('Too bad...')
else:
print('You had to answer Yes or No')
I refactored your program a bit, and got rid of a lot of unnecessary steps and inconsistencies. Here it is in full, then I'll explain each part:
import random
import string
import sys
possible_chars = string.ascii_letters + string.digits + string.punctuation
def nextchar(chars):
return random.choice(chars)
yes_or_no = input("""
Would you like a random password suggestion generated?
Type Yes to continue: """).lower()
if yes_or_no == 'yes':
try:
pwd_len = int(input('How long do you want your password? '))
except ValueError:
sys.exit("You need to enter an integer. Please start the program over.")
if 0 < pwd_len < 26:
new_pwd = ""
for _ in range(pwd_len):
new_pwd += nextchar(possible_chars)
print("Your new password is:\n" + new_pwd)
else:
print("I can only generate passwords between 1 and 25 characters long.")
else:
print("Well then, why did you run me?")
Python is not just the syntax and builtin functions, it is also the standard library or stdlib. You're going to be working with the stdlib's modules all the time, so when you think you'll be using one, read the docs! You'll learn about the module, what its intended use is, some of its history and changes (such as in which version a certain function was added), and all of the classes, functions, and attributes contained therein. Make sure you read the whole thing (none of them are that long) and try to get at least a basic idea of what each thing does. That way, such as in this case, you'll be able to pick the best function for the job. One thing I like to do in my spare time is just pick a random module and read the docs, just to learn. They're generally fairly well written, and usually pretty inclusive. Get used to Monty Python references, they're everywhere.
import random
import string
import sys
Imports are first, and should almost always be only at the top. I like to put mine in alphabetical order, with the stdlib on top, then a blank line, then 3rd-party modules, including self-written ones next. Put a blank line or two after the imports as well. One thing to remember, that I mentioned in the comments: readability counts. Code is not only meant to be read by machines, but by people as well. Comment when necessary. Be generous with whitespace (also remember that whitespace is syntactically important in Python as well, so it forces you to indent properly) to separate related bits of code, functions, classes, blocks, etc. I highly recommend reading, rereading, and spending time pondering PEP-8, the Python style guide. Its recommendations aren't absolute, but many projects that enforce coding standards rely on it. Try to follow it as much as you can. If a line comes out to 83 characters, don't sweat it, but be aware of what you're doing.
The reason I made such a big deal out of reading the docs is the following few lines:
possible_chars = string.ascii_letters + string.digits + string.punctuation
def nextchar(chars):
return random.choice(chars)
They get rid of about half of your code. string contains a bunch of predefined constants for working with strings. The three I chose should all be good valid password characters. If you're on a system that won't take punctuation marks, just remove it. Note that possible_chars is a string - like tuples, lists and dicts, strings are iterable, so you don't need to make a separate list of each individual possible character.
Next is the function - it replaces your randInt(), randChar(), and changeCase() functions, along with a bunch of your inline code, which was rather bizarre, to tell you the truth. I liked the method you came up with to decide if a letter was upper- or lower-case, but the rest of it was just way too much effort when you have random.choice() and the string constants from above.
yes_or_no = input("""
Would you like a random password suggestion generated?
Type Yes to continue: """).lower()
You may not have been aware, but you don't need to print() a description string before getting user input() - just pass the string as a single argument to input() and you'll get the same effect. I also used a triple-quoted """ (''' can also be used) string literal that differs from the more common single- ' and double-quoted " string literals in that any newlines or tabs contained within it don't need to be escaped. The take-home for now is that you can write several lines of text, and when you print() it, it will come out as several lines.
try:
pwd_len = int(input('How long do you want your password? '))
except ValueError:
sys.exit("You need to enter an integer. Please start the program over.")
I used a try/except block for the next part. If the user enters a non-integer up at the input prompt, the int() function will fail with a ValueError. I picked the simplest manner possible of dealing with it: if there's an error, print a message and quit. You can make it so that the program will re-ask for input if an error is raised, but I figured that was beyond the scope of this exercise.
if 0 < pwd_len < 26:
new_pwd = ""
for _ in range(pwd_len):
new_pwd += nextchar(possible_chars)
print("Your new password is:\n" + new_pwd)
else:
print("I can only generate passwords between 1 and 25 characters long.")
Here is where all the action happens. Using an if/else block, we test the desired length of the password, and if it's between 1 and 25 (an arbitrary upper bound), we generate the password. This is done with a for loop and the range() function (read the docs for exactly how it works). You'll notice that I use a common Python idiom in the for loop: since I don't actually need the number generated by range(), I "throw it away" by using the underscore _ character in place of a variable. Finally, the else statement handles the alternative - either pwd_len is 0 or less, or 26 or greater.
else:
print("Well then, why did you run me?")
We're at the end of the program! This else is paired with the if yes_or_no == 'yes': statement - the user entered something other than yes at the input prompt.
Hopefully this will help you understand a little bit more about how Python works and how to program efficiently using it. If you feel like you're spending a bit too much time implementing something that you think should be easier, you're probably right. One of Python's many advantages is its "batteries included" philosophy - there's a huge range of things you can do with the stdlib.
I made some small edits, and my code seems to be working now. Here is the finished product (I put comments to show what the code does, and also to mark the edits.):
import math
import random #Import necessary modules
alpha = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] #List with alphabet
print('Would you like a random password suggestion generator', 'Yes or No') #Prints the question for permission
permissionRandomGenerator = input().lower() #Stores the answer of the above question in lower case
if permissionRandomGenerator == 'yes': #Generates a password if the answer of the first question is 'yes'
print('How long do you want your password?') #Asks for length
lengthRandomGenerator = int(input()) #Stores length as an integer
def randInt(): #Creates a random integer
return math.floor(random.random()*10)
def randChar(): #Selects a random string from the list with the alphabet
return alpha[math.floor(random.random()*27) - 1]
randPasswordList = [] #Creates a list to store the password
listInsert = 0 #Creates a list index variable
def changeCase(f): #Defines a function to randomly change the case of letters before adding them to the list randPasswordList
g = round(random.random())
if g == 0:
return f.lower()
elif g == 1:
return f.upper()
while listInsert < lengthRandomGenerator + 1: #Creates a random password and inserts it into randPasswordList (I added `+ 1` here)
randPasswordList.insert(listInsert, randInt())
listInsert = listInsert + 1
if listInsert >= lengthRandomGenerator:
break
randPasswordList.insert(listInsert, randChar())
randPasswordList[listInsert] = changeCase(randPasswordList[listInsert]) #Calls the changeCase function whenever it inserts a letter
listInsert = listInsert + 1
continue
listInsert = 0
printList = 0
if lengthRandomGenerator <= 0: #If the length it 0 or less (for example, negatives) the password will not generate (I need to fix this a little bit. Currently the code attempts to create a password beforehand)
print('It has to be longer than that')
elif lengthRandomGenerator >= 25:
print('I can\'t generate a password that long')
elif math.isnan(lengthRandomGenerator): #Currently this doesn't do anything, it needs to be moved farther forward
print('error: not valid data type')
else:
while printList < (len(randPasswordList)-1): #Prints the list item by item
printItem = randPasswordList[printList]
print(printItem)
printList = printList + 1
printList = 0 #Resets the variables
randPasswordList = []
elif permissionRandomGenerator == 'no':
print('Too bad...')
else:
print('You had to answer Yes or No')
Note: I made this code purely to experiment and better learn basic aspects of Python. This code is not optimized, and is also not as random as I can (and will) make it.
P.S. Sorry if the comments are incomplete, I am still learning this language.
I don't know why you are doing over complicated for this simple problem, you can just use the constant provided by the string object, I would rather have the following programs to generate random password
import random, sys, string
def pgen(length=8):
if length < 8:
length = 8
keys = list(string.printable[:-6])
random.shuffle(keys)
return ''.join(keys)[:length]
if __name__ == '__main__':
try:
print( pgen(int(sys.argv[1])))
except Exception as e:
print("Provide length of password \n passwordgen.py <length_of_password>")
Outputs
magautam#nix1947:/tmp$ python passwordgen.py 12
HNLxi!{.qe=b
magautam#nix1947:/tmp$ python passwordgen.py 45
}w5u?+C=e[DfI.n'*1G(m{r0FH|UBKz/#kL>;Sh`tEW8-
Related
I need to test a function, similar to below, to check the prompts, and output, from the function. As you can see, there is some branching. My ideal goal is to be able to specify the values I want to try in the function, but also capture all of the output text along the way.
def prompt():
guess = input("guess a letter: ")
return(guess)
def testme():
i = 1
vocab = ['a', 'b', 'c','d', 'e', 'f']
import random
correct = random.choice(vocab)
while i < 4:
i = i + 1
guess = prompt()
if correct == guess:
print("Well done!")
i = 100
else:
print("Wrong!\n")
testme()
My need:
It's worth noting that you can see my function test intentionally does not take any arguments, but rather, is a function where the user has to interface with the prompts. I need to automate this part, and capture how the function reacts (the messages sent to the console) until the function ends.
I am trying to avoid manually running through this by hand to see if the function behaves the way I expect given the fact that the user can provide multiple inputs.
You can just take an argument and pass the list to it:
def testme(vocab):
i = 1
import random
correct = random.choice(vocab)
while i < 4:
i = i + 1
guess = prompt()
if correct == guess:
print("Well done!")
i = 100
else:
print("Wrong!\n")
And call it like:
testme(['a','b','c','d','e','f'])
Also you can take comma delimited values from user, split them and convert to list and pass to the function.
What do you mean by capturing the output? Do you want to save what the user typed? Do you want to see how many guesses it took?
I think you could take a look at the for loop and the break statement :) they will make your work easier
def prompt():
guess = input("guess a letter: ")
return(guess)
def testme(vocab):
import random
correct = random.choice(vocab)
for i in range(4):
guess = prompt()
if correct == guess:
print("Well done!")
break
else:
print("Wrong!\n")
return my_list_of_outputs
testme(['a','b','c','d','e','f'])
So (as you will probably see from my code) I am a beginner at Python (version 3.8.3) and enjoying it very much so far, and I have challenged myself on several different beginner projects. I am currently making a random string generator (i.e. a password generator, hence the use of the secrets module).
# Password Generator
import secrets, string
print("Welcome to the generator. Please specify your requirements")
print("A. All Characters;\nB. No Numbers;\nC. No Punctuation\nPlease choose the appropriate letter for your needs.")
userInput = input()
def userWelcome():
if userInput.lower() == "a":
generatePass = string.ascii_letters + string.digits + string.punctuation
print("How long do you want your string to be?")
stringRange = int(input())
print( "".join(secrets.choice(generatePass) for _ in range(stringRange)) )
elif userInput.lower() == "b":
generatePass = string.ascii_letters + string.punctuation
print("How long do you want your string to be?")
stringRange = int(input())
print("".join(secrets.choice(generatePass) for _ in range(stringRange)))
elif userInput.lower() == "c":
generatePass = string.ascii_letters + string.digits
print("How long do you want your string to be?")
stringRange = int(input())
print("".join(secrets.choice(generatePass) for _ in range(stringRange)))
else:
print("Not an option! Let's try again.")
userWelcome()
userWelcome()
However, my problem is what to do if the user inputs an incorrect option. As you can see, with the else statement I assume what they filled in does not match any of the earlier options - and so I want to try to rerun the generator again (so I try to call userWelcome again in the else statement).
However, when I type in for example 12 as input, my shell starts to output my string (Not an option Let's try again) literally a thousand times like it is stuck in a loop. I am wondering what I am doing wrong exactly.
What I have tried:
(1) So I have tried to solve this input problem first with try and except, running the except when there is a ValueError but that only works for numbers and I did not manage to rerun userWelcome()
(2) I have tried to create a elif statement in which I check the input for integers, however that also gets stuck in a loop. Code:
elif userInput.isalpha() == False:
print("Not an option! Let's try again.")
userWelcome()
Anyway, I hope that explains it well. I have been busy with this for a few hours now and I thought I'd ask this. Maybe it's a very stupid question but for me it's hard :)
TL;DR: Want to check for proper user input by running my function again, get stuck in weird loop
Thank you for your time and effort!
The code calls userWelcome() recursively, without changing the global variable userInput. The same bad string is processed again, causing the same result, which again calls userWelcome() - for ever (at least until max call depth).
You should read a new string at the beginning of userWelcome, instead of using a global variable. Also, recursion here is an overkill that confuses you. Better use a simple while loop:
while True:
userInput = ....
if ....
do something
return
elif ...
do something else
return # exit the function - breaking out of the loop
else:
print(error message)
# No return here, means the loop will continue to loop
If you want to call the function instead of loop inside, you can instead make the function return success (True) vs. failure (False), and loop that in the caller:
while not userWelcome(inputString):
inputString = read the string
def userWelcome(inputString):
if inputString == ....:
something
return True # To mark OK
elif inputString == .....:
something else
return True # To mark OK
else:
print an error
return False # To mark failure
Just avoid global variables, it is a bad practice. Pass the value through parameters, as in the code above.
I was working on this assignment for the EdX MIT Python course and decided I wanted the output to display differently. Based on my code, I thought that the task program would end when guesses = 0. However, I'm either getting "IndexError: list assignment index out of range" or the program ends at guess 2. This appears to depend on the length of the secretWord. Can anyone point me in the right direction?
def hangman(secretWord):
'''
secretWord: string, the secret word to guess.
Starts up an interactive game of Hangman.
* At the start of the game, let the user know how many
letters the secretWord contains.
* Ask the user to supply one guess (i.e. letter) per round.
* The user should receive feedback immediately after each guess
about whether their guess appears in the computers word.
* After each round, you should also display to the user the
partially guessed word so far, as well as letters that the
user has not yet guessed.
Follows the other limitations detailed in the problem write-up.
'''
trackedguess = []
letcount = ()
letterlist = []
guess = ''
for i in range(0, (len(secretWord)-1)):
trackedguess.append('_')
letcount = len(secretWord)
guessesleft = 8
for i in range(0, 7):
if ''.join(trackedguess) == secretWord:
print('You win!')
break
if guessesleft < 1:
print('You have 0 guesses remaining.')
break
print(trackedguess)
print("You have ", guessesleft, " guesses remaining.")
guess = input('Please guess a letter and press return: ')
if guess in letterlist:
print("You've already guessed that. Try again.")
else:
guessesleft -= 1
letterlist.append(guess)
for i in range(0, len(secretWord)):
if secretWord[i] in letterlist:
coordinate = i
trackedguess[coordinate] = secretWord[i]
hangman(chooseWord(wordlist))
A few things stick out to me:
range uses an exclusive end. That means range(0, (len(secretWord)-1) will iterate one time less than the length of secretWord. You want the lengths to match. More straightforward, less error-prone approaches would be just: trackedGuess = ['_'] * len(secretWord) or trackedGuess = list('_' * len(secretWord)).
You should verify your assumptions. For example, the above case could have been caught easily if you did assert(len(trackedGuess) == len(secretWord)).
for i in range(0, 7) suffers from the same problem as your earlier usage of range(). If you want to iterate 8 (guessesleft) times, then you should use range(0, 8). However, you're also decrementing guessesleft inside the loop and exiting when it reaches 0. Do one or the other, but not both. As your code currently is, if someone enters a guess that they've already made, it will count one iteration against them (which I'm not sure is what you want).
It's a very good attempt, but there are some things that are wrong. I'm going to try to walk through them one by one.
Instead of a for loop, I'd suggest using a while guessesleft>0. In the current implementation the for loop will run for 8 times regardless of whether or not there are any guesses left (for example, try providing the same letter as a guess every time). With the while, however, you gain much more control over the loop.
The trackedguess generation is flawed. It will always miss the last letter of the secretword (this is also the reason you were getting the IndexError) Try for i in range(len(secretWord)) instead. You'll also find it much more concise and readable.
I also took the liberty of moving the win or lose condition down in the loop. Previously, if you won on the last guess, you'd still lose (because the win check condition happened before the input and after that the loop ended); also, the guess wasn't printed if you won (because the loop would break before the print statement).
Revised code below:
def hangman(secretWord):
'''
secretWord: string, the secret word to guess.
Starts up an interactive game of Hangman.
* At the start of the game, let the user know how many
letters the secretWord contains.
* Ask the user to supply one guess (i.e. letter) per round.
* The user should receive feedback immediately after each guess
about whether their guess appears in the computers word.
* After each round, you should also display to the user the
partially guessed word so far, as well as letters that the
user has not yet guessed.
Follows the other limitations detailed in the problem write-up.
'''
trackedguess = []
letterlist = []
for i in range(len(secretWord)):
trackedguess.append('_')
guessesleft = 8
while guessesleft > 0:
print(trackedguess)
print("You have ", guessesleft, " guesses remaining.")
guess = input('Please guess a letter and press return: ')
if guess in letterlist:
print("You've already guessed that. Try again.")
else:
guessesleft -= 1
letterlist.append(guess)
for i in range(0, len(secretWord)):
if secretWord[i] in letterlist:
coordinate = i
trackedguess[coordinate] = secretWord[i]
if ''.join(trackedguess) == secretWord:
print(trackedguess)
print('You win!')
break
if guessesleft < 1:
print('You have 0 guesses remaining.')
break
hangman('test')
Hope that helps.
I need to create a final project for a Beginning Python class and I decided to create a riddle program and want to simplify the following code, but am unsure how to do it. I would like to use a class to do this, but I'm having trouble figuring out how to change my main program to use the information from Class The program runs fine without using Class, but I have to use it for the final project. I am unsure how to do this, I've tried a few things like doing a points system (5 points increment for each win) and thought it would be nice to make the riddles/answers etc. into a class. The problem I'm having is how to code the self instances in the program itself. I know to import the class, but I'm struggling with how/what to change in the program without screwing everything up.. I've looked all over Stack Overflow and a variety of other internet resources, and believe I have a decent understanding of classes, importing and inheritance, but I can't seem to find any examples similar to what I'm trying to do.. Any help is greatly appreciated.
import riddle_class
banner = "~Riddle Me This~"
print(banner)
print('')
print('Instructions: You have 10 chances to answer each of the following 5 riddles.\
You will receive 5 points for each riddle you answer – up to 25 points.')
points = {'win': 0}
riddle_one = 'What\'s round, but not always around. It is light sometimes and is dark sometimes. Everyone wants to walk all over me. What am I?'
riddle_two = 'What has roots as nobody sees, Is taller than trees, Up, up it goes, And yet never grows?'
riddle_three = 'Voiceless it cries, Wingless flutters, Toothless bites, Mouthless mutters. What am I?'
riddle_four = 'Tear one off and scratch my head; what was once red is black instead.'
riddle_five = 'We\'re five little items of an everyday sort; you\'ll find us all in a tennis court'
riddle_one_answer = 'moon'
riddle_two_answer = 'mountain'
riddle_three_answer = 'wind'
riddle_four_answer = 'match'
riddle_five_answer = 'vowels'
hidden_one = '-' * len(riddle_one_answer)
hidden_two = '-' * len(riddle_two_answer)
hidden_three = '-' * len(riddle_three_answer)
hidden_four = '-' * len(riddle_four_answer)
hidden_five = '-' * len(riddle_five_answer)
guess_one = 0
guess_two = 0
guess_three = 0
guess_four = 0
guess_five = 0
points = 0
score = {'win':0}
print('')
#Riddle One
print('~Riddle Number One!~')
print(riddle_one)
print('')
while guess_one < 11:
print(hidden_one)
user_input = input('Enter one letter at a time (guess #%d): ' % guess_one)
if len(user_input) != 1:
continue
# Count the number of times the character occurs in the word
num_occurrences = riddle_one_answer.count(user_input)
# Replace the appropriate position(s) in hidden_word with the actual character.
position = -1
for occurrence in range(num_occurrences):
position = riddle_one_answer.find(user_input, position+1) # Find the position of the next occurrence
hidden_one = hidden_one[:position] + user_input + hidden_one[position+1:] # Rebuild the hidden word string
if not '-' in hidden_one:
print('')
print('Nice Job!', end=' ')
results = 'win'
break
guess_one += 1
else:
print('Loser!', end=' ')
print('The word was %s' % riddle_one_answer,'\.')
print('______________________________________')
print('')
#Riddle Two
print('')
print('~Riddle Number Two!~')
print(riddle_two)
print('')
while guess_two < 11:
print(hidden_two)
user_input = input('Enter one letter at a time (guess #%d): ' % guess_two)
if len(user_input) != 1:
continue
# Count the number of times the character occurs in the word
num_occurrences = riddle_two_answer.count(user_input)
# Replace the hidden_word with the character.
position = -1
for occurrence in range(num_occurrences):
position = riddle_two_answer.find(user_input, position+1) # Find the position of the next occurrence
hidden_two = hidden_two[:position] + user_input + hidden_two[position+1:] # Rebuild the hidden word string
if not '-' in hidden_two:
print('')
print('WINNER!', end=' ')
results = 'win'
break
guess_two += 1
else:
print('Loser!', end=' ')
print('The word was: %s' % riddle_two_answer,'\.')
print('______________________________________')
print('')
#Riddle Three
print('')
print('~Riddle Number Three!~')
print(riddle_three)
print('')
while guess_three < 11:
print(hidden_three)
user_input = input('Enter one letter at a time (guess #%d): ' % guess_three)
if len(user_input) != 1:
continue
# Count the number of times the character occurs in the word
num_occurrences = riddle_three_answer.count(user_input)
# Replace the appropriate position(s) in hidden_word with the actual character.
position = -1
for occurrence in range(num_occurrences):
position = riddle_three_answer.find(user_input, position+1) # Find the position of the next occurrence
hidden_three = hidden_three[:position] + user_input + hidden_three[position+1:] # Rebuild the hidden word string
if not '-' in hidden_three:
print('')
print('WINNER!', end=' ')
results = 'win'
break
guess_three += 1
else:
print('Loser!', end=' ')
print('The word was %s' % riddle_three_answer,'/.')
print('______________________________________')
print('')
#Riddle Four
print('')
print('~Riddle Number Four!~')
print(riddle_four)
print('')
while guess_four < 11:
print(hidden_four)
user_input = input('Enter one letter at a time (guess #%d): ' % guess_four)
if len(user_input) != 1:
continue
# Count the number of times the character occurs in the word
num_occurrences = riddle_four_answer.count(user_input)
# Replace the appropriate position(s) in hidden_word with the actual character.
position = -1
for occurrence in range(num_occurrences):
position = riddle_four_answer.find(user_input, position+1) # Find the position of the next occurrence
hidden_four = hidden_four[:position] + user_input + hidden_four[position+1:] # Rebuild the hidden word string
if not '-' in hidden_four:
print('')
print('WINNER!', end=' ')
results = 'win'
break
guess_four += 1
else:
print('Loser!', end=' ')
print('The word was %s' % riddle_four_answer,'/.')
print('______________________________________')
print('')
#Riddle Five
print('')
print('~Riddle Number Five!~')
print(riddle_five)
print('')
while guess_five < 11:
print(hidden_five)
user_input = input('Enter one letter at a time (guess #%d): ' % guess_five)
if len(user_input) != 1:
continue
# Count the number of times the character occurs in the word
num_occurrences = riddle_five_answer.count(user_input)
# Replace the appropriate position(s) in hidden_word with the actual character.
position = -1
for occurrence in range(num_occurrences):
position = riddle_five_answer.find(user_input, position+1) # Find the position of the next occurrence
hidden_five = hidden_five[:position] + user_input + hidden_five[position+1:] # Rebuild the hidden word string
if not '-' in hidden_five:
print('')
print('WINNER!', end=' ')
results = 'win'
break
guess_five += 1
else:
print('Loser!', end=' ')
print('The word was %s' % riddle_five_answer,'/.')
print('______________________________________')
print('')
if results == 'win':
score['win'] += 1
total_wins = (score['win'])
points += 5 * total_wins
print('Total points: %d' % points) *#This is only recognizing 1 'win'*
Class
class riddle_me:
def __init__(self, turns, guess):
self.turns = 5
guess = 0
score = {'wins': 0}
def __init__(self):
guess_one = 0
guess_two = 0
guess_three = 0
guess_four = 0
guess_five = 0
UPDATE
Thank you all for your help and questions. Even the questions got me thinking more about my project. I still haven't completed the program yet, but I know it could work. I just need to practice more. I've been studying nonstop, but for some reason I'm struggling to learn this language no matter how many tutorials I search.
I'm assuming you understand what a class is, right? So your question really is, "how should I use classes in this program?"
Usually the best way to figure out what classes (and objects) you need in a program is to sit down and write out a description of what your program is trying to do, and then pick out words from that. For example:
This riddle game will ask the user a series of questions, and each time compare the user's guess with the correct answer. When the user guesses correctly, the user's points will increase. If the user does not guess correctly after 10 guesses, we will move to the next question. After all the questions have been asked, the game will display the user's points.
OK, so this is still pretty straightforward. I've highlighted some of the words that occur more than once. Each of these words represents an idea that might be usefully made into a class: the game itself, each question, the user's guess.
Some of these things don't need to be their own objects, but can be properties of other objects. For example, notice how we kept referring to "the user's points". Clearly the points number is an attribute of the user object. And the correct answer to a question should be an attribute of that question.
OK, so now we have some "things" we're going to use in our program, and some idea of what we want to know about each of them:
User:
has points (a number)
Question:
has question text (a string)
has correct answer (a string)
Guess:
has the text of the guess, what the user entered (a string)
is either right or wrong (a boolean)
Game:
has 5 questions
OK, these are starting to look like classes! We can translate these into classes pretty directly:
class User:
def __init__(self):
self.points = 0 # user starts out with zero points
class Question:
def __init__(self, question_text, correct_answer):
self.question_text = question_text
self.correct_answer = correct_answer
class Guess:
def __init__(self, text, is_correct):
self.text = text
self.is_correct = is_correct
class Game:
def __init__(self, questions):
self.questions = questions
Now we have some objects, but we can't do anything useful with them. Now we need to decide what methods to add. There's more than one right way to do this (and a lot of subtle tradeoffs, depending on which things you care more or less about).
Let's start, though, by identifying things we want to be able to do:
ask a Question, that is, print out its question_text
read in a Guess from the user
determine whether a Guess matches a Question's correct_answer text
add points to a User
find the next Question in a Game, or determine when there are no more questions
Each of these things we want to do should be a method! Now you're breaking down your program into smaller and smaller pieces. Keep doing it until it becomes obvious what each piece should do -- and then translate each piece into code. That's it!
Some of these things could potentially live in multiple places. Some things to think about:
Should reading in a Guess from the user be a method on Guess or on User? Either one is OK. Or it could even be a separate function (not a method of a class).
Where should the function that determines whether a Guess is correct or not live? Right now I have it as an attribute of the Guess class, but maybe it should be a method on Guess like is_correct_answer_to(question) that takes the question and returns True or False.
Where should we keep track of the number of guesses so far for a particular question? It could either be on the Question object, or on the User object. Or it could just be a local variable in a function like ask_question(question) which could live either on Question or maybe on Game, or maybe not as a member of a class.
These are the decisions you need to make. But hopefully this will help you get started!
I think there are two possible places to put a class into your code. One is urgently needed, the other not so much (but it could come in handy if you want to add new features to the program).
The obvious place to use a class is to encapsulate all the data and logic related to a single riddle. Each instance of the class would have attributes for the question, the answer, and perhaps other things that would be modified as the player tries to solve the riddle (the masked solution shown to the player and a list of the guesses so far, perhaps).
Something like this:
class Riddle(object):
def __init__(self, question, answer):
self.question = question
self.answer = answer
self.guesses = []
self.update_mask()
def update_mask(self):
self.masked_answer = "".join(c if c in self.guesses else "-"
for c in self.answer)
def guess(self, letter):
# perhaps do some sanity checking here, for invalid or duplicated letters
self.guesses.append(letter)
self.update_mask()
# return number of letters just matched, number yet to be guessed
return self.answer.count(letter), self.masked_answer.count("-")
The other place you might use a class is to encapsulate the game logic (e.g. how many guesses the user gets to make, how they score points) and state (e.g. the player's score and the list of Riddles to ask). This would let you adapt your program in various ways, like letting the player play again after they complete the first run.
However, I don't think that using a class for this is nearly as important as it was for the riddles. Most of the benefits would simply come from gathering the game logic into functions, rather than repeating it over and over. Make an ask_riddle function to handle a single riddle and return the score the player earned and your code will get a whole lot cleaner. If you want to make it a method of the Game class, and update the instance with the score instead, that works too. Python doesn't require you to write object oriented code (though it does make it easy to do so), so you don't need to wedge classes in where they're not needed.
I've made the game Mastercode and I am having troubles getting the computer to tell the user which numbers they got correct and incorrect.
My code is listed below, along with the attempt I used for getting the computer to print the correct answers. If someone could tell me what I am doing wrong and point me in the right direction, that would be great.
import random
def masterMind():
Password = "%05d" % random.randint(0, 99999) #the computer chooses 5 random numbers
for tries in range(10):
userGuess = raw_input("Guess my 5 digit password to access the treasure:")
if Password == userGuess:
print "Win on the %d try" % (tries + 1)
hint(password, userGuess)
break #terminates the ongoing loop and executes next statement
print "answer was:", Password #tells computer to print the password
def hint(password, guess): #function of the hints
for i in range(5): #the range within the five integers
if guess[i] == password[i]: #if the user's integer aligns with computers integer then an 'x' should appear
print 'x',
continue
if guess[i] in answer: #if don't have corresponding number then an 'o' will appear
print 'o',
I think you really need to check for black and white pegs in separate parts of hint(). This allows you to REMOVE something that "matched as black", and not [incorrectly] score additional whites for it.
Using lists, this can be implemented as such:
def hint(password, guess):
# convert the strings to lists so we can to assignments, below
password_list = list(password)
guess_list = list(guess)
# check for black (correct number in correct position)
for i in range(5): #the range within the five integers
if guess_list[i] == password_list[i]:
print 'x',
# punch in some non-possible value so we can exclude this on the check for white
guess_list[i] = None
password_list[i] = None
# check for white (correct number in wrong position)
for i in range(5):
if guess_list[i] == None:
continue
if guess_list[i] in password_list:
print 'o',
# remove this from the password list, so that a given
# password digit doesn't incorrectly count as multiple white
password_list.remove(guess_list[i])
# or this would work, too:
#password_list[password_list.index(guess_list[i])] = None
print
One you have more Python experience you might find more concise ways to do this with set() objects or Counter() objects... but see if you can see WHY the above works.
Here's my test case: I set the password to "12345", then did these tests:
Guess my 5 digit password to access the treasure:11111
x
Guess my 5 digit password to access the treasure:21777
o o
Guess my 5 digit password to access the treasure:77721
o o
Guess my 5 digit password to access the treasure:21774
o o o
Guess my 5 digit password to access the treasure:21775
x o o
Guess my 5 digit password to access the treasure:14355
x x x o
Guess my 5 digit password to access the treasure:12345
Win on the 7 try
Is that the result you are looking for?
First, you should move the hint call out of the if block (probably it's not a good idea to hint a user only when he/she got the password right):
if Password == userGuess:
print "Win on the %d try" % (tries + 1)
break #terminates the ongoing loop and executes next statement
hint(Password, userGuess)
By the way you were calling hint as hint(password, userGuess). Python names are case-sensitive and you should call it like this: hint(Password, userGuess). Well, really you should rename the Password variable to password - common Python convention is to use lowercase in the variable names.
Second, you have undefined variables in the hint function. I think this function should look like this:
def hint(password, guess): #function of the hints
for i in range(5): #the range within the five integers
if guess[i] == password[i]: #if the user's integer aligns with computers integer then an 'x' should appear
print 'x',
else:
print 'o',
With these changes your code works: I got 18744 password on the 10th try.
About the masterMind() part : The hint() call is executed at the wrong place. Also it uses password and not Password as a parameter
def masterMind():
Password = "%05d" % random.randint(0, 99999) #the computer chooses 5 random numbers
for tries in range(10):
userGuess = raw_input("Guess my 5 digit password to access the treasure:")
if Password == userGuess:
print "Win on the %d try" % (tries + 1)
break #terminates the ongoing loop and executes next statement
else :
hint(Password, userGuess)
print "answer was:", Password #tells computer to print the password
About the hint part :
answer isn't defined anywhere, I think you meant password.
I'm not sure you can work with integers like that, but it'll be fine if you convert them to strings.
Finally the algorithm structure doesn't work well. If a number of the guess is at the right place, the guess function will both echo x and o.
You should use a if ... elif structure, and then you could add a else clause to notify the user that this number isn't in the password at all !
Try to rewrite your hint() function with these indications, but if you need more help, here's a working solution.
def hint(password, guess): #function of the hints
for i in range(5): #the range within the five integers
if str(guess)[i] == str(password)[i]: #if the user's integer aligns with computers integer then an 'x' should appear
print 'x',
elif str(guess)[i] in str(password): #if don't have corresponding number then an 'o' will appear
print 'o',
else:
print '_', # _ is displayed if the number isn't in the password at all.
Also, it would be nice to check that the user's guess has exactly five digits. If it has less than five digits, there'll be an error.