# Need help improving number guessing game

F

#### feba

#!/usr/bin/python/
#Py3k, UTF-8

import random

print(" --- WELCOME TO THE SUPER NUMBER GUESSING GAME --- " + ("\n" *
5))
pnum = int(input("1 OR 2 PLAYER?\nP#: "))

target = random.randint(1, 99) #Pick a random number under two digits

guess1 = 0 #Zero will never be picked as target...
guess2 = 0 #so it makes a good default value
p1score = 0 #For two player mode...
p2score = 0 #let's keep score!

print("LET'S START THE GAME. \nGUESS ANY WHOLE NUMBER FROM 1 TO 99.")

while True:
if pnum == 1: #1p mode
while True:
guess1 = int(input("\n>> "))
if guess1 > 100:
print("ONLY NUMBERS FROM 1 TO 99")
elif guess1 > target:
print("TOO HIGH")
elif guess1 == target:
print("CONGLATGURATIONS! PLAY AGAIN?")
target = random.randint(1, 99) #Set up the game
again
play = int(input("0 TO END: "))
if play == 0:
print("GOOD BYE. PLAY AGAIN SOON!")
quit()
else:
print("TOO LOW")

if pnum == 2: #2p mode
while True:
guess1 = int(input("\nP1> ")) #Player 1's turn
if guess1 > 100:
print("ONLY NUMBERS FROM 1 to 99")
elif guess1 > target:
print("TOO HIGH")
elif guess1 == target:
p1score += 1
print("GOOD JOB, PLAYER 1! THE SCORE IS:\nP1: %s
--- P2: %s\nPLAY AGAIN?" % (p1score, p2score))
target = random.randint(1, 99) #Set up game
again
play = int(input("0 TO END: "))
if play == 0:
print("GOOD BYE. PLAY AGAIN SOON!")
else:
print("TOO LOW")

guess2 = int(input("\nP2> ")) #Player 2's turn
if guess2 > 100:
print("ONLY NUMBERS FROM 1 to 99")
elif guess2 > target:
print("TOO HIGH")
elif guess2 == target:
p2score += 1
print("GOOD JOB, PLAYER 2! THE SCORE IS:\nP1: %s
--- P2: %s\nPLAY AGAIN?" % (p1score, p2score))
target = random.randint(1, 99) #Set up game again
play = int(input("0 TO END: "))
if play == 0:
print("GOOD BYE. PLAY AGAIN SOON!")
else:
print("TOO LOW")
else:
print("INVALID PLAYER SELECTION")
pnum = int(input("1 OR 2 PLAYER?\nPN#: "))

I have one major problem with this; the 'replay' selection. It quits
if you put in 0, as it should, and continues if you put in any other
number. However, if you just press enter, it exits with an error. it
also looks really ugly, and I'm sure there has to be plenty of better
ways to do it.

I'd also appreciate tips on how it could be better in general. I
should think that P1 and P2's turns shouldn't have to be completely
repeated; but I'm not quite sure how to def something like that.

J

#### James Stroud

feba said:
#!/usr/bin/python/
#Py3k, UTF-8

import random

print(" --- WELCOME TO THE SUPER NUMBER GUESSING GAME --- " + ("\n" *
5))
pnum = int(input("1 OR 2 PLAYER?\nP#: "))

target = random.randint(1, 99) #Pick a random number under two digits

guess1 = 0 #Zero will never be picked as target...
guess2 = 0 #so it makes a good default value
p1score = 0 #For two player mode...
p2score = 0 #let's keep score!

print("LET'S START THE GAME. \nGUESS ANY WHOLE NUMBER FROM 1 TO 99.")

while True:
if pnum == 1: #1p mode
while True:
guess1 = int(input("\n>> "))
if guess1 > 100:
print("ONLY NUMBERS FROM 1 TO 99")
elif guess1 > target:
print("TOO HIGH")
elif guess1 == target:
print("CONGLATGURATIONS! PLAY AGAIN?")
target = random.randint(1, 99) #Set up the game
again
play = int(input("0 TO END: "))
if play == 0:
print("GOOD BYE. PLAY AGAIN SOON!")
quit()
else:
print("TOO LOW")

if pnum == 2: #2p mode
while True:
guess1 = int(input("\nP1> ")) #Player 1's turn
if guess1 > 100:
print("ONLY NUMBERS FROM 1 to 99")
elif guess1 > target:
print("TOO HIGH")
elif guess1 == target:
p1score += 1
print("GOOD JOB, PLAYER 1! THE SCORE IS:\nP1: %s
--- P2: %s\nPLAY AGAIN?" % (p1score, p2score))
target = random.randint(1, 99) #Set up game
again
play = int(input("0 TO END: "))
if play == 0:
print("GOOD BYE. PLAY AGAIN SOON!")
else:
print("TOO LOW")

guess2 = int(input("\nP2> ")) #Player 2's turn
if guess2 > 100:
print("ONLY NUMBERS FROM 1 to 99")
elif guess2 > target:
print("TOO HIGH")
elif guess2 == target:
p2score += 1
print("GOOD JOB, PLAYER 2! THE SCORE IS:\nP1: %s
--- P2: %s\nPLAY AGAIN?" % (p1score, p2score))
target = random.randint(1, 99) #Set up game again
play = int(input("0 TO END: "))
if play == 0:
print("GOOD BYE. PLAY AGAIN SOON!")
else:
print("TOO LOW")
else:
print("INVALID PLAYER SELECTION")
pnum = int(input("1 OR 2 PLAYER?\nPN#: "))

I have one major problem with this; the 'replay' selection. It quits
if you put in 0, as it should, and continues if you put in any other
number. However, if you just press enter, it exits with an error. it
also looks really ugly, and I'm sure there has to be plenty of better
ways to do it.

I'd also appreciate tips on how it could be better in general. I
should think that P1 and P2's turns shouldn't have to be completely
repeated; but I'm not quite sure how to def something like that.

1. Refactor. You should look at your code and see where you repeat the
same or similar patterns, see where they differ, make functions, and
make the differences parameters to the function call:

def guess(player, p1score, p2score):
guess1 = int(input("\n>> "))
if guess1 > 100:
print("ONLY NUMBERS FROM 1 TO 99")
elif guess1 > target:
print("TOO HIGH")
elif guess1 == target:
print("GOOD JOB, PLAYER %s! THE SCORE IS:" % player)
print("P1: %s --- P2: %s" % (p1score, p2score)))
print("PLAY AGAIN?")
#Set up the game again
play = int(input("0 TO END: "))
if play == 0:
print("GOOD BYE. PLAY AGAIN SOON!")
quit()
else:
target = random.randint(1, 99)
else:
print("TOO LOW")

You would call guess() like this, perhaps:

guess(2, 15, 22)

2. You need to use a try: except: within a loop when you cast the input
to int:

while True:
try:
guess1 = int(input("\n>> "))
except ValueError:
else:
break

Same with the seeing if the player will play again. See below.

3. Don't try to fit too much on one line. See how I broke the print
statement at logical places (new lines).

4. Further subdivide functions based on conceptual tasks. For example, I
would define a function like this:

def play_again():
while True:
try:
print("PLAY AGAIN?")
again = bool(input("0 TO END: "))
except ValueError:
else:
break
return again

You would use play_again() like this:

if play_again():
print("GOOD BYE. PLAY AGAIN SOON!")
quit()
else:
target = random.randint(1, 99)

5. Don't do anything until you need to do it. See how I handled setting
the new target for what I mean. You set a new target before you know if
the player wants to play again. Its not a big deal in this case, but
these inefficiencies build up in larger applications. This isn't
"premature optimization", but simply good coding style [ed: put in to
fend off the "premature optimization is bad" puppets who infest this list].

Notice that the sequence of commands follows a logical sequence
corresponding to decisions in the game. It makes the code more sensible,
readable, and a little faster some times to boot. Good logic has the
happy consequence of good speed most of the time.

My code is typed real-time so is untested. No promise that there aren't
typos but you will probably get the idea. But see if you can plug the
these functions into the proper places and apply some of these techniques.

James

--
James Stroud
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA 90095

http://www.jamesstroud.com

S

#### Steven D'Aprano

I have one major problem with this; the 'replay' selection. It quits if
you put in 0, as it should, and continues if you put in any other
number. However, if you just press enter, it exits with an error. it
also looks really ugly, and I'm sure there has to be plenty of better
ways to do it.

Start by refactoring your code into small, easy to understand functions.
For example,

You mix in the same piece of code the logic for:

- error handling;
- starting a new game;
- quiting (although you use a function for this, well done);
- and game logic

and then you have to repeat it all again, almost word-for-word, for one
player mode and two player mode.

structure:

while you want to play a game:
play a game

which in Python might look like this:

playing = True
while playing:
play_one_game()
playing = play_again()

def play_again():
# For Python 3, change "raw_input" to "input".
response = raw_input("Would you like to play again? y/n ")
return response.strip().lower() == "y"

This function accepts *only* Y or y to play another game. Later, after
you've got the game working, you can come back to this and modify it so
that it accepts Yes or just enter on it's own. Make it work as simply as
possible first, then come back and make it more complicated later.

Now do the same thing for playing one game. A single game in two player
mode looks something like this:

pick a target number
until the target is guessed:
guess a number
let the other person be the guesser

which in Python might look like this:

def play_one_game():
target = pick_target() # you need to write this function
guessed = False
player = "Player One"
while not guessed:
guess = guess_number(player) # you need to write this too
if guess == target:
guessed = True
else:
player = swap_player(player) # player one <=> player two
# When we exit the loop, player is the person who guessed correctly.
if player == "Player One":
p1score += 1
else:
p2score += 1

Best of all, you can change from two player mode to one player mode just
by skipping the line "player = swap_player(player)". The rest of the code
remains exactly the same, and you don't need to repeat everything.

Have a play around with this approach, and then come back to us if you
need more hints.

J

#### James Stroud

James said:
1. Refactor. You should look at your code and see where you repeat the
same or similar patterns, see where they differ, make functions, and
make the differences parameters to the function call:

def guess(player, p1score, p2score):
guess1 = int(input("\n>> "))
if guess1 > 100:
print("ONLY NUMBERS FROM 1 TO 99")
elif guess1 > target:
print("TOO HIGH")
elif guess1 == target:
print("GOOD JOB, PLAYER %s! THE SCORE IS:" % player)
print("P1: %s --- P2: %s" % (p1score, p2score)))
print("PLAY AGAIN?")
#Set up the game again
play = int(input("0 TO END: "))
if play == 0:
print("GOOD BYE. PLAY AGAIN SOON!")
quit()
else:
target = random.randint(1, 99)
else:
print("TOO LOW")

I realized this has a bug. The target is locked in the scope of the
function. I wouldn't use global, though:

def guess(player, p1score, p2score):
target = None
guess1 = int(input("\n>> "))
if guess1 > 100:
print("ONLY NUMBERS FROM 1 TO 99")
elif guess1 > target:
print("TOO HIGH")
elif guess1 == target:
print("GOOD JOB, PLAYER %s! THE SCORE IS:" % player)
print("P1: %s --- P2: %s" % (p1score, p2score)))
print("PLAY AGAIN?")
#Set up the game again
play = int(input("0 TO END: "))
if play == 0:
print("GOOD BYE. PLAY AGAIN SOON!")
quit()
else:
target = random.randint(1, 99)
else:
print("TOO LOW")

Use it like this:

new_target = gues(player, p1score, p2score)
if new_target is not None:
target = new_target

I officially declare that I can't guarantee no more bugs in my previous
post. I just fixed this one because my conscience was bothering me.

James

--
James Stroud
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA 90095

http://www.jamesstroud.com

J

#### James Stroud

I forgot to return target:

def guess(player, p1score, p2score):
target = None
guess1 = int(input("\n>> "))
if guess1 > 100:
print("ONLY NUMBERS FROM 1 TO 99")
elif guess1 > target:
print("TOO HIGH")
elif guess1 == target:
print("GOOD JOB, PLAYER %s! THE SCORE IS:" % player)
print("P1: %s --- P2: %s" % (p1score, p2score)))
print("PLAY AGAIN?")
#Set up the game again
play = int(input("0 TO END: "))
if play == 0:
print("GOOD BYE. PLAY AGAIN SOON!")
quit()
else:
target = random.randint(1, 99)
else:
print("TOO LOW")
return target

