首页 > 解决方案 > 我收到 TypeError 因为 NoneType 对象不可下标。如何使其可下标?

问题描述

我正在尝试按照Phillip Johnson的《 Make Your Own Python Text Adventure》一书为基于文本的 RPG 编写代码。我无法确定错误在我的代码中的位置。我已经经历了好几次,似乎无法弄清楚。我试过摆弄不同的东西,但到目前为止它只会给出不同的错误。我将发布我的代码和 Traceback。

Traceback (most recent call last):
  File "C:\Users\Timothy Hall\Desktop\Yradel_Game\Yradel_Game5.py", line 99, in <module>
    main()
  File "C:\Users\Timothy Hall\Desktop\Yradel_Game\Yradel_Game5.py", line 31, in main
    player = Player()
  File "C:\Users\Timothy Hall\Desktop\Yradel_Game\player.py", line 11, in __init__
    self.x = world.start_tile_location[0]
TypeError: 'NoneType' object is not subscriptable

Yradel_Game5.py

# this program is a text based rpg

#import the OrderedDict
from collections import OrderedDict

# import the player module
from player import Player

# import the world module
import world

def main():
    # display Yradellian name examples
    print("Some examples of female Yradellian names include Nelsi, Drew, Casey, Ilya, etc.")
    print("Some examples of male Yradellian names include Toreth, Daren, Attel, Mayes, etc.")

    # get the character name from the user
    yesName = False
    while yesName == False:
        charName = input("What can I call you? ")
        nameInput = input("Your name is, " + charName + "? Type Yes or No: ")
        if nameInput == "Yes":
            yesName = True
        else:
            print("Sorry...")

    # call the parse world function
    world.parse_world_dsl()

    # create a player object
    player = Player()

    # display the welcome message
    print()
    player.say_hello(charName)

    # display current inventory
    print()
    print("Here's your starting game Inventory:")
    player.print_inventory()

    while True:
        # display the intro text for each tile
        print()
        room = world.tile_at(player.x, player.y)
        print(room.intro_text())

        # modify the player depending on the tile type
        room.modify_player(player)

        # get the action input from the user
        print()

        choose_action(room, player)
# create a funtion for available actions
def get_available_actions(room, player):
    actions = OrderedDict()
    print("Choose what to do...")

    if player.inventory:
        action_adder(actions, "i", player.print_inventory, "Print inventory")
    if isinstance(room, world.TraderTile):
        action_adder(actions, "t", player.trade, "Trade")
    if isinstance(room, world.EnemyTile) and room.enemy.is_alive():
        action_adder(actions, "a", player.attack, "Attack")
    else:
        if world.tile_at(room.x, room.y - 1):
            action_adder(actions, "n", player.move_north, "Go North!")
        if world.tile_at(room.x + 1, room.y):
            action_adder(actions, "e", player.move_east, "Go East!")
        if world.tile_at(room.x, room.y + 1):
            action_adder(actions, "s", player.move_south, "Go South!")
        if world.tile_at(room.x - 1, room.y):
            action_adder(actions, "w", player.move_west, "Go West!")
    if player.hp < 100:
        action_adder(actions, "h", player.heal, "Heal")

    return actions

# create the action adder function
def action_adder(action_dict, hotkey, action, name):
    action_dict[hotkey.lower()] = action
    action_dict[hotkey.upper()] = action
    print("{}: {}".format(hotkey, name))

# create a function to utilize the action dictionary
def choose_action(room, player):
    action = None
    while not action:
        available_actions = get_available_actions(room, player)
        action_input = input("Action: (Type a letter) ")
        action = available_actions.get(action_input)
        if action:
            action()
        else:
            print("That is invalid input.")

# call the main function
main()

世界.py

# import the enemies module
import enemies
# import the npc module
import npc
# import the random module
import random

# create a parent class for the map tiles
class MapTile:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def intro_text(self):
        raise NotImplementedError("Create a subclass instead!")
    def modify_player(self, player):
        pass

# create the tile subclasses
class StartTile(MapTile):
    def intro_text(self):
        return """You find yourself in a forest, sunlight trickling through the leaves overhead. Your feet crunch over the underbrush. You can see four paths through the trees.\n"""

class BoringTile(MapTile):
    def intro_text(self):
        return """The trees all look the same here...\n"""

class CityTile(MapTile):
    def intro_text(self):
        return """You made it out of the forest into a small town known as Burenburg. The people greet you warmly and you are filled with a sense of accomplishment.\n"""

