Python Code Improvement


#1

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.


#2

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


#3

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


#4

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


#5

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)


#6

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.


#7

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())

#8

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()

#9

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')

#10

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


#11

no @mtf,
I am doing problem #3


#12

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.