class TestBag:
    def setup_method(self, method):
        self.bag = Bag()

    def teardown_method(self, method):
        self.bag = None

    def bag_n_dump(self, name):
        item = Item(name)
        self.bag.add(item)
        return self.bag.dump()

    def test_check_if_bag_object(self):
        assert type(self.bag) is Bag

    def test_check_if_item_object(self):
        assert Item in Bag.__bases__

    def test_check_if_name_description_can_be_assigned(self):
        self.bag.name = "The Bag"
        self.bag.description = "for carrying"
        assert self.bag.name == "The Bag"
        assert self.bag.description, "for carrying"

    def test_can_check_bag_to_see_if_empty(self):
        assert self.bag.is_empty()

    def test_can_return_item_count_when_empty(self):
        assert self.bag.item_count() == 0

    def test_after_adding_item_bag_is_no_longer_empty(self):
        item = Item("rock")
        self.bag.add(item)
        assert not self.bag.is_empty()

    def test_can_add_item_to_bag(self):
        assert self.bag.item_count() == 0
        item = Item("thing")
        self.bag.add(item)
        assert self.bag.item_count() == 1

    def test_dumped_pile_has_count(self):
        pile = self.bag_n_dump("fuzz")

        assert len(pile) == 1
        fuzz = pile["fuzz"]
        assert fuzz["count"] == 1

    def test_dumped_pile_has_name(self):
        pile = self.bag_n_dump("cheese")

        cheese = pile["cheese"]
        assert cheese["name"] == "cheese"

    def test_dumped_pile_has_item(self):
        pile = self.bag_n_dump("Amulet of Wendor")

        amulet = pile["Amulet of Wendor"]
        item = amulet["item"]
        assert type(item) is Item

    def test_can_look_in_bag(self):
        stick = Item("stick")
        self.bag.add(stick)
        seen = self.bag.look()
        assert "1 stick" in seen

    def test_can_fund_how_many_of_an_item_are_in_bag(self):
        planet = Item("planet")
        self.bag.add(planet)
        self.bag.add(planet)
        assert 2 == self.bag.how_many("planet")

    def test_how_many_handles_no_items_of_type(self):
        assert self.bag.how_many("jabberwocky") == 0

    def test_adding_multiple_of_the_same_item_increases_item_count(self):
        butter = Item("butter")
        self.bag.add(butter)
        self.bag.add(butter)
        assert 2 == self.bag.item_count()
        self.bag.add(butter)
        assert 3 == self.bag.item_count()

    def test_can_add_many(self):
        turtle = Item("turtle")
        self.bag.add_many(turtle, 5)
        assert 5 == self.bag.how_many("turtle")
        assert 5 == self.bag.item_count()

    def test_can_remove_an_item(self):
        self.bag.add(Item("cheezburger"))
        removed_count, item = self.bag.remove("cheezburger")
        assert 1 == removed_count
        assert "cheezburger" == item.name
        assert type(item) is Item
        assert 0 == self.bag.how_many("cheezburger")

    def test_cannot_remove_an_item_that_is_not_there(self):
        removed_count, item = self.bag.remove("Kaiser Soze")
        assert 0 == removed_count
        assert item == None
        assert 0 == self.bag.how_many("Kaiser Soze")

    def test_removed_items_behave_like_items_that_never_existed(self):
        self.bag.add(Item("smoke"))
        removed_count, item = self.bag.remove("smoke")
        assert 1 == removed_count

        removed_count, item = self.bag.remove("smoke")
        assert 0 == removed_count
        assert item == None
        assert 0 == self.bag.how_many("smoke")
        assert 0 == self.bag.item_count()

    def test_remove_many_items_from_bag_at_once(self):
        self.bag.add_many(Item("blind mouse"), 5)
        removed_count, item = self.bag.remove_many("blind mouse", 3)
        assert 3 == removed_count
        assert 2 == self.bag.how_many("blind mouse")
        assert "blind mouse" == item.name

    def test_remove_many_items_will_remove_only_as_many_as_exist(self):
        self.bag.add_many(Item("rhymes"), 5)
        removed_count, item = self.bag.remove_many("rhymes", 7)
        assert 5 == removed_count
        assert 0 == self.bag.how_many("rhymes")
        assert "rhymes" == item.name
        assert self.bag.isEmpty()

    def test_adding_multiple_of_the_same_item_increases_item_count(self):
        butter = Item("butter")
        self.bag.add(butter)
        self.bag.add(butter)
        assert 2 == self.bag.item_count()
        self.bag.add(butter)
        assert 3 == self.bag.item_count()

    def test_can_remove_an_item(self):
        self.bag.add(Item("cheezburger"))
        removed_count, item = self.bag.remove("cheezburger")
        assert 1 == removed_count
        assert "cheezburger" == item.name
        assert type(item) is Item
        assert 0 == self.bag.how_many("cheezburger")

    def test_cannot_remove_an_item_that_is_not_there(self):
        removed_count, item = self.bag.remove("Kaiser Soze")
        assert 0 == removed_count
        assert item == None
        assert 0 == self.bag.how_many("Kaiser Soze")

    def test_removed_items_behave_like_items_that_never_existed(self):
        self.bag.add(Item("smoke"))
        removed_count, item = self.bag.remove("smoke")
        assert 1 == removed_count

        removed_count, item = self.bag.remove("smoke")
        assert 0 == removed_count
        assert item == None
        assert 0 == self.bag.how_many("smoke")
        assert 0 == self.bag.item_count()

    def test_remove_many_items_from_bag_at_once(self):
        self.bag.add_many(Item("blind mouse"), 5)
        removed_count, item = self.bag.remove_many("blind mouse", 3)
        assert 3 == removed_count
        assert 2 == self.bag.how_many("blind mouse")
        assert "blind mouse" == item.name

    def test_remove_many_items_will_remove_only_as_many_as_exist(self):
        self.bag.add_many(Item("rhymes"), 5)
        removed_count, item = self.bag.remove_many("rhymes", 7)
        assert 5 == removed_count
        assert 0 == self.bag.how_many("rhymes")
        assert "rhymes" == item.name
