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.
> 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:
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: print 'Bad value.' 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
On Sat, 13 Dec 2008 00:57:12 -0800, feba wrote: > 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.
Start with a high-level approach. The guessing game has the following structure:
while you want to play a game: play a game ask play again?
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 start with one person as the guesser 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.
James Stroud wrote: > 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
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.
feba wrote: > 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 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.
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.
> 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
> 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)
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:
>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?
feba wrote: > 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
>> 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.
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 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.
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.
>> 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.
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: print "Bad Value" 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.
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
On Mon, 15 Dec 2008 01:06:51 -0800, feba wrote: > 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() scores = {} # start with an empty dict for name in names: # the player name is the key, and the score is the value scores[name] = 0 print scores
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.
[...]
>>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.
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.
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: print("BAD VALUE") 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 quickly by reading tutorials and documentation, let alone had anything 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.
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().
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 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: print("BAD VALUE!") 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: print("BAD VALUE!") 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 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.
> #!/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
If you had initialized your "game" dict with
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 > quickly by reading tutorials and documentation, let alone had anything > 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.
> 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.
> .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 readded it.
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: print("BAD INPUT!") else: break return x
def safestr(prompt="y"): while True: x = input(prompt) try: x = str(x) except ValueError: print("BAD INPUT!") 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 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 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.
You could as well replace the 'break' statement by 'return x' - since returning will obviously break the loop
Now don't you notice kind of a pattern in these two functions ? obviously, the only thing that change is the conversion/validation part. The good news is that Python functions are objects too, so you can pass them as params to another function. The generic version of your above code could be:
def safeinput(prompt, convert): while True: x = input(prompt) try: x = convert(x) except ValueError, e: print("Bad input : %s" % e) else: return x
then you can use it for int:
i = safeinput("Please enter an integer", int)
or use more complex conversion/validation:
def yesno(s): s = s.strip().lower() if not s in ("y", "n"): raise ValueError("please answer with 'y' or 'n'") # we return a boolean return s == 'y'
answer = safeinput("really do this ?", yesno)
Last point : if your conversion/validation function needs more arguments than the value to convert - ie : converting to an int and vaidating this int is in a specified range, which require mini and maxi arguments for the range - you can use partial evaluation[1]:
from functools import partial
def int_in_range(x, mini, maxi): x = int(x) if not mini <= x <= maxi: raise ValueError("%s is not in range (%s, %s)" % (x, mini, maxi)) return x
i = safeinput( "Please enter an int between 1 and 99", partial(int_in_range, mini=1, maxi=99) )
[1] given the function f(x, y): return x + y, the partial evaluation partial(f, x=2) is a function fx(y) that when called returns the result of f(2, y).
(snip)
>> rewrite it once again using objects instead of dicts ?
> I'd need to find out how those work,
Mostly as glorified dicts with associated functions. Your game and players dicts are obvious candidates. Whenever you see a dict with a more or less defined "structure" and a set of functions working on this dict, you have in fact a class waiting to be born.
> 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.
Indeed. That was just a suggestion, and it would have been pretty interesting IMHO as a basis for a "from Q&D procedural scripting to OO application programing" tutorial.
>The good news is that Python functions are objects too, so you can pass >them as params to another function.
duh, duh, duh, duh, duh! I knew I was missing something there. Thanks.
>if not mini <= x <= maxi:
also thanks for this, I forgot about that. But I have it as
if not minr < guess < maxr:
because it's to DISALLOW the numbers to stay the same.
>That was just a suggestion, and it would have been pretty >interesting IMHO as a basis for a "from Q&D procedural scripting to OO >application programing" tutorial.
Bruno Desthuilliers wrote: > .... The generic version of your above code could be:
> def safeinput(prompt, convert): > while True: > x = input(prompt) > try: > x = convert(x) > except ValueError, e: > print("Bad input : %s" % e) > else: > return x
Or (I think more straightforwardly): def safeinput(prompt, convert): while True: text = input(prompt) try: return convert(text) except ValueError as e: print("Bad input ({0!r}): {1}".format(text, e))
> ...
> def yesno(s): > s = s.strip().lower() > if not s in ("y", "n"): > raise ValueError("please answer with 'y' or 'n'") > # we return a boolean > return s == 'y'
def yesno(s): s = s.strip().lower() if s in ("y", "n"): return s == 'y' # return a boolean raise ValueError("please answer with 'y' or 'n'")
> def int_in_range(x, mini, maxi): > x = int(x) > if not mini <= x <= maxi: > raise ValueError("%s is not in range (%s, %s)" % (x, mini, maxi)) > return x
def int_in_range(x, below, above): x = int(x) # may cause ValueError on its own if below < x < above: return x raise ValueError("{0} is not between {1} and {2}".format( x, mini, maxi))
These changes are mostly: (1) Negated tests are harder yo read (2) raise and return change the flow of control, so if ...: <raise or return> else: ... is "fat" (more trouble to read). (3) Adopting to the new 3.0 string formatting.
On Tue, 16 Dec 2008 13:59:24 -0800 Scott David Daniels <Scott.Dani...@Acm.Org> wrote:
>> > def yesno(s): > > s = s.strip().lower() > > if not s in ("y", "n"):
You could also do this to be a little more user friendly: if not (s and s[0] in ("y", "n")):
Or reverse the test for clarity.
-- D'Arcy J.M. Cain <da...@druid.net> | Democracy is three wolves http://www.druid.net/darcy/ | and a sheep voting on +1 416 425 1212 (DoD#0082) (eNTP) | what's for dinner.
On Dec 15, 1:29 pm, feba <feb...@gmail.com> wrote:
> 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.
Well, if you want to aim for several more rounds of refinement, how about having the game allow many players identified by name and have it keep records in a file: player name, number of times played, best score, best elapsed time for game completion - for each player. Can you do it in such a way that multiple people on different PC's can all play the game at the same time and not scribble over each other's scores? One approach would be to insist that the player's computers be able to share access to a particular directory, perhaps using a network mount of a disk from somewhere. A fancier approach would be to have a score keeping "service" that runs somewhere and each player's computer uses the network to interact with that score keeping service. Besides the game playing front end to the score keeping service, maybe you should have an administrative front end to the score keeping service to allow you to delete the names of deceased game players. (The only thing keeping him on his perch were tiny little nails through his feet).
Can just anyone play the game or does the administrator have to add their name as an authorized player to the score keeping service before they are allowed to play? Is it "Scout's honor" that palyers are who they say they are, or is there some kind of authentication for a player to "prove" their identify before they are allowed to play?
I remember many years ago back in graduate school, a friend implemented a clone of "pong" with record keeping. People would sit with that stupid game into the middle of the night striving to improve their standings in the best score display. May be more addictive with a "harder" game than this number guessing game but having your score being visible to other players can be powerful motivation to strive for better scores. If only my friend had figured out a way to have the little DEC GT40 demand quarters if the player wanted to play again, he'd have had most of his tuition nicely covered.
If your mind is ready to try something completely different, another thing you could strive to do is change the game from a "tty" interface to something more screen oriented. e.g. instead of having a prompt for "play again (y or n):" you'd have buttons on the screen that the player can click that say "quit" or "start new game". Note that the player might decide to click on either of those buttons at any time, not just after they have completed a game. Python has several many different libraries that would give you a basis for building such a "graphical user interface" (GUI) version of your game, but I'm not experienced enough to tell you which GUI package you should look into.
Not so much related to the business of making a niftier game, but another area you might want to look into is change management (e.g. "subversion") so you can track the evolution and refinement of your source code over time. The player-visible part of that is perhaps just to have something that announces which revision of the game they are running such that you can tell from that which revision of the source code to look at if you are tracking down a fix for a problem they have encountered and reported to you.