--
James Stroud
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA 90095

http://www.jamesstroud.com

F

#### feba

#!/usr/bin/python
#Py3k, UTF-8

import random

def startup():
print("WELCOME TO THE SUPER NUMBER GUESSING GAME!")
global pnum, play, player, p1sc, p2sc
pnum = int(input("1 OR 2 PLAYERS?\n> "))
play = True
player = "P1" #P1 goes first
p1sc = 0 #Number of times...
p2sc = 0 #player guessed before opponent

def setup():
global target, guess, a, b
a = 1
b = 99
target = random.randint(a, b)
guess = 0 #Won't fall between 1 and 99

def playerswap():
global player
if player == "P1":
player = "P2"
else:
player = "P1"

def guessing():
global guess, player, target, play, pnum, p1sc, p2sc, a, b
guess = int(input("[%s-%s]%s>> " % (a, b, player))) #keeps the
user aware of min/max
if guess == target:
if pnum == 1:
print("CONGRATULATIONS!" )
else:
if player == "P1":
p1sc += 1
else:
p2sc += 1
print("CONGRATULATIONS %s! SCORE -- P1:%s P2:%s" %(player,
p1sc, p2sc))

playover = input("PLAY AGAIN? Y/N: ")
if playover.strip().lower() == "y":
play = True
setup()
else:
play = False
elif guess > b:
print("NUMBER MUST BE IN RANGE")
elif guess <= a:
print("NUMBER MUST BE IN RANGE")
elif guess > target:
print("TOO HIGH")
b = guess
else:
print("TOO LOW")
a = guess
if pnum ==2:
playerswap()