Beispiel #2
0
class Engine:
    """This class ties it all together and might be viewed as something
    somewhat akin to a controller in an MVC framework.

    """
    def __init__(self, library_path, prompt_func=input, print_func=print):
        self.prompt_char = ">"
        self.library_path = library_path
        self.reset_game()
        self.interface = CommandLine(self, prompt_func, print_func)
        self.bag = Bag()
        self.level = None
        self.player = None
        self.player_in_room = None
        self.room = None

        for direction in ("north", "south", "east", "west"):
            while not self.add_direction_to_commands(direction):
                pass
                # The heart of what we want to do within this while loop is contained within
                # The add_direction_to_commands function.  We just want to keep calling it
                # until it returns True.

    def start(self):
        """Use this method to start the game"""
        player_name = self.interface.greet()
        self.player = Player(player_name)
        self.interface.display(initial_narration())
        self.init_level()
        self.interface.display(self.level.draw_map())

    def init_level(self):
        """Looks up the level information from file, loads it and inserts
        the player into the room.
        """
        self.room = LevelLoader(self.library_path, self.room_file)
        self.level = self.room.enter(self.player, "entrance")
        self.player_in_room = True
        self.interface.display(self.room.room_description())

    def reset_game(self):
        """Returns the game to its initial state"""
        self.room_file = "level_1.json"
        self.player_in_room = False

    def in_room(self):
        """Used to determine if the player is currently in a room"""
        if self.player == None:
            return False

        return self.player.in_room()

    def load_player(self, player):
        """Attribute setter for the player"""
        self.player = player

    def north(self):
        """Moves the player north if able"""
        if not self.level.can_go_north(self.player):
            self.interface.display("You cannot go north")
        else:
            for creature in self.level.get_move_ai():
                if creature.coords == (self.player.coords[0],
                                       self.player.coords[1] + 1):
                    self.attack(creature)
                    break
            else:
                self.player.travel("n")

    def south(self):
        """Moves the player south if able"""
        if not self.level.can_go_south(self.player):
            self.interface.display("You cannot go south")
        else:
            for creature in self.level.get_move_ai():
                if creature.coords == (self.player.coords[0],
                                       self.player.coords[1] - 1):
                    self.attack(creature)
                    break
            else:
                self.player.travel("s")

    def east(self):
        """Moves the player east if able"""
        if not self.level.can_go_east(self.player):
            self.interface.display("You cannot go east")
        else:
            for creature in self.level.get_move_ai():
                if creature.coords == (self.player.coords[0] + 1,
                                       self.player.coords[1]):
                    self.attack(creature)
                    break
            else:
                self.player.travel("e")

    def west(self):
        """Moves the player west if able"""
        if not self.level.can_go_west(self.player):
            self.interface.display("You cannot go west")
        else:
            for creature in self.level.get_move_ai():
                if creature.coords == (self.player.coords[0] - 1,
                                       self.player.coords[1]):
                    self.attack(creature)
                    break
            else:
                self.player.travel("w")

    def attack(self, enemy):
        dmg = self.player.weapon.damage
        self.interface.display("You attack the " + enemy.name + " for " +
                               str(dmg) + " damage!")
        response = enemy.take_damage(dmg)
        enemy.set_target(self.player)
        if response:
            self.interface.display(response)
            for index, item in enumerate(self.level.contents):
                if item is enemy:
                    self.level.remove(enemy.name)

    def exit(self):
        """Tests for exit conditions and exits the player if they are met
        The length of this method suggests that it is ripe for splitting
        these actions into separate methods
        """
        can_exit = self.level.exit(self.player)

        if can_exit:
            self.display_exit_message()

            if not self.enter_next_level():
                self.player_in_room = False
                self.interface.display_end_of_game()
        else:
            format_string = "Sorry, you are not at an exit of {0}."
            message = format_string.format(self.room.name)
            self.interface.display(message)
        return can_exit

    def display_exit_message(self):
        """Displays the level's exit text to the user if it exists"""
        if self.room.exit_text == None:
            self.interface.display("You have exited {0}".format(
                self.room.name))
        elif self.room.exit_text == "final" and "bacon" in self.bag.items:
            self.interface.display(final_narration_win())
        elif self.room.exit_text == "final" and "bacon" not in self.bag.items:
            self.interface.display(final_narration_lose())
        else:
            self.interface.display(self.room.exit_text)

    def enter_next_level(self):
        """Transports the player to the next level or returns False if
        there is no next level
        """
        next_level, has_next_level = self.room.enter_next_level(self.player)
        if has_next_level:
            self.level = next_level
            self.interface.display(self.room.room_description())
            return True

        return False

    def item_count(self):
        """Displays the player's inventory"""
        self.interface.display(self.bag.look())

    def coordinates(self):
        """Returns the x, y coordinates of the player"""
        coords = self.player.locate()
        message = "Your co-ordinates are: ({0},{1})".format(
            coords[0], coords[1])
        self.interface.display(message)

    def vaccum_key_and_gold(self):
        """Automagically picks up gold and keys"""
        if self.pick_up_item("key"):
            self.interface.display("You picked up the key!")
        if self.pick_up_item("gold"):
            self.interface.display("You picked up the gold!")
        if self.pick_up_item("bacon"):
            self.interface.display("You picked up the bacon!")

    def vaccum_weapons(self):
        """Swiftly picks up Excalibur"""
        if self.pick_up_item("excalibur"):
            self.interface.display("Behold! The most power ever felt!")

    def pick_up_item(self, item):
        """Allows the player to pick up and item by removing an item from the
        room and placing it in their bag
        """
        if self.level.get_by_name(item) != None:
            player_coord = self.player.locate()
            item_coord = self.level.get_by_name(item).locate()
            if player_coord == item_coord:
                self.bag.add(Item(item))
                self.level.remove(item)
                return True

        return False

    def display_help(self):
        """Displays the help menu"""
        self.interface.display_help(self.in_room())

    def invalid_command(self):
        """Displays a message that tells the user their command was invalid"""
        self.interface.display("Sorry that command is not valid.")
        self.interface.display(
            "Please type 'help' and press enter for a menu.")

    def main_loop(self):
        """This is the core game loop that cycles through the turns"""
        play = True

        self.start()
        while play:
            play = self.move_player()

            if self.in_room():
                self.vaccum_key_and_gold()
                self.vaccum_weapons()
                play &= self.move_creatures()
                if play:
                    self.interface.display(self.level.draw_map())
                    self.interface.display(self.player.show_health())

    def move_player(self):
        """Gets the command from the player and moves (or quits)"""
        command = self.interface.prompt(self.prompt_char).lower()
        possible_commands = self.interface.current_commands(self.in_room())

        if command == "q":
            return False
        else:
            if not self.execute_command(command, possible_commands):
                self.invalid_command()

        return True

    def execute_command(self, command, commands):
        """Executes the command if valid, returns false if invalid"""
        try:
            cmd_tuple = commands[command]
            cmd_tuple[0]()
            return True
        except KeyError:
            return False

    def move_creatures(self):
        """Moves the creatures in the room"""
        creatures = self.level.get_move_ai()

        for creature in creatures:
            target_tile = next_tile(creature.coords, creature.target)
            if target_tile == self.player.coords:
                dmg = creature.weapon.damage
                self.interface.display("You were attacked by the " +
                                       creature.name + " for " + str(dmg) +
                                       " damage!")
                response = self.player.take_damage(dmg)
                if response:
                    self.interface.display(response)
                    return False
            else:
                creature.move()
        return True

    def add_direction_to_commands(self, direction):
        if not hasattr(self, direction):
            self.interface.display("That is not a valid direction")
            return False

        response = self.interface.prompt(
            "Please choose a key to be your {} movement: ".format(direction))
        if response in self.interface.command_mapping:
            self.interface.display("That key is already in use to {}".format(
                self.interface.command_mapping[response][1]))
            return False

        self.interface.command_mapping[response] = (getattr(
            self, direction), "move {}".format(direction), False)
        return True
