Python Code Improvement

Here is the question

(Game: lottery) Revise Listing 4.10, Lottery.py, to generate a three-digit lottery number. The program prompts the user to enter a three-digit number and deter- mines whether the user wins according to the following rules:

  1. If the user input matches the lottery number in the exact order, the award is $10,000.
  2. If all the digits in the user input match all the digits in the lottery number, the award is $3,000.
  3. If one digit in the user input matches a digit in the lottery number, the award is $1,000.

Here is my solution:

I am quite happy with solution to 1. and 2. But can anyone give a simple solution to 3 using only mathematical and string functions and selection statements.

Thanks.

There is one very fundamental problem with your code, the use of eval(), this means the user input is evaluated as code(!!!), which is quit a security risk, you really shouldn’t.

assuming you use python3 (useful information to put into a question), don’t use eval, input() will give you the code as string, if you want integer use int() instead

2 Likes

and you could use lists and loops to improve the code, if you have gotten to that already

2 Likes

Besides the eval() part, everything is correct, right? Thank you stetim94!

correct, yes. Not very efficient, you can improve this program once you learned list and loops, for example you could use all() to verify point 2:

https://docs.python.org/3/library/functions.html#all

and any for point3 (which below all in the python doc)

2 Likes

Just pitching in an idea…

import random

def get_pick():
  validRange = range(100, 1000)
  isValid = False
  while not isValid:
    # force correct length of input
    user = ''
    while len(user) != 3:
        user = input("Enter a three digit number")
    # validate
    if not user.isdigit(): print ("Not a Number"); continue
    if int(user) not in validRange: print ("Not in Range"); continue
    isValid = True
  return user

def get_draw():
  return str(random.randrange(100, 1000))

guess = get_pick()
draw = get_draw()

both guess and draw are strings which are iterable. we don’t need a bunch of variables moving forward. The strings as they are should suffice.

2 Likes

The only thing I haven’t figured out is the 1000 dollar prize. Is it,

matching last digit or \
last two digits match last digit or \
all three digits match last digit

?

Pardon my confusion.

Here is a test sequence based upon what I’ve discrened from the OP code…

import itertools

def result(g, d):
  
    # exact match
    if g == d: return "You have won 10,000 dollars!"
    
    # same digits, different order
    if tuple(d) in itertools.permutations(tuple(g), 3): return "You have won 3,000 dollars!"
    
    # match one or two positions
    match_pos = [ int(g[0] == d[0] and g[0]), int(g[1] == d[1] and g[1]), int(g[2] == d[2] and g[2]) ]
    if sum(match_pos) is 0: return "Not a winner."
    
    # match one or two digits
    match_digs = [ int(g[0] in d and g[0]), int(g[1] in d and g[1]), int(g[2] in d and g[2]) ]
    return "Something matches", match_pos, match_digs, d

print (result(guess, draw))

guess, draw = '456', '416'
print (result(guess, draw))

guess, draw = '456', '461'
print (result(guess, draw))
Python 3.6.1 (default, Dec 2015, 13:05:11)
[GCC 4.8.2] on linux
 >
 Enter a three digit number 789
 ('Something matches', [0, 8, 0], [7, 8, 0], '187')
 ('Something matches', [4, 0, 6], [4, 0, 6], '416')
 ('Something matches', [4, 0, 0], [4, 0, 6], '461')
 >

https://repl.it/L7qE


refactoring
import random, itertools

def get_pick():
  validRange = range(100, 1000)
  while True:
    user = ''
    while len(user) != 3:
        user = input("Enter a three digit number")
    if not user.isdigit(): print ("Not a Number"); continue
    if int(user) not in validRange: print ("Not in Range"); continue
    break
  return user

def get_draw():
  return str(random.randrange(100, 1000))

def result(g, d):
    if g == d: return "You have won 10,000 dollars!"
    if tuple(d) in itertools.permutations(tuple(g), 3): return "You have won 3,000 dollars!"
    match_pos = ( int(g[0] == d[0] and g[0]), int(g[1] == d[1] and g[1]), int(g[2] == d[2] and g[2]) )
    match_digs = ( int(g[0] in d and g[0]), int(g[1] in d and g[1]), int(g[2] in d and g[2]) )
    if sum(match_digs) is 0: return "Not a winner."
    return "Something matches", match_pos, match_digs, d

guess = get_pick()
draw = get_draw()

print (result(guess, draw))

# arbitrary tests
print (result('456', '416'))
print (result('456', '461'))
print (result('379', '379'))
print (result('379', '937'))
print (result('284', '735'))

https://repl.it/L7qE/1

The short-circuit of all this is is,

print (result(get_pick(), get_draw())
1 Like

You have over complicated the find out if numbers matching, you tried to do it using maths. You can do it easier with logic.

import random

def main():
    #Gets user's guess.
    userGuess = str(input("Enter a three digit number: "))

    #Generate a random three digit number.
    ranNumber = str(random.randrange(100,1000))

    print("The lotery number is " + ranNumber)

    #If the user's guess and generated number are the same they win 10k!
    if userGuess == ranNumber:
        print("You have won $10,000!")
    else:
        # Make a list of the matching numbers.
        matchNumbers = [x for x in userGuess if x in ranNumber]

        # All numbers match, just not in the right order.
        if len(matchNumbers) == 3:
            print("You have won $3,000!")

        # Will return True if at least one number matched.
        elif matchNumbers:
            print("You have won $1,000!")

        # Nothing matched, they get nothing.
        else:
            print("You Lose!")

if __name__ == "__main__":
    main()

I know this technically doesn’t use only string and maths as it uses a list to find out if $1000 is won. It can be done just as easily, It isn’t as nice to look at though:

import random

def main():
    #Gets user's guess.
    userGuess = str(input("Enter a three digit number: "))

    #Generate a random three digit number.
    ranNumber = str(random.randrange(100,1000))

    print("The lotery number is " + ranNumber)

    #If the user's guess and generated number are the same they win 10k!
    if userGuess == ranNumber:
        print("You have won $10,000!")
    else:
        # Checks that all numbers in the users guess are in the random number,
        if all((userGuess[0] in ranNumber, userGuess[1] in ranNumber, userGuess[2] in ranNumber)):
        print("You have won $3,000!")

        # At least one number matched.
        elif any((userGuess[0] in ranNumber, userGuess[1] in ranNumber, userGuess[2] in ranNumber)):
            print("You have won $1,000!")

        # Nothing matched, they get nothing.
        else:
            print("You Lose!")

if __name__ == "__main__":
    main()
1 Like

you could use this:

numlst = [int(numberD1),int(numberD2),int(numberD3)]
if int(guessD1) in numLst or int(guessD2) in numLst or int(guessD3) in numLst:
                   print('You have won $1,000')

OR? Don’t all three numbers have to match?

no @mtf,
I am doing problem #3

Yeah, I didn’t scan the thread. You are suggesting a solution in a morabund topic. Members do not favor bumping of old topics, so this is going to be treated as a post to post.