startup()
setup()

while play is True:
guessing()

This is what I have so far. better? worse? I'm guessing a mix of the
two. It took me a lot longer to get working, but I think it works
better. I also added a bit that tells you if you go higher or lower
than an already specified too high/low markers; although it doesn't
make you repeat that turn. I'm not sure if all those 'globals' are
bad, but they don't seem like they're something that should be good.
Functionally, it seems to work just fine.

J

#### James Stroud

feba said:
This is what I have so far. better? worse?

Much better. I didn't check if it works. But you need to figure out a
way to give up on your reliance on global variables. They will end up
stifling you in the long run when you move to substantial projects.

Also, you should start moving the nested if: then: blocks to functions.

Finally, you should limit your lines to no more than 80 chars. I do 71.
Don't be afraid to use transient variables to help.

Be assured that it takes on special intelligence to write unintelligible
code at will. Also be assured that fitting everything on one line is not
a good way to save time.

Aim for clarity.

If you want to start off on the right foot, you'll grind on your
guessing game until all variables are insulated within local scope and
the logic of the program becomes apparent from its architecture as a
collection of functions.

This is an example of how to combine these concepts:

def update_scores(player, p1sc, p2sc):
if player == "P1":
plsc +=1
else:
p2sc +=1
tmplt = "CONGRATULATIONS %s! SCORE -- P1:%s P2:%s"
print(tmplt % (player, p1sc, p2sc))
return p1sc, p2sc

p1sc, p2sc = update_scores(player, p1sc, p2sc)

James

J

#### James Stroud

James said:
Be assured that it takes on special intelligence to write unintelligible

I meant "*no* special intelligence".

F

#### feba

#!/usr/bin/python
#Py3k, UTF-8

import random

def setup():
#global target, guess, a, b
#a, b make minimum, maximum. Can be adjusted.
a, b = 1, 99
target = random.randint(a, b)
return a, b, target

def playerswitch(player):
#Player Switch
#if player's a witch, burn her!
if player == "P1":
player = "P2"
else:
player = "P1"
return player

def youwin(pnum, player, p1sc, p2sc):
if pnum == 1:
print("CONGRATULATIONS!")
else:
if player == "P1":
p1sc += 1
else:
p2sc += 1
end = "CONGRATULATIONS %s! SCORE -- P1:%s P2:%s"
print(end %(player, p1sc, p2sc))
return p1sc, p2sc

def playagain(play):
playover = input("PLAY AGAIN? Y/N: ")
if playover.strip().lower() == "y":
play = 1
a, b, target = setup()
else:
print("GOOD BYE. PLAY AGAIN SOON!")
quit()
return play, a, b, target

def guesscheck(guess, target, play, a, b, p1sc, p2sc):
if guess == target:
p1sc, p2sc = youwin(pnum, player, p1sc, p2sc)
play, a, b, target = playagain(play)
elif guess >= b:
print("NUMBER MUST BE IN RANGE")
guess = int(input("[%s-%s]%s>> " % (a, b, player)))
play, a, b, target, p1sc, p2sc = guesscheck(guess, target,
play,
a, b, p1sc, p2sc)
elif guess <= a:
print("NUMBER MUST BE IN RANGE")
guess = int(input("[%s-%s]%s>> " % (a, b, player)))
play, a, b, target, p1sc, p2sc = guesscheck(guess, target,
play,
a, b, p1sc, p2sc)
elif guess > target:
print("TOO HIGH")
b = guess
else:
print("TOO LOW")
a = guess
return play, a, b, target, p1sc, p2sc

def guessing(a, b, player, pnum, target, p1sc, p2sc, play):
#a and b are to keep the user aware of min/max
guess = int(input("[%s-%s]%s>> " % (a, b, player)))
play, a, b, target, p1sc, p2sc = guesscheck(guess, target, play,
a, b, p1sc, p2sc)
if pnum == 2:
player = playerswitch(player)
return play, a, b, player, target, p1sc, p2sc

#Let the show begin!
print("WELCOME TO THE SUPER NUMBER GUESSING GAME!")
pnum = int(input("1 OR 2 PLAYERS?\n> "))
play = 1
player = "P1" # P1 goes first
#Scores, keep track of times player guessed first.
p1sc, p2sc = 0, 0

#Grabs minimum, maximum, and target numbers
a, b, target = setup()

while play == 1:
play, a, b, player, target, p1sc, p2sc \
= guessing(a, b, player, pnum, target, p1sc, p2sc, play)

This is what I have now. It seems to work perfectly, and I tried to
work in your suggestions. Thanks, by the way, for recommending the
style guide. I've bookmarked it, and I'll try to remember to consult
it in the future.

One (extremely minor) annoyance is that if you have only one possible
guess left you still have to type it in manually. As an example,
[30-32]P#>> can only possibly be 31 (and I've changed it so that any
number >= 32 or <= 30 won't be accepted, so it's all the user can
input), which means whatever player is in control has a %100 chance of
winning. I know that "if b - a == 2" would be a simple enough test for
this, but I'm not quite sure when to put it, or what to do with it.

And yes, I'm aware that

guess = int(input("[%s-%s]%s>> " % (a, b, player)))
play, a, b, target, p1sc, p2sc = guesscheck(guess, target, play,
a, b, p1sc, p2sc)

is repeated three times. I will probably try to change this into a
function later on; right now, I just spent a few hours trying to get
this to work and making sure that it does (by playing it), so I'm
going to take a break.

B

#### Bruno Desthuilliers

feba a écrit :
#!/usr/bin/python
#Py3k, UTF-8

import random

def startup():
print("WELCOME TO THE SUPER NUMBER GUESSING GAME!")
global pnum, play, player, p1sc, p2sc

You should now try to rewrite the whole thing to avoid using globals.
pnum = int(input("1 OR 2 PLAYERS?\n> "))

What happens here if you type something that's not a valid argument for
int() ?

(snip)