class TestBag:
    def setup_method(self, method):
        self.bag = Bag()

    def teardown_method(self, method):
        self.bag = None

    def bag_n_dump(self, name):
        item = Item(name)
        self.bag.add(item)
        return self.bag.dump()

    def test_check_if_bag_object(self):
        assert type(self.bag) is Bag

    def test_check_if_item_object(self):
        assert Item in Bag.__bases__

    def test_check_if_name_description_can_be_assigned(self):
        self.bag.name = "The Bag"
        self.bag.description = "for carrying"
        assert self.bag.name == "The Bag"
        assert self.bag.description, "for carrying"

    def test_can_check_bag_to_see_if_empty(self):
        assert self.bag.is_empty()

    def test_can_return_item_count_when_empty(self):
        assert self.bag.item_count() == 0

    def test_after_adding_item_bag_is_no_longer_empty(self):
        item = Item("rock")
        self.bag.add(item)
        assert not self.bag.is_empty()

    def test_can_add_item_to_bag(self):
        assert self.bag.item_count() == 0
        item = Item("thing")
        self.bag.add(item)
        assert self.bag.item_count() == 1

    def test_dumped_pile_has_count(self):
        pile = self.bag_n_dump('fuzz')

        assert len(pile) == 1
        fuzz = pile['fuzz']
        assert fuzz["count"] == 1

    def test_dumped_pile_has_name(self):
        pile = self.bag_n_dump('cheese')

        cheese = pile['cheese']
        assert cheese["name"] == "cheese"

    def test_dumped_pile_has_item(self):
        pile = self.bag_n_dump('Amulet of Wendor')

        amulet = pile['Amulet of Wendor']
        item = amulet['item']
        assert type(item) is Item

    def test_can_look_in_bag(self):
        stick = Item("stick")
        self.bag.add(stick)
        seen = self.bag.look()
        assert "1 stick" in seen

    def test_can_fund_how_many_of_an_item_are_in_bag(self):
        planet = Item("planet")
        self.bag.add(planet)
        self.bag.add(planet)
        assert 2 == self.bag.how_many("planet")

    def test_how_many_handles_no_items_of_type(self):
        assert self.bag.how_many("jabberwocky") == 0

    def test_adding_multiple_of_the_same_item_increases_item_count(self):
        butter = Item("butter")
        self.bag.add(butter)
        self.bag.add(butter)
        assert 2 == self.bag.item_count()
        self.bag.add(butter)
        assert 3 == self.bag.item_count()

    def test_can_add_many(self):
        turtle = Item("turtle")
        self.bag.add_many(turtle, 5)
        assert 5 == self.bag.how_many("turtle")
        assert 5 == self.bag.item_count()

    def test_can_remove_an_item(self):
        self.bag.add(Item("cheezburger"))
        removed_count, item = self.bag.remove("cheezburger")
        assert 1 == removed_count
        assert "cheezburger" == item.name
        assert type(item) is Item
        assert 0 == self.bag.how_many("cheezburger")

    def test_cannot_remove_an_item_that_is_not_there(self):
        removed_count, item = self.bag.remove("Kaiser Soze")
        assert 0 == removed_count
        assert item == None
        assert 0 == self.bag.how_many("Kaiser Soze")

    def test_removed_items_behave_like_items_that_never_existed(self):
        self.bag.add(Item("smoke"))
        removed_count, item = self.bag.remove("smoke")
        assert 1 == removed_count

        removed_count, item = self.bag.remove("smoke")
        assert 0 == removed_count
        assert item == None
        assert 0 == self.bag.how_many("smoke")
        assert 0 == self.bag.item_count()

    def test_remove_many_items_from_bag_at_once(self):
        self.bag.add_many(Item("blind mouse"), 5)
        removed_count, item = self.bag.remove_many("blind mouse", 3)
        assert 3 == removed_count
        assert 2 == self.bag.how_many("blind mouse")
        assert "blind mouse" == item.name

    def test_remove_many_items_will_remove_only_as_many_as_exist(self):
        self.bag.add_many(Item("rhymes"), 5)
        removed_count, item = self.bag.remove_many("rhymes", 7)
        assert 5 == removed_count
        assert 0 == self.bag.how_many("rhymes")
        assert "rhymes" == item.name
        assert self.bag.isEmpty()

    def test_adding_multiple_of_the_same_item_increases_item_count(self):
        butter = Item("butter")
        self.bag.add(butter)
        self.bag.add(butter)
        assert 2 == self.bag.item_count()
        self.bag.add(butter)
        assert 3 == self.bag.item_count()

    def test_can_remove_an_item(self):
        self.bag.add(Item("cheezburger"))
        removed_count, item = self.bag.remove("cheezburger")
        assert 1 == removed_count
        assert "cheezburger" == item.name
        assert type(item) is Item
        assert 0 == self.bag.how_many("cheezburger")

    def test_cannot_remove_an_item_that_is_not_there(self):
        removed_count, item = self.bag.remove("Kaiser Soze")
        assert 0 == removed_count
        assert item == None
        assert 0 == self.bag.how_many("Kaiser Soze")

    def test_removed_items_behave_like_items_that_never_existed(self):
        self.bag.add(Item("smoke"))
        removed_count, item = self.bag.remove("smoke")
        assert 1 == removed_count

        removed_count, item = self.bag.remove("smoke")
        assert 0 == removed_count
        assert item == None
        assert 0 == self.bag.how_many("smoke")
        assert 0 == self.bag.item_count()

    def test_remove_many_items_from_bag_at_once(self):
        self.bag.add_many(Item("blind mouse"), 5)
        removed_count, item = self.bag.remove_many("blind mouse", 3)
        assert 3 == removed_count
        assert 2 == self.bag.how_many("blind mouse")
        assert "blind mouse" == item.name

    def test_remove_many_items_will_remove_only_as_many_as_exist(self):
        self.bag.add_many(Item("rhymes"), 5)
        removed_count, item = self.bag.remove_many("rhymes", 7)
        assert 5 == removed_count
        assert 0 == self.bag.how_many("rhymes")
        assert "rhymes" == item.name