class EnemyTile(MapTile):
    # have enemies randomly appear
    def __init__(self, x, y):
        r = random.random()
        if r < 0.50:
            self.enemy = enemies.Wolf()
            self.alive_text = "A lone Wolf approaches you baring its fangs."
            self.dead_text = "The Wolf keels over, dead before you."
        else:
            self.enemy = enemies.Goblin()
            self.alive_text = "A Goblin tries to steal your gold, you must defend yourself against his blade."
            self.dead_text = "The Goblin sticks its tongue out at you as it falls over dead."
        super().__init__(x, y)
    # display their alive/dead message
    def intro_text(self):
        text = self.alive_text if self.enemy.is_alive() else self.dead_text
        return text
    # have enemies attack the player
    def modify_player(self, player):
        if self.enemy.is_alive():
            player.hp = player.hp - self.enemy.damage
            print("Enemy does {} damage. You have {} HP remaining.".format(self.enemy.damage, player.hp))

class TraderTile(MapTile):
    def __init__(self, x, y):
        self.trader = npc.Trader()
        super().__init__(x, y)
    # create a method to trade between a buyer and seller
    def trade(self, buyer, seller):
        for i, item in enumerate(seller.inventory, 1):
            print("{}. {} - {} Gold".format(i, item.name, item.value))
        while True:
            user_input = input("Choose an item or press Q to exit: ")
            if user_input in ["Q", "q"]:
                return
            else:
                try:
                    choice = int(user_input)
                    to_swap = seller.inventory[choice - 1]
                    self.swap(seller, buyer, to_swap)
                except ValueError:
                    print("Invalid choice!")
    # create a method to swap items and gold between the buyer and seller
    def swap(self, seller, buyer, item):
        if item.value > buyer.gold:
            print("That's too expensive")
            return
        seller.inventory.remove(item)
        buyer.inventory.append(item)
        seller.gold = seller.gold + item.value
        buyer.gold = buyer.gold - item.value
        print("Trade complete!")
    # create a method to accept user input about who is the buyer and seller
    def check_if_trade(self, player):
        while True:
            print("Would you like to (B)uy, (S)ell or (Q)uit?")
            user_input = input()
            if user_input in ["Q", "q"]:
                return
            elif user_input in ["B", "b"]:
                print("Here's what's available to buy: ")
                self.trade(buyer = player, seller = self.trader)
            elif user_input in ["S", "s"]:
                print("Here's what's available to sell: ")
                self.trade(buyer = self.trader, seller = player)
            else:
                print("Invalid choice!")
    # create intro text for this room
    def intro_text(self):
        return """A man in a brown robe awaits you, willing to trade."""

class GoldTile(MapTile):
    def __init__(self, x, y):
        self.gold = random.randint(1, 50)
        self.gold_claimed = False
        super().__init__(x, y)

    def modify_player(self, player):
        if not self.gold_claimed:
            self.gold_claimed = True
            player.gold = player.gold + self.gold
            print("+{} Gold added.".format(self.gold))

    def intro_text(self):
        if self.gold_claimed:
            return """Another clearing in the forest with nothing in it but cute animals and underbrush."""
        else:
            return """Someone must've dropped some gold! You pick it up."""

# create the basic world map
world_dsl = """
| |E|C|E| |
|B|B| |B| |
|T| |G|E|B|
|G| |E| |G|
|E|B|S|T|B|
| | |E| |G|
| | |G|B|E|
"""

# create a function to validate the dsl
def is_dsl_valid(dsl):
    if dsl.count("|S|") != 1:
        return False
    if dsl.count("|C|") == 0:
        return False
    lines = dsl.splitlines()
    lines = [l for l in lines if l]
    pipe_counts = [line.count("|") for line in lines]
    for count in pipe_counts:
        if count != pipe_counts[0]:
            return False

    return True

# define a dictionary that maps dsl abreviations to tile types
tile_type_dict = {"C": CityTile,
                  "E": EnemyTile,
                  "S": StartTile,
                  "B": BoringTile,
                  "G": GoldTile,
                  "T": TraderTile,
                  " ": None}

world_map = []

start_tile_location = None

# create the function to parse the dsl
def parse_world_dsl():
    if not is_dsl_valid(world_dsl):
        raise SyntaxError("DSL is invalid!")

    dsl_lines = world_dsl.splitlines()
    dsl_lines = [x for x in dsl_lines if x]

    # iterate over each line in the dsl
    # instead of i, the variable y is used because we're working with an X-Y grid.
    for y, dsl_row in enumerate(dsl_lines):
        # create an object to store the tiles
        row = []
        # split the line into abbreviations using the "split" method
        dsl_cells = dsl_row.split("|")
        # the split method includes the beginning and end of the line
        # so we need to remove those nonexistent cells
        dsl_cells = [ c for c in dsl_cells if c]
        # iterate over each cell in the dsl line
        # instead of j, the variable x is used becuase we're working with an X-Y grid
        for x, dsl_cell in enumerate(dsl_cells):
            # look up the abbreviation in the dictionary
            tile_type = tile_type_dict[dsl_cell]
            # if the dictionary returned a valid type, create a new
            # tile object, pass it the X-Y coordinates as required
            # by the tile__init__(), and add it to the row object. If
            # None was found in the dictionary, we just add None.
            row.append(tile_type(x, y) if tile_type else None)

        # add the whole row to the world_map
        world_map.append(row)