B

#### Bruno Desthuilliers

feba a écrit :
#!/usr/bin/python
#Py3k, UTF-8

import random

def setup():
#global target, guess, a, b
#a, b make minimum, maximum. Can be adjusted.
a, b = 1, 99
target = random.randint(a, b)
return a, b, target

Seems ok. You may want to use arguments with default values for a and b
(and possibly to use more meaningfull names):

def setup(mini=1, maxi=99)
# sanity check
if mini >= maxi:
raise ValueError("mini must be lower than maxi")
target = random.randint(mini, maxi)
return mini, maxi, target
def playerswitch(player):
#Player Switch
#if player's a witch, burn her!

Extra bonus point for quoting Monty Pythons !-)
if player == "P1":
player = "P2"
else:
player = "P1"
return player

I'd name this one "switch_player" (most of the time, function names
should be verbs) - but this is mostly a matter of personal taste !-)

Minor point for a short program, but still good practice : use
constants. IE :

# at the top level
PLAYER_1 = 1
PLAYER_2 = 2

.....
if player == PLAYER_1:
....

Also, you could use early returns here instead of rebinding then
returning player.

def switch_player(player):
if player == PLAYER_1:
return PLAYER_2
else:
return PLAYER_1
def youwin(pnum, player, p1sc, p2sc):

I assume 'p1sc' means "player_1_score" ?

If so, you may want to use a dict for scores:

scores = {PLAYER_1:0, PLAYER_2:0}

then...
if pnum == 1:
print("CONGRATULATIONS!")

else:
if player == "P1":
p1sc += 1
else:
p2sc += 1

Which would then become:
scores[player] +=1
end = "CONGRATULATIONS %s! SCORE -- P1:%s P2:%s"
print(end %(player, p1sc, p2sc))
return p1sc, p2sc

def playagain(play):
playover = input("PLAY AGAIN? Y/N: ")
if playover.strip().lower() == "y":
play = 1

If it's meant to be 0/1 flag, Python has proper booleans too (True and
False).
a, b, target = setup()
else:
print("GOOD BYE. PLAY AGAIN SOON!")
quit()

It might be better to avoid exiting so brutally. Why not returning 0,
none, None, None ?
return play, a, b, target

def guesscheck(guess, target, play, a, b, p1sc, p2sc):
if guess == target:
p1sc, p2sc = youwin(pnum, player, p1sc, p2sc)
play, a, b, target = playagain(play)
elif guess >= b:
print("NUMBER MUST BE IN RANGE")
guess = int(input("[%s-%s]%s>> " % (a, b, player)))
play, a, b, target, p1sc, p2sc = guesscheck(guess, target,
play,
a, b, p1sc, p2sc)
elif guess <= a:
print("NUMBER MUST BE IN RANGE")
guess = int(input("[%s-%s]%s>> " % (a, b, player)))
play, a, b, target, p1sc, p2sc = guesscheck(guess, target,
play,
a, b, p1sc, p2sc)
elif guess > target:
print("TOO HIGH")
b = guess
else:
print("TOO LOW")
a = guess
return play, a, b, target, p1sc, p2sc

def guessing(a, b, player, pnum, target, p1sc, p2sc, play):
#a and b are to keep the user aware of min/max
guess = int(input("[%s-%s]%s>> " % (a, b, player)))
play, a, b, target, p1sc, p2sc = guesscheck(guess, target, play,
a, b, p1sc, p2sc)
if pnum == 2:
player = playerswitch(player)
return play, a, b, player, target, p1sc, p2sc

#Let the show begin!

You should either put this in it's own function (could be named 'main'),
or at least "protect" it with an "if __name__ == '__main__':" test.
print("WELCOME TO THE SUPER NUMBER GUESSING GAME!")
pnum = int(input("1 OR 2 PLAYERS?\n> "))
play = 1
player = "P1" # P1 goes first
#Scores, keep track of times player guessed first.
p1sc, p2sc = 0, 0

#Grabs minimum, maximum, and target numbers
a, b, target = setup()

while play == 1:
play, a, b, player, target, p1sc, p2sc \
= guessing(a, b, player, pnum, target, p1sc, p2sc, play)

This is what I have now.

And it looks way better than your first version.

(snip)

J

#### James Stroud

It looks much better. But as Bruno suggests and as I alluded to earlier,
you should get in the habit of forming verification loops on input
channels that aren't already guaranteed to provide valid input messages.
I'm not sure how to say that in English, but in python my example was:

while True:
try:
guess1 = int(input("\n>> "))
except ValueError:
else:
break

Other than that, you still have some points where you can break lines
down to improve clarity. Think of it like trying to avoid run-on sentences.

Over all, it looks like you have picked up some good technique that you
could only get by experimentation over the course of about 5 or 6 months.

James

F

#### feba

Seems ok. You may want to use arguments with default values for a and b
(and possibly to use more meaningfull names):

I changed it to minr and maxr. Mini is fine, but I can't name a
variable maxi unless I have a good feminine hygiene joke to use with
it.

I don't see the aim of your changes to setup(). I can kinda understand
checking to make sure that you didn't make the minimum higher than the
maximum, but I think where you put minr/maxr would make it use the
same minr/maxr as the end of the previous game, wouldn't it?
Minor point for a short program, but still good practice : use
constants. IE :

I had done this initially, but it seemed wasteful and needlessly
confusing in this situation.
I assume 'p1sc' means "player_1_score" ?
If so, you may want to use a dict for scores:

I don't really understand dicts yet; actually, the tutorial I'm
following (http://www.briggs.net.nz/log/writing/snake-wrangling-for-
kids/ , designed for tweens, but other than the pointless anecdote and
joke here and there, I've found it a very good guide) doesn't even
seem to mention them, from a search of the pdf. Actually, apparently I
stopped and started working on this just before the chapter on
functions and modules.

I'll look into that later on, but for now I'm pretty happy with how it
works.
<ot>Is it really necessary to WRITE THIS IN ALL UPPERS ?-)</ot>

If you didn't notice, in the original it was "CONGLATURATIONS".

I could also make a "VIDEO GAME BAND. Heavy Metal's Not Dead." joke
here, but I'm afraid a disappointingly small amount of people will get
it.
Python has proper booleans too

Yeah, I had those initially too, but "play = True" was causing trouble
for some reason when I copy/pasted it into IDLE to try to
troubleshoot, so I changed it to 1. I'll probably change it back
later.
You should either put this in it's own function (could be named 'main'),
or at least "protect" it with an "if __name__ == '__main__':" test.

Could you go into a bit more detail on this? I don't understand what
should be its own function, nor do I understand what that line would
do or how to use it.