class Engine:
    """This class ties it all together and might be viewed as something
    somewhat akin to a controller in an MVC framework.

    """

    def __init__(self, library_path, prompt_func=input, print_func=print):
        self.prompt_char = ">"
        self.library_path = library_path
        self.reset_game()
        self.interface = CommandLine(self, prompt_func, print_func)
        self.bag = Bag()
        self.level = None
        self.player = None
        self.player_in_room = None
        self.room = None

        for direction in ("north", "south", "east", "west"):
            while not self.add_direction_to_commands(direction):
                pass
                # The heart of what we want to do within this while loop is contained within
                # The add_direction_to_commands function.  We just want to keep calling it
                # until it returns True.

    def start(self):
        """Use this method to start the game"""
        player_name = self.interface.greet()
        self.player = Player(player_name)
        self.interface.display(initial_narration())
        self.init_level()
        self.interface.display(self.level.draw_map())

    def init_level(self):
        """Looks up the level information from file, loads it and inserts
        the player into the room.
        """
        self.room = LevelLoader(self.library_path, self.room_file)
        self.level = self.room.enter(self.player, "entrance")
        self.player_in_room = True
        self.interface.display(self.room.room_description())

    def reset_game(self):
        """Returns the game to its initial state"""
        self.room_file = "level_1.json"
        self.player_in_room = False

    def in_room(self):
        """Used to determine if the player is currently in a room"""
        if self.player == None:
            return False

        return self.player.in_room()

    def load_player(self, player):
        """Attribute setter for the player"""
        self.player = player

    def north(self):
        """Moves the player north if able"""
        if not self.level.can_go_north(self.player):
            self.interface.display("You cannot go north")
        else:
            for creature in self.level.get_move_ai():
                if creature.coords == (self.player.coords[0], self.player.coords[1] + 1):
                    self.attack(creature)
                    break
            else:
                self.player.travel("n")

    def south(self):
        """Moves the player south if able"""
        if not self.level.can_go_south(self.player):
            self.interface.display("You cannot go south")
        else:
            for creature in self.level.get_move_ai():
                if creature.coords == (self.player.coords[0], self.player.coords[1] - 1):
                    self.attack(creature)
                    break
            else:
                self.player.travel("s")

    def east(self):
        """Moves the player east if able"""
        if not self.level.can_go_east(self.player):
            self.interface.display("You cannot go east")
        else:
            for creature in self.level.get_move_ai():
                if creature.coords == (self.player.coords[0] + 1, self.player.coords[1]):
                    self.attack(creature)
                    break
            else:
                self.player.travel("e")

    def west(self):
        """Moves the player west if able"""
        if not self.level.can_go_west(self.player):
            self.interface.display("You cannot go west")
        else:
            for creature in self.level.get_move_ai():
                if creature.coords == (self.player.coords[0] - 1, self.player.coords[1]):
                    self.attack(creature)
                    break
            else:
                self.player.travel("w")

    def attack(self, enemy):
        dmg = self.player.weapon.damage
        self.interface.display("You attack the " + enemy.name + " for " + str(dmg) + " damage!")
        response = enemy.take_damage(dmg)
        enemy.set_target(self.player)
        if response:
            self.interface.display(response)
            for index, item in enumerate(self.level.contents):
                if item is enemy:
                    self.level.remove(enemy.name)

    def exit(self):
        """Tests for exit conditions and exits the player if they are met
        The length of this method suggests that it is ripe for splitting
        these actions into separate methods
        """
        can_exit = self.level.exit(self.player)

        if can_exit:
            self.display_exit_message()

            if not self.enter_next_level():
                self.player_in_room = False
                self.interface.display_end_of_game()
        else:
            format_string = "Sorry, you are not at an exit of {0}."
            message = format_string.format(self.room.name)
            self.interface.display(message)
        return can_exit

    def display_exit_message(self):
        """Displays the level's exit text to the user if it exists"""
        if self.room.exit_text == None:
            self.interface.display("You have exited {0}".format(self.room.name))
        elif self.room.exit_text == "final" and "bacon" in self.bag.items:
            self.interface.display(final_narration_win())
        elif self.room.exit_text == "final" and "bacon" not in self.bag.items:
            self.interface.display(final_narration_lose())
        else:
            self.interface.display(self.room.exit_text)

    def enter_next_level(self):
        """Transports the player to the next level or returns False if
        there is no next level
        """
        next_level, has_next_level = self.room.enter_next_level(self.player)
        if has_next_level:
            self.level = next_level
            self.interface.display(self.room.room_description())
            return True

        return False

    def item_count(self):
        """Displays the player's inventory"""
        self.interface.display(self.bag.look())

    def coordinates(self):
        """Returns the x, y coordinates of the player"""
        coords = self.player.locate()
        message = "Your co-ordinates are: ({0},{1})".format(coords[0], coords[1])
        self.interface.display(message)

    def vaccum_key_and_gold(self):
        """Automagically picks up gold and keys"""
        if self.pick_up_item("key"):
            self.interface.display("You picked up the key!")
        if self.pick_up_item("gold"):
            self.interface.display("You picked up the gold!")
        if self.pick_up_item("bacon"):
            self.interface.display("You picked up the bacon!")

    def vaccum_weapons(self):
        """Swiftly picks up Excalibur"""
        if self.pick_up_item("excalibur"):
            self.interface.display("Behold! The most power ever felt!")

    def pick_up_item(self, item):
        """Allows the player to pick up and item by removing an item from the
        room and placing it in their bag
        """
        if self.level.get_by_name(item) != None:
            player_coord = self.player.locate()
            item_coord = self.level.get_by_name(item).locate()
            if player_coord == item_coord:
                self.bag.add(Item(item))
                self.level.remove(item)
                return True

        return False

    def display_help(self):
        """Displays the help menu"""
        self.interface.display_help(self.in_room())

    def invalid_command(self):
        """Displays a message that tells the user their command was invalid"""
        self.interface.display("Sorry that command is not valid.")
        self.interface.display("Please type 'help' and press enter for a menu.")

    def main_loop(self):
        """This is the core game loop that cycles through the turns"""
        play = True

        self.start()
        while play:
            play = self.move_player()

            if self.in_room():
                self.vaccum_key_and_gold()
                self.vaccum_weapons()
                play &= self.move_creatures()
                if play:
                    self.interface.display(self.level.draw_map())
                    self.interface.display(self.player.show_health())

    def move_player(self):
        """Gets the command from the player and moves (or quits)"""
        command = self.interface.prompt(self.prompt_char).lower()
        possible_commands = self.interface.current_commands(self.in_room())

        if command == "q":
            return False
        else:
            if not self.execute_command(command, possible_commands):
                self.invalid_command()

        return True

    def execute_command(self, command, commands):
        """Executes the command if valid, returns false if invalid"""
        try:
            cmd_tuple = commands[command]
            cmd_tuple[0]()
            return True
        except KeyError:
            return False

    def move_creatures(self):
        """Moves the creatures in the room"""
        creatures = self.level.get_move_ai()

        for creature in creatures:
            target_tile = next_tile(creature.coords, creature.target)
            if target_tile == self.player.coords:
                dmg = creature.weapon.damage
                self.interface.display("You were attacked by the " + creature.name + " for " + str(dmg) + " damage!")
                response = self.player.take_damage(dmg)
                if response:
                    self.interface.display(response)
                    return False
            else:
                creature.move()
        return True

    def add_direction_to_commands(self, direction):
        if not hasattr(self, direction):
            self.interface.display("That is not a valid direction")
            return False

        response = self.interface.prompt("Please choose a key to be your {} movement: ".format(direction))
        if response in self.interface.command_mapping:
            self.interface.display(
                "That key is already in use to {}".format(self.interface.command_mapping[response][1])
            )
            return False

        self.interface.command_mapping[response] = (getattr(self, direction), "move {}".format(direction), False)
        return True
