I am designing a mastermind game to be played with python. But I encounter some problems when I try to set a function to repeat itself when the attempts are not completely correct.
My code is in two parts. For the first part it asks the user for the correct number, and then the second user tries to input his attempt number. The second part of the code breaks down his attempt into lists of numbers, and compute the number of correct integers and number of integers in correct position, then if the answer is not completely correct, the programme asks the user for a second input.
def getnumber():
predestine = input("Please input your test number")
a = str(predestine)
attempt()
def attempt():
attempt = input("Attempt:")
b = str(attempt)
correctrecord = []
sequencerecord = []
for i in b:
if i in a:
correctrecord.append(1)
for i in range(0,4):
if b[i] == a[i]:
s equencerecord.append(1)
correctlength = len(correctrecord)
sequencelength = len(sequencerecord)
print(f"You have made {correctlength} correct attempts, and of these {sequencelength} are of correct positions")
if sequencelength == 4:
print("You have won, the game is ended")
else:
return attempt()
The problem is with the last code: return attempt(). It seems it fails to repeat the function with 'str object not callable' error.
The problem in your code lies in variable shadowing.
Your repeated function is in a variable named attempt, a global variable. Then, inside the attempt function you define an attempt string variable, local to this function, and therefore temporarily shadowing the global attempt variable that held the function.
Therefore, the call attempt() fails, as you're essentially trying to call a string.
The solution would be to rename the local string variable attempt to not shadow the global one:
def attempt():
attempt_ = input("Attempt:")
b = str(attempt_)
correctrecord = []
sequencerecord = []
for i in b:
if i in a:
correctrecord.append(1)
for i in range(0,4):
if b[i] == a[i]:
sequencerecord.append(1)
correctlength = len(correctrecord)
sequencelength = len(sequencerecord)
print(f"You have made {correctlength} correct attempts, and of these {sequencelength} are of correct positions")
if sequencelength == 4:
print("You have won, the game is ended")
else:
return attempt()
Your use same variable-names multiple times. Python functions are first class citizens, which allows you to do:
# define a function by name r
def r():
return 1
print(type(r)) # <class 'function'> now r is a function
# reassign name r to be a string
r = "22"
print(type(r)) # <class 'str'> now r is a string
If you do r() now you get TypeError: 'str' object is not callable
Your code uses global variables and you call your own function again and avain - this can lead to recursion overflow - see What is the maximum recursion depth in Python, and how to increase it?
You will get wrong results when calculating the amount of correct "digits" when there are duplicates - try "1122" as correct value and "1234" as attempt.
Recursion is not needed to code your game. I restructured it a bit to showcase a different way:
def getnumber(text):
"""Loops until a number is inputted. Returns the number as string.
'text' is the prompt for the user when asking for a number."""
while True:
try:
predestine = int(input(text).strip())
return str(predestine)
except:
print("Only numbers allowed!")
def attempt(correct_number):
"""One game round, returns True if correct result was found."""
attempt = getnumber("Attempt: ")
# avoid double-counting for f.e. 1212 and 1111 inputs
correct_digits = len(set(attempt) & set(correct_number))
sequencelength = 0 # no need to collect into list and use len
for i,c in enumerate(attempt): # simply increment directly
if c == correct_number[i]:
sequencelength += 1
print(f"You have found {correct_digits} correct digits, and of these {sequencelength} are of correct positions.")
if len(attempt) < len(correct_number):
print("Your number has too few digits.")
elif len(attempt) > len(correct_number):
print("Your number has too many digits.")
return correct_number == attempt
# game - first get the correct number
number = getnumber("Please input your test number: ")
# loop until the attempt() returns True
ok = False
while not ok:
ok = attempt(number)
print("You have won, the game is ended")
Output:
Please input your test number: 1234
Attempt: 1111
You have found 1 correct digits, and of these 1 are of correct positions.
Attempt: 1212
You have found 2 correct digits, and of these 2 are of correct positions.
Attempt: 1321
You have found 3 correct digits, and of these 1 are of correct positions.
Attempt: 1234
You have found 4 correct digits, and of these 4 are of correct positions.
You have won, the game is ended
Related
This question already has answers here:
How do I get a result (output) from a function? How can I use the result later?
(4 answers)
Closed last month.
The issue I'm having is that in one of my functions ( function range_and_number() ), I'm asking the user for input, and I'm saving this input in a variable called max number. Next I checked to make sure that this value only has numbers in it, and if it does I break the loop.
def range_and_number():
while True:
max_number = input("Max Number Possible (No Decimals): ")
if max_number.isnumeric() == True:
print("Your Good")
max_number = int(max_number)
break
else:
print("Please Re-Type Your Maximum Number: ")
return max_number
def get_length():
lives = len(range_and_number)
return lives
def main():
s = get_length()
print(f"================= {s} =================")
Issue: I want to access the value of max number in another function ( function get_length() ) because in this function, I want to measure the length of characters in the variable 'max number'.
The error I'm getting is:
lives = len(range_and_number)
TypeError: object of type 'function' has no len()
I understand why I'm getting the error, but how do I get around it. How do I access the value stored in max_number without calling the function itself.
P.S. If you have the time, please post as many possible solutions please. Thanks in advance
(What I tried)
I tried using a Global Variable and created it at the top of my function
I named the Global Variable TE
Then, I did TE = max_number
After, I tried accessing TE in the second function, but it didn't work
You need to put () after a function name to call it.
You can't get the length of an integer, so you need to convert it to a string in order to call len().
def get_length():
lives = len(str(range_and_number()))
return lives
Issue: I want to access the value of max number in another function ( function get_length() ) because in this function, I want to measure the length of characters in the variable 'max number'.
I tried using a Global Variable and created it at the top of my function
Don't use a global variable. Use a variable in function main.
def range_and_number():
while True:
max_number_as_string = input("Max Number Possible (No Decimals): ")
try:
max_number = int(max_number_as_string)
print("You're good.")
return max_number
except ValueError:
print("Please Re-Type Your Maximum Number.")
def get_length(n):
return len(str(n))
def main():
max_number = range_and_number()
lives = get_length(max_number)
print('Lives: {}'.format(lives))
print('Max number: {}'.format(max_number))
if __name__ == '__main__':
main()
Output:
Max Number Possible (No Decimals): hello
Please Re-Type Your Maximum Number.
Max Number Possible (No Decimals): 127.5
Please Re-Type Your Maximum Number.
Max Number Possible (No Decimals): 127
You're good.
Lives: 3
Max number: 127
You have to call the function and convert it to string:
s = range_and_number().__str__().__len__()
or print the len(max_number) before converting it to int
or you could use logarithm to calculate the length after converting it to intdough it would be unnecessary and inefficient when you already have the length of string.
Fancy way of doing it:
Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
range_and_number = lambda f: lambda s: (s.__len__() if (s:=input(s)) and s.isnumeric() else f("Please Re-Type Your Maximum Number: \n"))
print("================= %d ================="%Y(range_and_number)('Max Number Possible (No Decimals): \n'))
The function get_length should call the function range_and_number. Your code should be something like this.
def range_and_number():
while True:
max_number = input("Max Number Possible (No Decimals): ")
if max_number.isnumeric() == True:
print("Your Good")
max_number = int(max_number)
break
else:
print("Please Re-Type Your Maximum Number: ")
return max_number
def get_length():
lives = len(str(range_and_number()))
return lives
def main():
s = get_length()
print(f"================= {s} =================")
Hello I have a problem in my code. I want to check valu of ran_dice in method main but I dont know how I can do. For example I wrote ran_dice(2) it return 2 random integers and I want to check these two integers equals or not. Can I do in main method ? How ?
Printing ran_dice(2) should do the trick.
Edit according to the comment:
a,b=ran_dice(2)
if a==b:
# code to stop
As another comment mentioned, however, the ran_dice(s) function is a little dangerous as the amount of things it returns varies. It's good practice to have a program return a consistent amount of things. You could return the values in a list, and the size of the list could vary, but at least you're always returning one list.
Here is an example, you could return the dice values in a list. So you can adjust the returned number of dice as you like, and compare the result.
import random
def ran_dice(s):
if s==1:
a=random.randint(1,6)
return [a]
elif s==2:
a=random.randint(1,6)
b=random.randint(1,6)
return [a,b]
def main():
credit=100
print('Welcome user, you have ', credit,'credits.')
number=int(input('How much do you want to gamble?: '))
while number <0 or number>100:
print('You need to give a positive integer no more than your credit.')
number=int(input('How much do you want to gamble?: '))
result = ran_dice(2)
print ("dice=", result)
firstdice = 0
for dice in result:
if firstdice == 0:
firstdice = dice
elif dice == firstdice:
print("equal")
else:
print("different")
if result[0] == result[1]:
print("equal")
main()
I got some homework to do and I got stuck with this code. I have no idea how to continue.
This is what I'm suppose to do:
generate_sequence - Will generate a list of random numbers between 1 to 101. The list
length will be difficulty.
get_list_from_user - Will return a list of numbers prompted from the user. The list length
will be in the size of difficulty.
is_list_equal - A function to compare two lists if they are equal. The function will return
True / False.
play - Will call the functions above and play the game. Will return True / False if the user
lost or won.
(Sorry for copy/pasting. My English is not so good.)
import random
difficulty = 101
secret_number = 6
def generate_number():
global secret_number
secret_number = random.randint(0, difficulty)
def get_guess_from_user():
return input( "Please choose number between 1 to " + str(difficulty))
def compare_results(userInput):
isSame = False
if(secret_number == userInput):
isSame = True
return isSame
def play():
generate_number()
userInput = get_guess_from_user()
isSame = compare_results(userInput)
print("number generated is: " + str(secret_number))
print(isSame)
play()
Your "problem" is, that if(secret_number == userInput): is currently comparing an int to a str, because the result of input() is always a str, even if the input is numeric. An int and a str are never equal, thus isSame will always be False.
You have to cast the user input to int somewhere along the way before or during the comparison.
e.g.:
def get_guess_from_user():
return int(input( "Please choose number between 1 to " + str(difficulty)))
# ^^^
Otherwise, your program seems to do what you are describing.
You could save some lines of code by writing:
def compare_results(userInput):
return (secret_number == userInput)
I took the liberty to rewrite your application w/o global variables:
import random
def generate_number(difficulty):
return random.randint(1, difficulty)
def get_guess_from_user(difficulty):
return int(input( "Please choose number between 1 to {}".format(difficulty)))
def play(difficulty):
secret_number = generate_number(difficulty)
user_input = get_guess_from_user(difficulty)
is_same = (secret_number == user_input)
print("number generated is: {}".format(secret_number))
print("Your guess was {}".format( "correct :)" if is_same else "not correct :(" ))
play(5)
Note: I also changed random.randint(0, difficulty) to random.randint(1, difficulty), because the lower part is also inclusive, meaning that it could return 0. When prompting the user for a number between 1 and 5, the user might be surprised that the correct number was 0 instead.
See the docs:
random.randint(a, b)
Return a random integer N such that a <= N <= b. Alias for randrange(a, b+1).
I am a new learner for Python. I have a question about while loop.
I wrote a program below to look for square roots.
When I input anything but integers, the message "is not an integer" shows up and it repeats itself until I input correct data(integers).
My question is, why does it end loop when it return value on line 5, return(int(val))?
Thank you for your attention.
def readInt():
while True:
val = input('Enter an integer: ')
try:
return(int(val))
except ValueError:
print(val, "is not an integer")
g = readInt()
print("The square of the number you entered is", g**2)
To answer your original question, 'return' effectively exits the loop and provide the result that follows the 'return' statement, but you have to explicity print it like so:
def read_int(num1, num2):
while True:
return num1 + num2
print(read_int(12, 15))
If you simply put 'read_int(12, 14)' instead of 'print(read_int(12, 15))' in this scenario, you won't print anything but you will exit the loop.
If you allow me, here are some modifications to your original code:
def read_int(): # functions must be lowercase (Python convention)
while True:
val = input('Enter an integer: ')
try:
val = int(val) # converts the value entered to an integer
minimum_value = 0 # There is no need to evaluate a negative number as it will be positive anyway
maximum_value = 1000000 # Also, a number above 1 million would be pretty big
if minimum_value <= val <= maximum_value:
result = val ** 2
print(f'The square of the number you entered is {result}.')
# This print statement is equivalent to the following:
# print('The square of the number you entered is {}.'.format(result))
break # exits the loop: else you input an integer forever.
else:
print(f'Value must be between {minimum_value} and {maximum_value}.')
except ValueError: # If input is not an integer, print this message and go back to the beginning of the loop.
print(val, 'is not an integer.')
# There must be 2 blank lines before and after a function block
read_int()
With the final 'print' that you actually have at the end of your code, entering a string of text in the program generates an error. Now it doesn't ;). Hope this is useful in some way. Have a great day!
The following code is my attempt at simulating a lottery.
import random
def lottery(numbers):
lottoNumbers = [randint('0,100') for count in range(3)]
if numbers == lottoNumbers:
print('YOU WIN $10,000')
else:
print('YOU LOSE,DUN DUN DUNNN!')
return numbers
def main():
numbers = int(input('Enter a number: '))
if numbers == lottoNumbers:
numbers = lottery(numbers)
else:
numbers = lottery(numbers)
main()
Hey guys I've gotten this far with the help you've given me. I'm trying to write the code so that 3 lotto numbers at random will be chosen. Then the user must enter 3 of his/her own lotto numbers. If they get all 3 correct then they win the whole prize, if they get the 3 numbers but not in the correct order they win some of the prize. Obviously if they guess all wrong then a print statement would state that. What I'm confused about is how can I write the code so that the user can enter 3 numbers to try matching the random lottery numbers. I also want to print the 3 lottery numbers after the user inputs his/her choices. Any ideas guys?
Thanks for your help everyone.
You seem a bit confused about what the role of the arguments in a function are. You've said that your randm function takes the argument "number", but then you haven't actually used it anywhere. The next time number appears, you've assigned it a completely new value, so any value passed to randm isn't actually being used.
Also, the function is trying to return x, when x hasn't been assigned within the function. Either you already have a global variable called x already defined, in which case the function will just return that variable, or the function will just fail because it can't find the variable x.
Here's a quick example I've done where you pass their three numbers as a list as an argument to the function.
import random
theirNumbers=[5,24,67]
def checkNumbers(theirNumbers):
lottoNumbers = []
for count in range(3)
lottoNumbers.append(random.randint(0,100))
winning = True
for number in theirNumbers:
if not each in lottoNumbers: winning=False
if winning == True: print("Winner!")
There are a few things wrong with your implementation, to name a few:
if you are trying to compare the output of the function randm to x, you will need to include a return value in the function, like so:
def randm():
return return_value
You appear to be printing all the values but not storing them, in the end you will only end up with the final one, you should attempt to store them in a list like so:
list_name = [randint(0,100) for x in range(x)]
This will generate randint(0,100) x times in a list, which will allow you to access all the values later.
To fix up your code as close to what you were attempting as possible I would do:
import random
def randm(user_numbers):
number = []
for count in range(3):
number.append(random.randint(0, 100))
print(number)
return user_numbers == number
if randm(x):
print('WINNER')
If you are looking for a very pythonic way of doing this task,
you might want to try something like this:
from random import randint
def doLotto(numbers):
# make the lotto number list
lottoNumbers = [randint(0,100) for x in range(len(numbers))]
# check to see if the numbers were equal to the lotto numbers
if numbers == lottoNumbers:
print("You are WinRar!")
else:
print("You Lose!")
I'm assuming from your code (the print() specifically) that you are using python 3.x+
Try to post your whole code. Also mind the indentation when posting, there it looks like the definition of your function would be empty.
I'd do it like this:
import random
def lottery():
win = True
for i in range(3):
guess = random.randint(1,100)
if int(raw_input("Please enter a number...")) != guess:
win = False
break
return win
Let so do this in few steps.
First thing you should learn in writing code is to let separate pieces of code( functions or objects) do different jobs.
First lets create function to make lottery:
def makeLottery(slotCount, maxNumber):
return tuple(random.randint(1,maxNumber) for slot in range(slotCount))
Next lets create function to ask user's guess:
def askGuess(slotCount, maxNumber):
print("take a guess, write {count} numbers separated by space from 1 to {max}".format(count = self.slotCount, max = self.maxNumber))
while True: #we will ask user until he enter sumething suitable
userInput = raw_input()
try:
numbers = parseGuess(userInput,slotCount,maxNumber)
except ValueError as err:
print("please ensure your are entering integer decimal numbers separated by space")
except GuessError as err:
if err.wrongCount: print("please enter exactly {count} numbers".format(count = slotCount))
if err.notInRange: print("all number must be in range from 1 to {max}".format(max = maxNumber))
return numbers
here we are using another function and custom exception class, lets create them:
def parseGuess(userInput, slotCount,maxNumber):
numbers = tuple(map(int,userInput.split()))
if len(numbers) != slotCount : raise GuessError(wrongCount = True)
for number in numbers:
if not 1 <= number <= maxNumber : raise GuessError(notInRange = True)
return numbers
class GuessError(Exception):
def __init__(self,wrongCount = False, notInRange = False):
super(GuessError,self).__init__()
self.wrongCount = wrongCount
self.notInRange = notInRange
and finally function to check solution and conratulate user if he will win:
def checkGuess(lottery,userGuess):
if lottery == userGuess : print "BINGO!!!!"
else : print "Sorry, you lost"
As you can see many functions here uses common data to work. So it should suggest you to collect whole code in single class, let's do it:
class Lottery(object):
def __init__(self, slotCount, maxNumber):
self.slotCount = slotCount
self.maxNumber = maxNumber
self.lottery = tuple(random.randint(1,maxNumber) for slot in range(slotCount))
def askGuess(self):
print("take a guess, write {count} numbers separated by space from 1 to {max}".format(count = self.slotCount, max = self.maxNumber))
while True: #we will ask user until he enter sumething suitable
userInput = raw_input()
try:
numbers = self.parseGuess(userInput)
except ValueError as err:
print("please ensure your are entering integer decimal numbers separated by space")
continue
except GuessError as err:
if err.wrongCount: print("please enter exactly {count} numbers".format(count = self.slotCount))
if err.notInRange: print("all number must be in range from 1 to {max}".format(max = self.maxNumber))
continue
return numbers
def parseGuess(self,userInput):
numbers = tuple(map(int,userInput.split()))
if len(numbers) != self.slotCount : raise GuessError(wrongCount = True)
for number in numbers:
if not 1 <= number <= self.maxNumber : raise GuessError(notInRange = True)
return numbers
def askAndCheck(self):
userGuess = self.askGuess()
if self.lottery == userGuess : print "BINGO!!!!"
else : print "Sorry, you lost"
finally lets check how it works:
>>> lottery = Lottery(3,100)
>>> lottery.askAndCheck()
take a guess, write 3 numbers separated by space from 1 to 100
3
please enter exactly 3 numbers
1 10 1000
all number must be in range from 1 to 100
1 .123 asd
please ensure your are entering integer decimal numbers separated by space
1 2 3
Sorry, you lost
>>> lottery = Lottery(5,1)
>>> lottery.askAndCheck()
take a guess, write 5 numbers separated by space from 1 to 1
1 1 1 1 1
BINGO!!!!