James, could you work that into a section of what I have to make it a
bit easier to understand?

J

#### James Stroud

feba said:
I don't see the aim of your changes to setup(). I can kinda understand
checking to make sure that you didn't make the minimum higher than the
maximum, but I think where you put minr/maxr would make it use the
same minr/maxr as the end of the previous game, wouldn't it?

No. Each function call creates its own name space. The function as Bruno
has written it will have default values (mini & maxi). A call to this
function can set different values optionally:

setup(2) #==> range from 2 to 99
setup(maxi=101) #==> range from 1 to 101
setup(5, 10) #==> range from 5 to 10
setup(10, 5) #==> throws an error
I had done this initially, but it seemed wasteful and needlessly
confusing in this situation.

No. Tracking down and changing hard-coded values within a module is
wasteful and needlessly confusing. Creating well named and well
documented module level constants is good style and will make your life
easier in the long run.
I don't really understand dicts yet; actually, the tutorial I'm
following (http://www.briggs.net.nz/log/writing/snake-wrangling-for-
kids/ , designed for tweens, but other than the pointless anecdote and
joke here and there, I've found it a very good guide) doesn't even
seem to mention them, from a search of the pdf. Actually, apparently I
stopped and started working on this just before the chapter on
functions and modules.

scores = {'player 1' : 0, 'player 2' : 0 }
scores['player 2'] = 10 #==> now player 2's score is 10
scores['player 1'] += 1 #==> now player 1's score is 1
print scores['player 0'] #==> prints "0"
print scores['player 2'] * 2 #==> prints "20"

If you get that, you'll have about all you need to know about dicts to
use them for keeping track of scores (and other values) in your game.
I'll look into that later on, but for now I'm pretty happy with how it
works.

Try it sooner rather than later. I didn't get this kind of advice when I
was first learning. It would have shaved months from my learning curve.

Could you go into a bit more detail on this? I don't understand what
should be its own function, nor do I understand what that line would
do or how to use it.

The idea is that everything you write is reusable and can be imported as
a module. Upon importing a module, it's code is executed. So, as
written, if someone imports it as a library module, they will start
playing the game. Wrapping in the "if __name__ == '__main__':" test
prevents the main loop of the game from executing on import. Only when
the module is "__main__" will that test evaluate to true and its
commands execute.

For small programs like you have here, I'd do it like this (again
combining ideas):

def num_players(game, prompt='1 or 2 Players?\n> '):
while True:
num = input(prompt)
try:
num = int(num)
except ValueError:
else:
break
game['pnum'] = num

def main(game=None):
if game is None:
game = {}
print("WELCOME TO THE SUPER NUMBER GUESSING GAME!")
num_players(game)
game['play'] = 1
# P1 goes first
game['player'] = "P1"
#Scores, keep track of times player guessed first.
game['p1sc'], game['p2sc'] = 0, 0
setup(game)

while game['play'] == 1:
guessing(game)

if __name__ == "__main__":
main()

I just threw a little advanced stuff at you. But you would be doing
yourself a huge favor if you struggled to understand it. The idea is
that since game is a dict, you can modify game inside of the functions,
and all you do is pass the game around to functions that need the values
of its contents.

For example:

def setup(game):
game['a'], game['b'] = 1, 99
game['target'] = random.randint(a, b)

def playagain(game):
playover = input("PLAY AGAIN? Y/N: ")
if playover.strip().lower() == "y":
game['play'] = 1
setup(game)
else:
print("GOOD BYE. PLAY AGAIN SOON!")
quit()

Notice that I just made return values obsolete.

You will also notice that management of all of your values now becomes
more tractable using the mutability of dict ("game"). Long, ugly lines
now become simple function calls. See if you can understand what I'm
doing here and try to propagate the idea through your game using the
game dict.

If you can do it, you'll be at least 80% of the way to understanding
object oriented programming, by the way.

James

--
James Stroud
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA 90095

http://www.jamesstroud.com

S

#### Steven D'Aprano

