1

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.

hephaes
  • 167
  • 1
  • 6

2 Answers2

3

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()
illright
  • 3,991
  • 2
  • 29
  • 54
0

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
Patrick Artner
  • 50,409
  • 9
  • 43
  • 69