简体   繁体   中英

BANG! game not working properly

I'm new to Python. I built this awesome game called BANG! This game places you against the computer and gives you three options: to shoot, reload or put up your shield. The computer randomly selects one of these options for each turn. Then,the showdown takes place. For example if you shoot when the computer is reloading, the computer dies. Occasionally an extra life may be won.

However, the code isn't working properly. When I tell the computer to print something, under some circumstances it prints it, under other circumstances it doesn't. For example when I shoot with my only bullet, the computer doesn't print its actions. Moreover, sometimes the computer shoots with no bullets, causing its bullet level to drop to -1: I thought I had solved this problem by giving the computer different ranges to randomize with under different bullet conditions.

Could anyone please help me out and test the code for me?

import random

print "Welcome to the Wild West. You see a wild man come towards you. He has a loaded gun in his hand."

bullets = 0
bullets_comp = 3
lives = 1

for turn in range(200):
    if bullets_comp == 0:
        comp_move = random.randint(0,2)        
    elif bullets_comp > 0:
        comp_move = random.randint(0,3)  
    #0 will be reload, 1 wil be shield, 2 will be shoot

    life_chance = random.randint(0,6)
    if life_chance == 3:
        lives = lives + 1
        print "An angel descends randomly and gives you a life!"

    guess = raw_input('What do you choose to do: Reload, Shield, or Shoot?')

    if guess == 'reload' or guess == 'Reload':
        print 'You reload.'
        if comp_move == 0:
            bullets_comp = bullets_comp + 1
            print 'Your opponent reloads.'
        elif comp_move == 1:
            print 'Your opponent raises his shield.'
        elif comp_move == 2:
            if lives == 1:
                print 'Your opponent shoots...YOU DIE!'
                break
            if lives > 1:
                print 'Your opponent shoots...you lose a life.'
                lives = lives - 1


        bullets = bullets + 1

    elif guess == 'Shield' or guess == 'shield':
        print 'You protect yourself.'
        if comp_move == 0:
            bullets_comp = bullets_comp + 1
            print 'Your opponent reloads.'
        elif comp_move == 1:
            print 'Your opponent raises his shield.'
        elif comp_move == 2:
            print 'Your opponent shoots...but you are protected!'
            bullets_comp = bullets_comp - 1

    elif guess == 'Shoot' or guess == 'shoot':
        if bullets == 0:
            print 'You have no bullets!'
        elif bullets > 0:
            print 'You shoot.'
            if comp_move == 0:
                print 'Your opponent reloads.'
                print 'You kill your opponent! Congratulations!'
                break
            elif comp_move == 1:
                print '... but your opponent raises his shield.'
                bullets = bullets - 1
            elif comp_move == 2:
                print 'Your bullets meet each other halfway through the air and combust.'
                bullets_comp = bullets_comp - 1

        bullets = bullets - 1



    else:
        print "You can't do that mate!"

    print 'You have %d bullets and %d lives left' % (bullets, lives)
    print 'Your opponent has %d bullets' %  (bullets_comp)

    print """


    """

Updated for Python 3.3 and with a few issues fixed.

import random

print ("Welcome to the Wild West. You see a wild man come towards you. He has a loaded gun in his hand.")

bullets = 0
bullets_comp = 3
lives = 1
playing = True

It's neater to have a boolean variable that exits the loop. The previous game ended after 200 turns with no explanation. If you meant there to be a time limit it should be more explicit.

while playing:

Let's have the status report at the start as it's not obvious from the intro that your gun isn't loaded.

    print ("You have",bullets," bullets and ",lives," lives left")
    print ("Your opponent has ",bullets_comp," bullets")

Possibly a Python 3 change to how randInt works.

    if bullets_comp == 0:
        comp_move = random.randint(0,1)        
    elif bullets_comp > 0:
        comp_move = random.randint(0,2)  
    #0 will be reload, 1 wil be shield, 2 will be shoot

    life_chance = random.randint(0,6)
    if life_chance == 3:
        lives = lives + 1
        print ("An angel descends randomly and gives you a life!")

Use single letter codes for the moves - to avoid a clash on the letter S, we change Shoot to Fire. Storing our move in a variable lets us avoid heavy nesting.

    your_move = -1
    while your_move == -1:
        guess = input('What do you choose to do: (R)eload, (S)hield, or (F)ire?')
        if guess == 'r':
            your_move = 0
        elif guess == 's':
            your_move = 1
        elif guess == 'f':
            your_move = 2

Now report moves. Since shooting is the only move where your opponent's action matters, this way peaceful moves can be reported in just one place.

        if your_move == 0:
            print("You reload.")
            bullets = bullets + 1
        elif your_move == 1:
            print("You raise your shield.")
        else:
            assert your_move == 2
            if (bullets == 0):
                print("You fire your empty gun. D'oh.")
                your_move = 0