I don't really understand dicts yet; actually, the tutorial I'm
following (http://www.briggs.net.nz/log/writing/snake-wrangling-for-
kids/ , designed for tweens, but other than the pointless anecdote
and joke here and there, I've found it a very good guide) doesn't
even seem to mention them, from a search of the pdf. Actually,
apparently I stopped and started working on this just before the
chapter on functions and modules.

I'll look into that later on, but for now I'm pretty happy with how
it works.

Dicts, or dictionaries, also known as "hash tables" in some computer
languages, are a mapping from a key to a value. Think of looking up a
word in a real dictionary: the word is the key, and the definition is
the value.

Imagine a game with multiple players, each known by their name.
Because you don't know how many players there are, or what their
names are, you can't do this:

fred_score = 0 # How do I know there's a player called Fred?
barney_score = 0
....

But you can do this:

names = get_players_names()
for name in names:
# the player name is the key, and the score is the value
scores[name] = 0
print scores

=> {"fred": 0, "barney": 0, "wilma": 0, "betty": 0}

(or whatever names you have been given).

Later, you want to print Fred's score:

print "%s's score is %d" % ("fred", scores["fred"])

will print "fred's score is 0".

You might need to add 1 to Wilma's score:

score["wilma"] += 1

And so forth.

[...]
Could you go into a bit more detail on this? I don't understand what
should be its own function, nor do I understand what that line would
do or how to use it.

Consider a really simple Python module:

# module.py
def hello():
print "Hello parrot!"

print "Running the module"
hello()
# end module.py

If you execute that file, from the commandline or the desktop, it
prints

Running the module
Hello parrot!

just as you expect. But when another Python module imports it, using
the command "import module", not only is the function hello() loaded,
but the two lines above are printed too -- but only the first time
you import the module. This is usually not what you want.

Generally, you want the *execution* to be separate from the
*importing*. There is a way to do this is Python:

# module.py
def hello():
print "Hello parrot!"

if __name__ == "__main__":
print "Running the module"
hello()
# end module.py

When you import the module, Python sets the special variable
"__name__" to the string "module". It's the name of the module. But
when you are executing the file from the command line, Python sets
the special variable to the magic string "__main__" instead. So the
code inside the if __name__ block is only executed when you are
actually executing the module, not when you import the module.

Hope this helps somewhat.

F

#### feba

Alright! This is feeling more like it.

#!/usr/bin/python
#Py3k, UTF-8
import random

def setup(game, minr=1, maxr=99):
#minr, maxr make minimum and maximum. Can be adjusted.
game['minr'], game['maxr'] = minr, maxr
game['gcount'] = 0 #Reset guess count
game['target'] = random.randint(minr, maxr)

def playerswitch(game):
#Player Switch
#if player's a witch: burn(her)
if game['player'] == game['player1']:
game['player'] = game['player2']
else:
game['player'] = game['player1']

def youwin(game):
if game['pnum'] == 1:
print("CONGRATULATIONS! IT TOOK YOU %s GUESSES" % game
['gcount'])
else:
if game['player'] == game['player1']:
game['p1sc'] += 1
else:
game['p2sc'] += 1
end = "CONGRATULATIONS %s! SCORE -- P1:%s P2:%s"
#Can the following line be more compact?
print(end % (game['player'], game['p1sc'], game['p2sc']))

def playagain(game):
playover = input("PLAY AGAIN? Y/N: ")
if playover.strip().lower() == "y":
game['play'] = True
setup(game)
else:
print("GOOD BYE. PLAY AGAIN SOON!")
game['play'] = False

def autofinish(game):
if game['maxr'] - game['minr'] == 2:
print("...ONLY ONE OPTION LEFT!")
youwin(game)
playagain(game)

def numplayers(game, prompt="1 OR 2 PLAYERS?\n> "):
while True:
num = input(prompt)
try:
num = int(num)
except ValueError:
else:
break
game['pnum'] = num

def guesses(game):
game['guess'] = int(input("[%s-%s]%s>> " \
#keeps user aware of who's turn it is, and the range
% (game['minr'], game['maxr'], game['player'])))

def guesscheck(game):
if game['guess'] == game['target']:
if game['pnum'] == 1:
game['gcount'] += 1
youwin(game)
playagain(game)
elif game['guess'] >= game['maxr']:
print("NUMBER MUST BE IN RANGE")
guesses(game)
guesscheck(game)
elif game['guess'] <= game['minr']:
print("NUMBER MUST BE IN RANGE")
guesses(game)
guesscheck(game)
elif game['guess'] > game['target']:
print("TOO HIGH")
if game['pnum'] == 1:
game['gcount'] += 1
game['maxr'] = game['guess']
else:
print("TOO LOW")
if game['pnum'] == 1:
game['gcount'] += 1
game['minr'] = game['guess']

def guessing(game):
guesses(game)
guesscheck(game)
if game['pnum'] == 2:
playerswitch(game)
autofinish(game)

def main(game=None):
if game is None:
game = {}
print("WELCOME TO THE SUPER NUMBER GUESSING GAME!")
numplayers(game)
game['play'] = True
game['player1'], game['player2'] = "P1", "P2"
game['player'] = game['player1'] # P1 goes first
#Scores start at 0
game['p1sc'], game['p2sc'], game['gcount'] = 0, 0, 0
setup(game)

while game['play'] is True:
guessing(game)

if __name__ == "__main__":
main()

first off, I want to thank all of you for your help with this. I
really don't think I could've learned all of this out nearly as
near the grasp I have on it now. '''This''' is why I like learning by
doing. The only things I still don't really understand are .strip
().lower(), and try/except/else, and I plan on looking them up before
I do anything else. In the past few hours I've gone from not having a
clue what the whole {'fred': 0, 'barney': 0} thing was about to being
able to fully understand what you're talking about, and put it into
practice

2; I feel like this process is going quicker and quicker every time I
refine it. It also feels like it's getting easier to solve various
bugs when <s>I create them</s> they pop up. It might be because I'm
getting into it and having more fun, because the refinements are less
major each time, because they're easier to pick up, or some
combination of all of those. Either way, I feel very excited about it.

3; I found some very helpful gedit plugins. While I was in
preferences, I noticed you can have it highlight the margin; so I set
that to 75 to try to keep from going over the character limit/line
recommendation. Draw Spaces, a plugin, showing spaces is also pretty
helpful. I also found a python auto complete plugin which I haven't
used so far, but which looks very promising, along with a terminal
program (Keeps me from juggling windows, anyway)

4; I readded the counter for one player games. Out of curiosity, what
do you think of:

if game['pnum'] == 1:
game['gcount'] += 1

? I'm not sure whether this is good or bad. On the one hand, it keeps
it from adding to gcount without needing to, on the other hand it
seems like it might be more wasteful to check pnum than to just add to
gcount. It also makes it harder to adjust it to show gcount in two
player mode, if you want to do that for some reason.

5; I added the ability for it to automatically complete when there's
only one option left. I was amazed' I was actually going to ask for
advice on how to do it here. I was going to say "I was thinking (blah
blah)", but then I just typed it in, and it worked flawlessly. This
goes back to one, but I'm very excited, and thankful.