# create a function that locates the tile at a specific coordinate
def tile_at(x, y):
    if x < 0 or y < 0:
        return None
    try:
        return world_map[y][x]
    except IndexError:
        return None

播放器.py

# import the items module
import items
# import the world module
import world

# create a class for Player
class Player:
    # define the inventory
    def __init__(self):
        self.inventory = [items.Dagger(), items.BreadRoll(), items.Potion()]
        self.x = world.start_tile_location[0]
        self.y = world.start_tile_location[1]
        self.hp = 100
        self.gold = 100
    # print the inventory and the best weapon
    def print_inventory(self):
        print("Inventory:")
        for item in self.inventory:
            print("* " + str(item))
        print("Gold: {}".format(self.gold))
        best_weapon = self.most_powerful_weapon()
        print("Your best weapon is your {}".format(best_weapon))
    # create a function to heal the player
    def heal(self):
        consumables = [item for item in self.inventory if isinstance(item, items.Consumable)]
        if not consumables:
            print("You don't have any items to heal you!")
            return
        for i, item in enumerate(consumables, 1):
            print("Choose an item to use to heal: ")
            print("{}. {}".format(i, item))
        valid = False
        while not valid:
            choice = input("")
            try:
                to_eat = consumables[int(choice) - 1]
                self.hp = min(100, self.hp + to_eat.healing_value)
                self.inventory.remove(to_eat)
                print("Current HP: {}".format(self.hp))
                valid = True
            except (ValueError, IndexError):
                print("Invalid choice, try again.")
    # welcome the user to the game
    def say_hello(self, name):
        print("Hello, " + name, ", welcome to the world of Yradel.")
    # create a function to determine the best weapon
    def most_powerful_weapon(self):
        max_damage = 0
        best_weapon = None
        for item in self.inventory:
            try:
                if item.damage > max_damage:
                    best_weapon = item
                    max_damage = item.damage
            except AttributeError:
                pass
        return best_weapon
    # create functions to let the player move
    def move(self, dx, dy):
        self.x += dx
        self.y += dy
    def move_north(self):
        self.move(dx = 0, dy = -1)
    def move_east(self):
        self.move(dx = 1, dy = 0)
    def move_south(self):
        self.move(dx = 0, dy = 1)
    def move_west(self):
        self.move(dx = -1, dy = 0)
    # create a function to attack the enemy
    def attack(self):
        best_weapon = self.most_powerful_weapon()
        room = world.tile_at(self.x, self.y)
        enemy = room.enemy
        print("You use {} against the {}!".format(best_weapon.name, enemy.name))
        enemy.hp -= best_weapon.damage
        if not enemy.is_alive():
            print("You killed {}!".format(enemy.name))
        else:
            print("{} HP is {}.".format(enemy.name, enemy.hp))
    # create a method to allow trade
    def trade(self):
        room = world.tile_at(self.x, self.y)
        room.check_if_trade(self)

标签: python

解决方案


您似乎忘记start_tile_location在此函数中进行设置:

start_tile_location = None

# create the function to parse the dsl
def parse_world_dsl():
    if not is_dsl_valid(world_dsl):
        raise SyntaxError("DSL is invalid!")

    dsl_lines = world_dsl.splitlines()
    dsl_lines = [x for x in dsl_lines if x]

    # iterate over each line in the dsl
    # instead of i, the variable y is used because we're working with an X-Y grid.
    for y, dsl_row in enumerate(dsl_lines):
        # create an object to store the tiles
        row = []
        # split the line into abbreviations using the "split" method
        dsl_cells = dsl_row.split("|")
        # the split method includes the beginning and end of the line
        # so we need to remove those nonexistent cells
        dsl_cells = [ c for c in dsl_cells if c]
        # iterate over each cell in the dsl line
        # instead of j, the variable x is used becuase we're working with an X-Y grid
        for x, dsl_cell in enumerate(dsl_cells):
            # look up the abbreviation in the dictionary
            tile_type = tile_type_dict[dsl_cell]
            # if the dictionary returned a valid type, create a new
            # tile object, pass it the X-Y coordinates as required
            # by the tile__init__(), and add it to the row object. If
            # None was found in the dictionary, we just add None.
            row.append(tile_type(x, y) if tile_type else None)

            if "S" == dsl_cell:
                start_tile_location = (x, y)

        # add the whole row to the world_map
        world_map.append(row)


推荐阅读