class InventoryBagTest(unittest.TestCase):
    def setUp(self):
        self.bag = Bag()

    def tearDown(self):
        self.bag = None

    def test_can_use_bag_to_hold_items_that_are_found(self):
        # Inara is curious to know if she is carrying any items. She checks her bag to see what is in her inventory
        self.assertIsNotNone(self.bag)

        # Her inventory bag is empty and contains no items
        self.assertTrue(self.bag.is_empty())
        self.assertEqual(self.bag.item_count(), 0)

        # Inara sees a pile of rocks nearby and lacking any other items decides to put them into her bag. She puts one rock into her bag.  Looking into her bag, it is no longer empty, she has one rock in it.
        rock = Item("rock")
        self.bag.add(rock)
        self.assertFalse(self.bag.is_empty())
        self.assertEqual(self.bag.item_count(), 1)

        # note perhaps this should be checking for look() rather than dump()
        seen = self.bag.look()
        self.assertIn("1 rock", seen)

        # She puts two more rocks into her bag. Looking into her bag, she sees that it now contains 3 rocks
        self.bag.add(rock)
        another_rock = Item("rock")
        self.bag.add(another_rock)
        self.assertEqual(self.bag.item_count(), 3)

        seen = self.bag.look()
        self.assertIn("3 rock", seen)

        # Inara, happy to have something in her bag, starts on her adventure.  Before long she stumbles on something in a dark shadow.  Picking it up she sees that it is a shiny dagger.  After putting it into her bag she checks her bag to ensure that it is safe inside.  She now has 3 rocks and a dagger in her bag.
        dagger = Item("dagger")
        self.bag.add(dagger)

        seen = self.bag.look()
        self.assertIn("1 dagger", seen)

    def test_items_are_removed_from_bag(self):
        # Mary Poppins is summoned by the children to bring joy to their lives.  She is holding a bag.  It contains a hatrack, a carpet, two lollipops and 5 brooms.  It also contains medicine, a teaspoon and a few spoonfulls of sugar.
        self.bag.add(Item("hatrack"))
        self.bag.add(Item("carpet"))
        self.bag.add(Item("Thing 1"))
        self.bag.add(Item("Thing 2"))
        self.bag.add(Item("Cat"))
        self.bag.add(Item("Hat"))
        self.bag.add(Item("lollipop"))
        self.bag.add(Item("lollipop"))
        self.bag.add(Item("lollipop"))
        self.bag.add_many(Item("lollipop"), 5)
        self.bag.add(Item("medicine"))
        self.bag.add(Item("teaspoon"))
        self.bag.add_many(Item("spoonful of sugar"), 3)

        # Her bag contains at least 14 items.
        total_items = self.bag.item_count()
        self.assertTrue(total_items >= 14)

        # Mary enters the unhappy home with a messy room and tells the kids to tidy up.  The children don't want to tidy up so she starts searching through her bag.  She removes a hatrack but puts it back
        hatrack_count = self.bag.how_many("hatrack")
        self.bag.remove("hatrack")
        self.assertEqual(hatrack_count - 1, self.bag.how_many("hatrack"))
        self.bag.add(Item("hatrack"))
        self.assertEqual(hatrack_count, self.bag.how_many("hatrack"))

        # Mary then removes the medicine.
        med_count, medicine = self.bag.remove("medicine")
        self.assertEqual(med_count, 1)
        self.assertIsInstance(medicine, Item)
        self.assertEqual(medicine.name, "medicine")

        # She tries to remove two teaspoons but only has one. 
        # She removes the teaspoon
        teaspoon_count, teaspoon = self.bag.remove_many("teaspoon", 2)
        self.assertEqual(teaspoon_count, 1)
        self.assertEqual(self.bag.how_many("teaspoon"), 0)
        self.assertEqual(teaspoon.name, "teaspoon")

        # She removes one spoonful of sugar and begins to sing
        sugar_count, sugar = self.bag.remove("spoonful of sugar")
        self.assertEqual(1, sugar_count)

        # She removes another spoonful of sugar and continues to sing.
        sugar_count, sugar = self.bag.remove("spoonful of sugar")
        self.assertEqual(1, sugar_count)

        # She removes the third spoonful of sugar smiles to herself and takes her medicine. 
        sugar_count, sugar = self.bag.remove("spoonful of sugar")
        self.assertEqual(1, sugar_count)

        # Mary now has 5 less items in her bag
        new_total_items = self.bag.item_count()
        self.assertEqual(5, total_items - new_total_items)

        # She returns the medicine and spoon to her bag and with at least 12 items in her bag continues with her work
        self.bag.add(teaspoon)
        self.bag.add(medicine)
        self.assertTrue(self.bag.item_count() >= 12)

    def test_items_are_dumped_into_a_pile(self):

        # Items can be dumped into a pile and sorted through
        # This is a remnant of my inital version implementation of the InventoryBagAddAndLookTestCase which has since been converted to the look function.  Not sure if I should keep dump()
        # This may be useful in the future for saving the state of the bag
        rock = Item("rock")
        self.bag.add(rock)

        pile = self.bag.dump()
        item_list = pile.keys()
        self.assertEqual(len(item_list), 1)
        self.assertIn("rock", item_list)
        item = pile["rock"]
        self.assertEqual(item["name"], "rock")
        self.assertEqual(item["count"], 1)