Note that we change your_move here so the player isn't considered shooting in the next step. The original allows the player to fire the empty gun and lose a turn as a result so I assume this was intended and that it shouldn't be caught at move validation.

            else:
                bullets = bullets - 1
                print("You fire.")

        if comp_move == 0:
            print("Your opponent reloads.")
            bullets_comp = bullets_comp + 1
        elif comp_move == 1:
            print("Your opponent raises his shield.")
        else:

Using else+assert instead of elif means that conditions that should never happen in non-buggy code will not be tested in release versions. Bear in mind that using an elif means that nothing at all will happen if an unexpected condition arises which will make debugging hard.

            assert comp_move == 2
            assert bullets_comp > 0
            bullets_comp = bullets_comp - 1
            print("Your opponent fires.")

Now, if somebody fired we do need to compare the two moves..

        if your_move == 2:
            if comp_move == 2:
                print("Your bullets meet each other in the air and explode.")
            elif comp_move == 1:
                print("Your bullet hits your opponent's shield.")
            else:
                assert comp_move == 0
                print("You kill your opponent! Congratulations!")
                playing = False
        elif comp_move == 2:
            if your_move == 1:
                print("Your opponent's bullet hits your shield.")
            else:
                assert your_move == 0
                print("Your opponent shoots you..",end="")
                if (lives>1):
                    print(".. you lose a life.")
                    lives = lives - 1
                else:
                    print(".. and you die.")
                    playing = False

Note that the game design could also be improved. At the moment the player can always win because the shield is 100% effective and eventually the angel gives an unlimited number of lives. This is why I thought the time limit might be deliberate.

  • If you reload and the computer shoots, you don't subtract from bullets_comp .
  • You subtract from bullets in multiple places. If you shoot and your opponent raises his shield it takes two bullets.
  • If you attempt to shoot with no bullets it takes away a bullet anyway.

I deleted the multiple subtraction of the var "bullet" and fixed the rest:

import random

print "Welcome to the Wild West. You see a wild man come towards you. He has a loaded gun in his hand."

bullets = 0
bullets_comp = 3
lives = 1

for turn in range(200):
    if bullets_comp == 0:
        comp_move = random.randint(0,1)    #edited to (0,1) becuase when it got 2 the computer shot with no bullets.
    elif bullets_comp > 0:
        comp_move = random.randint(0,2)  #edited to (0,2) - when you get 3, the computer doen't do anything.
    #0 will be reload, 1 wil be shield, 2 will be shoot

    life_chance = random.randint(0,6)
    if life_chance == 3:
        lives = lives + 1
        print "An angel descends randomly and gives you a life!"

    guess = raw_input('What do you choose to do: Reload, Shield, or Shoot?')
    if guess == 'reload' or guess == 'Reload':
        print 'You reload.'
        if comp_move == 0:
            bullets_comp = bullets_comp + 1
            print 'Your opponent reloads.'
        elif comp_move == 1:
            print 'Your opponent raises his shield.'
        elif comp_move == 2:
            if lives == 1:
                print 'Your opponent shoots...YOU DIE!'
                break
            if lives > 1:
                print 'Your opponent shoots...you lose a life.'
                lives = lives - 1
        bullets = bullets + 1

    elif guess == 'Shield' or guess == 'shield':
        print 'You protect yourself.'
        if comp_move == 0:
            bullets_comp = bullets_comp + 1
            print 'Your opponent reloads.'
        elif comp_move == 1:
            print 'Your opponent raises his shield.'
        elif comp_move == 2:
            print 'Your opponent shoots...but you are protected!'
            bullets_comp = bullets_comp - 1

    elif guess == 'Shoot' or guess == 'shoot':
        if bullets == 0:
            print 'You have no bullets!'
        elif bullets > 0:
            print 'You shoot.'
            if comp_move == 0:
                print 'Your opponent reloads.'
                print 'You kill your opponent! Congratulations!'
                break
            elif comp_move == 1:
                print '... but your opponent raises his shield.'
            elif comp_move == 2:
                print 'Your bullets meet each other halfway through the air and combust.'
                bullets_comp = bullets_comp - 1
            bullets = bullets - 1 #Edited - now the subtruction is in the "elif bullets > 0:" so you won't subtruct when you have no bullets 



    else:
        print "You can't do that mate!"

    print 'You have %d bullets and %d lives left' % (bullets, lives)
    print 'Your opponent has %d bullets' %  (bullets_comp)

    print """


    """