6; can anyone think of anything else to add on to/do with this game?
With the minr/maxr display, multiplayer, score keeping, and
automation, I'm just about all of ideas. All I can think of left to
add is 3 and 4 player modes, or a fork where player 2 can't win
(kekekekeke. Though I'm not sure how to do it...), both of which I
feel are somewhat pointless for a game like this. If I can't learn
anything more from it, I will probably go back to reading python
guides for a bit, and then try to make something else.

F

#### feba

I added the ability to select your own range. It takes two new
modules:

def customrange(game, lowunsafe=True):
game['defrang'] = False #Keeps setup from changing range to
defaults
while lowunsafe: #makes sure that the low number is positive
picklow = int(input("PLEASE PICK THE LOW NUMBER: "))
if picklow < 0:
print("LOW NUMBER MUST BE POSTIVE")
else:
lowunsafe = False
pickhigh = int(input("PLEASE PICK THE HIGH NUMBER: "))
if pickhigh - picklow <= 2: #see setup().
print("HIGH MUST BE AT LEAST THREE GREATER THAN LOW")
else:
game['minr'], game['maxr'] = picklow, pickhigh
print("RANGE IS [%s-%s]!" % (game['minr'], game['maxr']))

def wantcustom(game, unsure=True):
#Allows user to decide their own range for guessing.
while unsure:
pickrange = input("WOULD YOU LIKE TO CREATE A CUSTOM RANGE? Y/
N: ")
if pickrange.lower() == "n":
game['minr'], game['maxr'] = 1, 99 #Default range
unsure = False
elif pickrange.lower() == "y":
customrange(game)
unsure = False
else:
print("INVALID INPUT")

A slightly updated setup (it needed it anyway):

def setup(game):
#minr, maxr make minimum and maximum. Can be adjusted.
#Make sure that maxr - minr is at least 3.
#1 or less would be impossible. 2 would only have one guess for
victory
#The first would be unplayable, the second would play itself
if game['maxr'] - game['minr'] <= 2:
raise ValueError("INVALID RANGE!")
game['gcount'] = 0 #Reset guess count
game['target'] = random.randint(game['minr'], game['maxr'])

and putting wantcustom(game) immediately before setup(game) in main().

B

#### Bruno Desthuilliers

feba a écrit :
Alright! This is feeling more like it.

#!/usr/bin/python
#Py3k, UTF-8
import random
(snip)

def youwin(game):
if game['pnum'] == 1:
print("CONGRATULATIONS! IT TOOK YOU %s GUESSES" % game
['gcount'])
else:
if game['player'] == game['player1']:
game['p1sc'] += 1
else:
game['p2sc'] += 1

player1 = dict(score=0)
player2 = dict(score=0),

game = dict(
player1 = player1,
player2 = player2
player = player1
# ...
)

you wouldn't need the test on
game['player'] == game["player1"]

, and could just use:

game["player"]["score"] += 1

(snip)
first off, I want to thank all of you for your help with this. I
really don't think I could've learned all of this out nearly as
near the grasp I have on it now. '''This''' is why I like learning by
doing. The only things I still don't really understand are .strip
().lower(),

..strip() returns a copy of the string without leading and ending
whitespaces (inlcuding newlines, tabs etc). .lower() returns a copy of
the string in all lowercases. Since .strip() returns a string object,
you can chain method calls.

is just a shortcut for

tmp1 = thestring.strip() # => "yaDDA"
tmp2 = tmp1.lower() # => "yadda"
and try/except/else, and I plan on looking them up before
I do anything else. In the past few hours I've gone from not having a
clue what the whole {'fred': 0, 'barney': 0} thing was about to being
able to fully understand what you're talking about, and put it into
practice

Quite close... You still failed to understand how dicts could be used to
replace 'if/else' statements (dict-base dispatch is very idiomatic in
Python, and is also a good introduction to OO).

(snip)
5; I added the ability for it to automatically complete when there's
only one option left. I was amazed' I was actually going to ask for
advice on how to do it here. I was going to say "I was thinking (blah
blah)", but then I just typed it in, and it worked flawlessly.

Yeps. That's probably why most of us here fell in love with Python: it
makes simple thing simple, and tend to JustWork(tm).
6; can anyone think of anything else to add on to/do with this game?

rewrite it once again using objects instead of dicts ?

Anyway, thanks for sharing your enthusiasm with us.

F

#### feba

Spent a bit more time looking over suggestions and working out some
annoyances.

import random

def customrange(game, lowunsafe=True):
game['defrang'] = False #Keeps setup from changing range to
defaults
while lowunsafe: #makes sure that the low number is positive
picklow = int(input("PLEASE PICK THE LOW NUMBER: "))
if picklow < 0:
print("LOW NUMBER MUST BE POSTIVE")
else:
lowunsafe = False
pickhigh = int(input("PLEASE PICK THE HIGH NUMBER: "))
if pickhigh - picklow <= 2: #see setup().
print("HIGH MUST BE AT LEAST THREE GREATER THAN LOW")
else:
game['minr'], game['maxr'] = picklow, pickhigh
print("RANGE IS [%s-%s]!" % (game['minr'], game['maxr']))

def wantcustom(game, unsure=True):
#Allows user to decide their own range for guessing.
while unsure:
pickrange = input("WOULD YOU LIKE TO CREATE A CUSTOM RANGE? Y/
N: ")
if pickrange.lower() == "n":
game['minr'], game['maxr'] = 1, 99 #Default range. see
setup
unsure = False
elif pickrange.lower() == "y":
customrange(game)
unsure = False
else:
print("INVALID INPUT")

def samesettings(game, unsure=True):
while unsure:
keepset = input("USE SAME SETTINGS? Y/N: ")
if keepset.lower() == "y":
game['minr'], game['maxr'] = 1, 99 #Default range. see
setup
unsure = False
elif keepset.lower() == "n":
wantcustom(game)
numplayers(game)
unsure = False
else:
print("INVALID INPUT")

def setup(game):
#minr, maxr make minimum and maximum. Can be adjusted.
#Make sure that maxr - minr is at least 3.
#1 or less would be impossible. 2 would only have one guess for
victory
#The first would be unplayable, the second would play itself
if game['maxr'] - game['minr'] <= 2:
raise ValueError("INVALID RANGE!") #If this fails, check line
43
game['gcount'] = 0 #Reset guess count
game['target'] = random.randint(game['minr'], game['maxr'])

def playerswitch(game):
#Player Switch
#if player's a witch: burn(her)
if game['player'] == game['player1']:
game['player'] = game['player2']
else:
game['player'] = game['player1']

def youwin(game):
if game['pnum'] == 1:
print("CONGRATULATIONS! IT TOOK YOU %s GUESSES" % game
['gcount'])
else:
if game['player'] == game['player1']:
game['p1sc'] += 1
else:
game['p2sc'] += 1
end = "CONGRATULATIONS %s! SCORE -- P1:%s P2:%s"
print(end % (game['player'], game['p1sc'], game['p2sc']))

def playagain(game, unsure=True):
while unsure:
playover = input("PLAY AGAIN? Y/N: ")
if playover.lower() == "y":
game['play'] = True
samesettings(game)
setup(game)
unsure = False
elif playover.lower() == "n":
print("GOOD BYE. PLAY AGAIN SOON!")
game['play'] = False
unsure = False
else:
print("INVALID INPUT")

def autofinish(game):
if game['maxr'] - game['minr'] == 2:
print("...ONLY ONE OPTION LEFT!")
youwin(game)
playagain(game)

def numplayers(game, unsafe=True, prompt="1 OR 2 PLAYERS?\n> "):
while unsafe:
while True:
num = input(prompt)
try: #Make sure that num is valid
num = int(num)
except ValueError:
else:
break
if num == 1 or 2: #ONLY allow 1 or 2P.
unsafe = False
else:
print("INVALID INPUT")
game['pnum'] = num

def guesses(game, unsafe=True):
while unsafe:
while True:
try:
guess = int(input("[%s-%s]%s>> " \
#keeps user aware of who's turn it is, and the range
% (game['minr'], game['maxr'], game['player'])))
except ValueError:
else:
break
if guess >= game['maxr']:
print("NUMBER MUST BE IN RANGE")
guesses(game)
guesscheck(game)
elif guess <= game['minr']:
print("NUMBER MUST BE IN RANGE")
guesses(game)
guesscheck(game)
else:
unsafe = False
game['guess'] = guess

def guesscheck(game):
if game['guess'] == game['target']:
if game['pnum'] == 1:
game['gcount'] += 1
youwin(game)
playagain(game)
elif game['guess'] > game['target']:
print("TOO HIGH")
if game['pnum'] == 1:
game['gcount'] += 1
game['maxr'] = game['guess']
else:
print("TOO LOW")
if game['pnum'] == 1:
game['gcount'] += 1
game['minr'] = game['guess']

def guessing(game):
guesses(game)
guesscheck(game)
if game['pnum'] == 2:
playerswitch(game)
autofinish(game)

def main(game=None):
if game is None:
game = {}
print("WELCOME TO THE SUPER NUMBER GUESSING GAME!")
game['play'] = True
game['player1'], game['player2'] = "P1", "P2"
game['player'] = game['player1'] # P1 goes first
#Scores start at 0
game['p1sc'], game['p2sc'], game['gcount'] = 0, 0, 0
wantcustom(game)
numplayers(game)
setup(game)

while game['play'] is True:
guessing(game)

if __name__ == "__main__":
main()

This is basically finding a balance between being annoyed by prompts
and being annoyed by having to quit and restart. Given the sorts of
people who will play this longer than to find out how it works are the
sort of people who find spreadsheets and powerpoints and EVE online
fun, I'll go with prompts.

F

#### feba

.strip() returns a copy of the string without leading and ending
whitespaces (inlcuding newlines, tabs etc).

Ahh. I had removed it because it didn't seem to do anything, but I've

And I understand your dictionary stuff correctly now, I think, and I
worked it in. Currently, I have:

import random

def safeint(prompt="y"):
while True:
x = input(prompt)
try:
x = int(x)
except ValueError:
else:
break
return x

def safestr(prompt="y"):
while True:
x = input(prompt)
try:
x = str(x)
except ValueError:
else:
break
return x

def customrange(game, lowunsafe=True):
game['defrang'] = False #Keeps setup from changing range to
defaults
while lowunsafe: #makes sure that the low number is positive
picklow = safeint(prompt="PLEASE PICK THE LOW NUMBER: ")
if picklow < 0:
print("LOW NUMBER MUST BE POSITIVE")
else:
lowunsafe = False
pickhigh = safeint(prompt="PLEASE PICK THE HIGH NUMBER: ")
if pickhigh - picklow <= 2: #see setup().
print("HIGH MUST BE AT LEAST THREE GREATER THAN LOW")
else:
game['minr'], game['maxr'] = picklow, pickhigh
print("RANGE IS [%s-%s]!" % (game['minr'], game['maxr']))

def wantcustom(game, unsure=True):
#Allows user to decide their own range for guessing.
while unsure:
tryrange = safestr(prompt=\
"WOULD YOU LIKE TO CREATE A CUSTOM RANGE?
"\
+"Y/N: ")
if tryrange.strip().lower() == "n":
game['minr'], game['maxr'] = 1, 99 #Default range. see
setup
unsure = False
elif tryrange.strip().lower() == "y":
customrange(game)
unsure = False
else:
print("INVALID INPUT")

def samesettings(game, unsure=True):
while unsure:
keepset = safestr(prompt="USE SAME SETTINGS? Y/N: ")
if keepset.strip().lower() == "y":
game['minr'], game['maxr'] = 1, 99 #Default range. see
setup
unsure = False
elif keepset.strip().lower() == "n":
wantcustom(game)
numplayers(game)
unsure = False
else:
print("INVALID INPUT")

def setup(game):
#minr, maxr make minimum and maximum. Can be adjusted.
#Make sure that maxr - minr is at least 3.
#1 or less would be impossible. 2 would only have one guess for
victory
#The first would be unplayable, the second would play itself
if game['maxr'] - game['minr'] <= 2:
raise ValueError("INVALID RANGE!") #If this fails, check line
43
game['gcount'] = 0 #Reset guess count
game['target'] = random.randint(game['minr'], game['maxr'])

def playerswitch(game):
#Player Switch
#if player's a witch: burn(her)
if game['player'] is game['player1']:
game['player'] = game['player2']
else:
game['player'] = game['player1']

def youwin(game):
if game['pnum'] == 1:
print("CONGRATULATIONS! IT TOOK YOU %s GUESSES" % game
['gcount'])
else:
game['player']['score'] += 1
end = "CONGRATULATIONS %s! SCORE -- P1:%s P2:%s"
print(end % (game['player']['name'],\
game['player1']['score'], game['player2']['score']))

def playagain(game, unsure=True):
while unsure:
playover = safestr(prompt="PLAY AGAIN? Y/N: ")
if playover.strip().lower() == "y":
game['play'] = True
samesettings(game)
setup(game)
unsure = False
elif playover.strip().lower() == "n":
print("GOOD BYE. PLAY AGAIN SOON!")
game['play'] = False
unsure = False
else:
print("INVALID INPUT")

def autofinish(game):
if game['maxr'] - game['minr'] == 2:
print("...ONLY ONE OPTION LEFT!")
youwin(game)
playagain(game)

def numplayers(game, unsafe=True):
while unsafe:
num = safeint(prompt="1 OR 2 PLAYERS?\n> ")
if num == 1 or 2: #ONLY allow 1 or 2P.
unsafe = False
else:
print("INVALID INPUT")
game['pnum'] = num

def guesses(game, unsafe=True):
while unsafe:
guess = safeint(prompt="[%s-%s]%s>> " % \
#Shows range
(game['minr'], game['maxr'],\
#And which player's turn
game['player']['name']))
if guess >= game['maxr']:
print("NUMBER MUST BE IN RANGE")
guesses(game)
guesscheck(game)
elif guess <= game['minr']:
print("NUMBER MUST BE IN RANGE")
guesses(game)
guesscheck(game)
else:
unsafe = False
game['guess'] = guess

def guesscheck(game):
if game['guess'] == game['target']:
if game['pnum'] == 1:
game['gcount'] += 1
youwin(game)
playagain(game)
elif game['guess'] > game['target']:
print("TOO HIGH")
if game['pnum'] == 1:
game['gcount'] += 1
game['maxr'] = game['guess']
else:
print("TOO LOW")
if game['pnum'] == 1:
game['gcount'] += 1
game['minr'] = game['guess']

def guessing(game):
guesses(game)
guesscheck(game)
if game['pnum'] == 2:
playerswitch(game)
autofinish(game)

def main(game=None):
player1, player2 = dict(name="P1",score=0), dict
(name="P2",score=0)
if game is None:
game = dict(
player1 = player1,
player2 = player2,
player = player1,
play = True
)
print("WELCOME TO THE SUPER NUMBER GUESSING GAME!")
wantcustom(game)
numplayers(game)
setup(game)

while game['play'] is True:
guessing(game)

if __name__ == "__main__":
main()

rewrite it once again using objects instead of dicts ?

I'd need to find out how those work, and I have a list of python stuff
to read piling up anyway... That said, I think for something like
that, something that's not a major flaw, I'd prefer to make something
else, and maybe work on this again later on. There is only so much
guessing numbers one person can take.