Beispiel #6
0
class Engine:
    """This class ties it all together and might be viewed as something
    somewhat akin to a controller in an MVC framework.

    """
    def __init__(self, library_path, prompt_func=input, print_func=print):
        self.prompt_char = ">"
        self.library_path = library_path
        self.reset_game()
        self.interface = CommandLine(self, prompt_func, print_func)
        self.bag = Bag()
        self.level = None
        self.player = None
        self.player_in_room = None
        self.room = None

        for direction in ("north", "south", "east", "west"):
            while not self.add_direction_to_commands(direction):
                pass
                # The heart of what we want to do within this while loop is contained within
                # The add_direction_to_commands function.  We just want to keep calling it
                # until it returns True.

    def start(self):
        """Use this method to start the game"""
        player_name = self.interface.greet()
        self.player = Player(player_name)
        self.interface.display(initial_narration())
        self.init_level()
        self.interface.display(self.level.draw_map())

    def init_level(self):
        """Looks up the level information from file, loads it and inserts
        the player into the room.
        """
        self.room = LevelLoader(self.library_path, self.room_file)
        self.level = self.room.enter(self.player, "entrance")
        self.player_in_room = True
        self.interface.display(self.room.room_description())

    def reset_game(self):
        """Returns the game to its initial state"""
        self.room_file = "level_1.json"
        self.player_in_room = False

    def in_room(self):
        """Used to determine if the player is currently in a room"""
        if self.player == None:
            return False

        return self.player.in_room()

    def load_player(self, player):
        """Attribute setter for the player"""
        self.player = player

    def north(self):
        """Moves the player north if able"""
        if not self.level.can_go_north(self.player):
            self.interface.display("你不能向北走")
        else:
            for creature in self.level.get_move_ai():
                if creature.coords == (self.player.coords[0],
                                       self.player.coords[1] + 1):
                    self.attack(creature)
                    break
            else:
                self.player.travel("n")

    def south(self):
        """Moves the player south if able"""
        if not self.level.can_go_south(self.player):
            self.interface.display("你不能向北走")
        else:
            for creature in self.level.get_move_ai():
                if creature.coords == (self.player.coords[0],
                                       self.player.coords[1] - 1):
                    self.attack(creature)
                    break
            else:
                self.player.travel("s")

    def east(self):
        """Moves the player east if able"""
        if not self.level.can_go_east(self.player):
            self.interface.display("你不能向北东")
        else:
            for creature in self.level.get_move_ai():
                if creature.coords == (self.player.coords[0] + 1,
                                       self.player.coords[1]):
                    self.attack(creature)
                    break
            else:
                self.player.travel("e")

    def west(self):
        """Moves the player west if able"""
        if not self.level.can_go_west(self.player):
            self.interface.display("你不能向西走")
        else:
            for creature in self.level.get_move_ai():
                if creature.coords == (self.player.coords[0] - 1,
                                       self.player.coords[1]):
                    self.attack(creature)
                    break
            else:
                self.player.travel("w")

    def attack(self, enemy):
        if self.bag.how_many('spear') > 0:
            wep_dmg = Spear('1').dmg
            wep_dev = Spear('1').deviation
        elif self.bag.how_many('excalibur') > 0:
            wep_dmg = Excalibur('1').dmg
            wep_dev = Excalibur('1').deviation
        else:
            wep_dmg = 0
            wep_dev = 0
        dmg = self.player.weapon.damage + wep_dmg
        dev = self.player.deviation + wep_dev
        dmgdev = randint(dmg - dev, dmg + dev)
        self.interface.display("你攻击 " + enemy.name + " 并对它造成了 " + str(dmgdev) +
                               " 点伤害!")
        response = enemy.take_damage(dmg)
        enemy.set_target(self.player)
        if response:
            self.interface.display(response)
            for index, item in enumerate(self.level.contents):
                if item is enemy:
                    self.level.remove(enemy.name)

    def exit(self):
        """Tests for exit conditions and exits the player if they are met
        The length of this method suggests that it is ripe for splitting
        these actions into separate methods
        """
        can_exit = self.level.exit(self.player)
        if can_exit:
            how_many_key = self.bag.how_many('key')
            if int(how_many_key) > 0:
                self.display_exit_message()
                self.player.exit()
                self.bag.remove('key')
                if not self.enter_next_level():
                    self.player_in_room = False
                    self.interface.display_end_of_game()
            else:
                format_string = "对不起,你还没找到 {0} 的钥匙."
                message = format_string.format(self.room.name)
                self.interface.display(message)
        else:
            format_string = "对不起,你还没找到 {0} 的出口."
            message = format_string.format(self.room.name)
            self.interface.display(message)
        return can_exit

    def display_exit_message(self):
        """Displays the level's exit text to the user if it exists"""
        if self.room.exit_text == None:
            self.interface.display("You have exited {0}".format(
                self.room.name))
        elif self.room.exit_text == "final" and "bacon" in self.bag.items:
            self.interface.display(final_narration_win())
        elif self.room.exit_text == "final" and "bacon" not in self.bag.items:
            self.interface.display(final_narration_lose())
        else:
            self.interface.display(self.room.exit_text)

    def enter_next_level(self):
        """Transports the player to the next level or returns False if
        there is no next level
        """
        next_level, has_next_level = self.room.enter_next_level(self.player)
        if has_next_level:
            self.level = next_level
            self.interface.display(self.room.room_description())
            return True

        return False

    def item_count(self):
        """Displays the player's inventory"""
        self.interface.display(self.bag.look())

    def coordinates(self):
        """Returns the x, y coordinates of the player"""
        coords = self.player.locate()
        message = "你的坐标在: ({0},{1})".format(coords[0], coords[1])
        self.interface.display(message)

    def vaccum_key_and_gold(self):
        """Automagically picks up gold and keys"""
        if self.pick_up_item("key"):
            self.interface.display("你捡起了钥匙!")
        if self.pick_up_item("gold"):
            self.interface.display("你捡起了黄金!")
        if self.pick_up_item("food"):
            self.interface.display("你吃了些食物,感觉恢复了一些体力(增加20点生命值)!")
        if self.pick_up_item("yao"):
            self.interface.display("你吃了些药,最大生命值增加20,赶紧去吃点食物补补身体吧!")

    def vaccum_weapons(self):
        """Swiftly picks up Excalibur"""
        if self.pick_up_item("excalibur"):
            self.interface.display("你拿起了神剑,感觉全身充满了力量!")
        if self.pick_up_item("spear"):
            self.interface.display("你拿起了神枪,感觉全身充满了力量!")

    def pick_up_item(self, item):
        """Allows the player to pick up and item by removing an item from the
        room and placing it in their bag
        """
        if self.level.get_by_name(item) != None:
            player_coord = self.player.locate()
            item_coord = self.level.get_by_name(item).locate()
            if player_coord == item_coord:

                if item == "food":
                    self.player.take_damage(-50)
                    if self.player.calc_health() > self.player.maxph:
                        self.player.take_damage(self.player.calc_health() -
                                                self.player.maxph)
                elif item == "yao":
                    self.player.maxph += 20
                else:
                    self.bag.add(Item(item))
                self.level.remove(item)
                return True

        return False

    def display_help(self):
        """Displays the help menu"""
        self.interface.display_help(self.in_room())

    def invalid_command(self):
        """Displays a message that tells the user their command was invalid"""
        self.interface.display("命令不可用")
        self.interface.display("输入\"help\"可以查询命令.")

    def main_loop(self):
        """This is the core game loop that cycles through the turns"""
        play = True

        self.start()
        while play:
            play = self.move_player()

            if self.in_room():
                self.vaccum_key_and_gold()
                self.vaccum_weapons()
                play &= self.move_creatures()
                if play:
                    self.interface.display(self.level.draw_map())
                    self.interface.display(self.player.show_health())

    def move_player(self):
        """Gets the command from the player and moves (or quits)"""
        command = self.interface.prompt(self.prompt_char).lower()
        possible_commands = self.interface.current_commands(self.in_room())

        if command == "q":
            return False
        else:
            if not self.execute_command(command, possible_commands):
                self.invalid_command()

        return True

    def execute_command(self, command, commands):
        """Executes the command if valid, returns false if invalid"""
        try:
            cmd_tuple = commands[command]
            cmd_tuple[0]()
            return True
        except KeyError:
            return False

    def move_creatures(self):
        """Moves the creatures in the room"""
        creatures = self.level.get_move_ai()
        for creature in creatures:
            target_tile = next_tile(creature.coords, creature.target)
            if target_tile == self.player.coords:
                dmg = creature.weapon.damage
                dev = creature.deviation
                dmgdev = randint(dmg - dev, dmg + dev)
                self.interface.display("你被 " + creature.name + " 攻击,受到了 " +
                                       str(dmgdev) + " 点伤害!")
                response = self.player.take_damage(dmg)
                if response:
                    self.interface.display(response)
                    return False
            else:
                creature.move()
        return True

    def add_direction_to_commands(self, direction):
        if not hasattr(self, direction):
            self.interface.display("这不是一个正确的方向")
            return False

        #response = self.interface.prompt("请输入一个键作为你向 {} 移动的控制键: ".format(self.translate_direction(direction)))
        response = self.get_direction_key(direction)
        if response in self.interface.command_mapping:
            self.interface.display("这个键已经被用在 {} 上了".format(
                self.interface.command_mapping[response][1]))
            return False

        self.interface.command_mapping[response] = (getattr(
            self,
            direction), "向 {} 移动".format(self.translate_direction(direction)),
                                                    False)
        return True

    def translate_direction(self, direction):
        return {
            'north': '北(north)',
            'south': '南(south)',
            'east': '东(east)',
            'west': '西(west)',
        }.get(direction, direction)

    def get_direction_key(self, direction):
        return {
            'north': 'n',
            'south': 's',
            'east': 'e',
            'west': 'w',
        }.get(direction, direction)