I refactored your code, I apologize if I went to far. In short, I would recommend keeping the bullet and lives manipulation to a very few parts. This will prevent bugs from double adding or subtracting. In my version of your code, I isolated the bullet manipulation to inside the Player class.

import cmd
import random
import logging
try:
    import pyreadline as readline
except ImportError:
    pass

SHIELD='shield'
RELOAD='reload'
SHOOT='shoot'


class OutOfLivesException(Exception):
    pass

class Player():

    def __init__(self, name, lives, bullets):
        self.name = name
        self.lives = lives
        self.bullets = bullets
        self.shield_enabled = False

    def reload(self):
        logging.info('%s reloads' % self.name)
        self.bullets = self.bullets + 1

    def has_bullets(self):
        return self.bullets > 0

    def shoot(self, opponent=None):
        if self.has_bullets():
            self.bullets = self.bullets - 1
            if opponent:
                logging.info('%s shoots %s' % (self.name,opponent.name))
                opponent.take_hit()
            else:
                logging.info('%s shoots' % self.name)
        else:
            logging.info('%s has no bullets' % self.name)

    def enable_shield(self):
        logging.info('%s raises shield' % self.name)
        self.shield_enabled = True

    def disable_shield(self):
        self.shield_enabled = False

    def take_hit(self):
        if self.shield_enabled:
            logging.info('%s is protected by shield' % self.name)
            return False

        self.lives = self.lives - 1
        logging.info('%s is hit' % self.name)
        if self.lives <= 0:
            logging.info('%s dies' % self.name)
            raise OutOfLivesException()
        return True

    def __str__(self):
        return '%s has %d bullets and %d lives left' % (self.name, self.bullets, self.lives)


class TurnManager:

    def __init__(self, computer, human):
        self.computer = computer
        self.human = human

    def next_turn(self, computer_action, human_action):
        self._process_pre_turn()
        result = self._process_turn(computer_action, human_action)
        self._process_post_turn()
        return result

    def _run_angel(self, player):
        life_chance = random.randint(0, 6)
        if life_chance == 3:
            player.lives = player.lives + 1
            logging.info(
                "An angel descends randomly and gives %s a life!" % player.name)

    def _display_state(self):
        logging.info(str(self.computer))
        logging.info(str(self.human))

    def _process_pre_turn(self):
        self._run_angel(self.human)

    def _process_turn(self, computer_action, human_action):
        # handle shields
        if(computer_action == SHIELD):
            self.computer.enable_shield()
        if(human_action == SHIELD):
            self.human.enable_shield()

        # handle reloads
        if(computer_action == RELOAD):
            self.computer.reload()
        if(human_action == RELOAD):
            self.human.reload()

        # handle shooting
        if human_action == SHOOT and human_action == computer_action and self.computer.has_bullets() and self.human.has_bullets():
            self.computer.shoot()
            self.human.shoot()
            logging.info(
                'Your bullets meet each other halfway through the air and combust.')
        else:
            if(computer_action == SHOOT):
                try:
                    self.computer.shoot(self.human)
                except OutOfLivesException:
                    return True  # returning true causes Cmd to stop looping
            if(human_action == SHOOT):
                try:
                    self.human.shoot(self.computer)
                except OutOfLivesException:
                    return True  # returning true causes Cmd to stop looping

        self._display_state()

    def _process_post_turn(self):
        # reset shields
        self.computer.disable_shield()
        self.human.disable_shield()


class Bang(cmd.Cmd):

    """Implementation of "Bang" using cmd.Cmd"""

    def emptyline(self):
        pass

    def do_EOF(self, line):
        return True

    def __init__(self, turn_manager):
        cmd.Cmd.__init__(self)

        self.intro = "Welcome to the Wild West. You see a wild man come towards you. He has a loaded gun in his hand."
        self.prompt = "What do you choose to do: Reload, Shield, or Shoot?"

        self.turn_manager = turn_manager

    def process_turn(self, human_action):
        computer_action = random.choice((RELOAD, SHIELD, SHOOT))
        return self.turn_manager.next_turn(computer_action, human_action)

    #
    # RELOAD
    #
    def do_reload(self, line):
        "Reload gun"
        return self.process_turn(RELOAD)

    #
    # SHIELD
    #
    def do_shield(self, line):
        "Cover with Shield"
        return self.process_turn(SHIELD)

    #
    # SHOOT
    #
    def do_shoot(self, line):
        "Shoot the opponent"
        return self.process_turn(SHOOT)


if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO,
                        format='\t%(message)s')

    computer = Player('Computer', 1, 3)
    human = Player('Human', 1, 0)

    turn_manager = TurnManager(computer, human)
    bang = Bang(turn_manager)
    bang.cmdloop()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM