Help with a simple python game

Started by
4 comments, last by Zahlman 18 years, 11 months ago
The game is called poison. The idea is that there are 21 tiles, and players take turns drawing 1, 2, or 3 tiles until there is only one left. The person who draws that last tile loses. Naturally the strategy is to go second, and always make sure the total number of tiles drwn is a multiple of 4. (So if your opponent draws 3, you draw 1, they draw 2, you draw 2, etc.) But that doesn't really matter. Here is my source code which seems to be on the brink of completion, I just have semi-minor bugs that need fixing. EDIT: Oh dear, it seems to have removed my tabs. It shows them here in the edit post screen. If someone figures out how to fix this please respond. Right now I need to go to bed.

print """There are 21 tiles. One of them is the poison tile. The first player will draw  1, 2, or 3 tiles. 
     The second player will then do the same. This will continue   until there is one tile left. 
     The person whose turn it is when this occurs will be poisoned. They will also lose."""

# allows playing more than one game
play_again = "y"
while play_again is "y":
    tiles = 21
    # keep track of which player it is
    player = 1

    def turn(tiles,player):
        print ""
        print "Player", player, "'s turn:"
        take = input("How many tiles would you like to take? ")
        if take == 1 or 2 or 3:
            tiles = tiles - take
            if tiles == 1:
                print "Congratulations, player", player, "you won!"
                valid = 2
            elif tiles < 1:
                print "Either there aren't that many tiles left, or you don't need to take the last tile."
            else:
                if player == 1:
                    player = player + 1
                else:
                    player = player - 1
                print "There are now", tiles, "tiles left."
                return player
                return tiles
        else:
            print "You must take either 1, 2, or 3 tiles. Try again."

    # make sure take is valid
    valid = 1
    while valid == 1:
        turn(tiles,player)
    play_again = raw_input("Would you like to play another game? (y/n): ")
print ""
print "Pressing Control (Ctrl), Z will close the program."


[Edit: Use the [source lang="cpp"] tag with the lang="python" attribute. - Oluseyi] The problems I have identified are: The obvious one, is for some reason sets tiles back to 21 every time. Also, if you enter a number other than 1, 2, or 3, it doesn't tell you that it isn't allowed. I wanted that to get caught in the 'if take == 1 or 2 or 3' bit, but it didn't and I don't know why. After this I plan to make some sort of AI for it, and eventually make a graphical version for it, but for now I just need help with this. Also, if there is a better way to approach this, that would be nice too. It seems a little longish for such a simple game, and this is a learning experience after all. Thank you, -Gavin Bauer [Edited by - Oluseyi on April 24, 2005 9:22:54 PM]
Advertisement
Quote:for some reason sets tiles back to 21 every time
Well, your structure is borked. You're defining your function within the while loop body, which means it gets recreated - along with all the variable initializations. That's why tiles gets set back to 21 each time.

def turn(tiles, player):  ...def main():  player = 1  play_again = "y"  tiles = 21  while play_again="y"    ...if __name__ is '__main__':  main()


Scoping is critical.

Quote:Also, if you enter a number other than 1, 2, or 3, it doesn't tell you that it isn't allowed. I wanted that to get caught in the 'if take == 1 or 2 or 3' bit, but it didn't and I don't know why.
if take == 1 or 2 or 3 doesn't do what you think it does. Integer values (among others) can be evaluated in a Boolean context: non-zero values are True. That means your statement evaluates as the following:

if take == 1 or True or True

Clearly, that's not what you want. What you want is:

if take == 1 or take == 2 or take == 3.
Quote:The obvious one, is for some reason sets tiles back to 21 every time.


Python integers are immutable. Changing them creates a new variable, it does not modify the old value. So essentially, they cannot be passed reference.

That problem also exists with the player variable and the valid variable.

Solution: either modify them as a global variable (see the global keyword), a member variable, or return the player from the function.

return playerreturn tiles


And one return is actually enough (return isn't like yield)

Quote:Also, if you enter a number other than 1, 2, or 3, it doesn't tell you that it isn't allowed. I wanted that to get caught in the 'if take == 1 or 2 or 3' bit, but it didn't and I don't know why.


if take == 1 or 2 or 3:

Is interpreted as if (take==1) or 2 or 3, which will be True if take==1 and 2 (which is also "true"), otherwise.

You really want if 1 <= take <= 3 or if take in (1,2,3).

def turn(tiles,player):    print "Player %s's turn:" % player    print "There are %s tiles left.\n" % tiles    take = raw_input("How many tiles would you like to take? ")    try:        take = int(take)    except:        take = None    print        if take not in (1,2,3):        print "You must take either 1, 2, or 3 tiles. Try again.\n"        return (tiles, player, True)    if take > tiles:        print "There aren't that many tiles left.\n"           return (tiles, player, True)        if take == tiles:        print "you don't need to take the last tile.\n"        return (tiles, player, True)    tiles -= take    if tiles == 1:        print "There is only one tile left."        print "Congratulations, player %s you won!\n" % player        return (None, None, False)    return (tiles, 3-player, True)       def main():    print """There are 21 tiles. One of them is the poison tile.       The first player will draw  1, 2, or 3 tiles.        The second player will then do the same.       This will continue   until there is one tile left.     The person whose turn it is when this occurs will be poisoned.        They will also lose.    """    # allows playing more than one game    play_again = "y"    while play_again is "y":        tiles, player, running = 21, 1, True        while running:            tiles, player, running = turn(tiles, player)           play_again = raw_input("Would you like to play another game? (y/n): ")    print "Game over."if __name__ == "__main__":    main()


Or, using a Game class.

class Game(object):    def __init__(self):        self.player = 1        self.tiles = 21        self.running = None    def run(self):        self.running = True        while self.running:            self.turn()    def turn(self):        print "Player %s's turn:" % self.player        print "There are %s tiles left.\n" % self.tiles        take = raw_input("How many tiles would you like to take? ")        try:            take = int(take)        except:            take = None        print            if take not in (1,2,3):            print "You must take either 1, 2, or 3 tiles. Try again.\n"                  elif take > self.tiles:            print "There aren't that many tiles left.\n"                           elif take == self.tiles:            print "You don't need to take the last tile.\n"        elif take == self.tiles-1:            print "There is only one tile left."            print "Congratulations, player %s you won!\n" % self.player            self.running = False        else:                       self.tiles -= take            self.player = 3-self.playerdef main():    print """There are 21 tiles. One of them is the poison tile.       The first player will draw  1, 2, or 3 tiles.        The second player will then do the same.       This will continue   until there is one tile left.     The person whose turn it is when this occurs will be poisoned.    They will also lose.    """    play_again = "y"    while play_again is "y":        game = Game()        game.run()            play_again = raw_input("Would you like to play another game? (y/n): ")    print "Game over."    if __name__ == "__main__":    main()
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
I haven't looked at this thread all week, I've been busy. I got help from a guy on the python tutoring service, who helped me considerably. Before I'd even looked at any of the posts on this forum, I had this:

def turn(tiles,player):    print ""    print "Player", player, "'s turn:"    take = input("How many tiles would you like to take? ")    if take in (1,2,3):        tiles = tiles - take        if tiles == 1:            print "Congratulations, player", player, "you won!"            valid = 2        elif tiles == 0:            print "Suicide is a crime!"        elif tiles < 0:            print "You can't take more tiles than are left!"        else:            player = (player % 2) + 1            print "There are now", tiles, "tiles left."    else:        print "You must take either 1, 2, or 3 tiles. Try again."    return (tiles, player)print '''There are 21 tiles. One of them is the poison tile. The first player will draw1, 2, or 3 tiles. The second player will then do the same. This will continueuntil there is one tile left. The person whose turn it is when this occurs willbe poisoned. They will also lose.'''# allows playing more than one gameplay_again = "y"while play_again is "y":    tiles = 21    # keep track of which player it is    player = 1    # make sure take is valid    valid = 1    while valid == 1:        tiles, player = turn(tiles,player)    play_again = raw_input("Would you like to play another game? (y/n): ")print ""print "Pressing Control (Ctrl), Z will close the program."


By the way, Fruny's 'if 1 <= take <= 3' doesn't work because that would allow someone to take, say, 1.5 tiles, which is not allowed. Other than that, excellent advise, I'm still quite new to python and programming in general, so I recognise now that my original code was quite scrambled.

[Edited by - gsail on April 28, 2005 4:35:14 PM]
Quote:By the way, Fruny's 'if 1 <= take <= 3' doesn't work because that would allow someone to take, say, 1.5 tiles, which is not allowed.


Ah, but note how I converted the input string to an int. [smile] The problem with input as opposed to raw_input is that you could type, say, file("helloworld","w") and it would accept it, creating a file on your disk. Then in the next input you could write to it, etc...
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Looking good. You'll probably want to fix it so that it will check the amount of remaining tiles before taking away from them, so that you don't leave the game in an invalid state after the turn() function. Oh, and take Fruny's advice seriously about input() vs raw_input() - the former is really intended for developing tools or interpreter shells etc., not games.

This topic is closed to new replies.

Advertisement