Питон текстовый квест


Я создаю прототип для текста-приключение, чтобы попасть в разработку игр, и я хотел бы воспользоваться возможностью, чтобы также попытаться использовать концепции ООП, так как я борюсь, чтобы понять ООП.

Вот мой код до сих пор (предупреждение стремные имена):

class Character:
    def __init__(self, name, sex):
        self.name = name
        self.sex = sex
        self.inventory = {
            "albums": [],
            "instruments": []
        }
        self.coordinates = {
            'n': 0,
            'e': 0,
            's': 0,
            'w': 0
        }

    def get_inventory(self):
        print(self.inventory)

    def get_coordinates(self):
        print(self.coordinates)

    def str_coordinates(self):
        to_string = [str(v) for k, v in self.coordinates.items()]

        return ' '.join(to_string)

    def __str__(self):
        return  self.name

class Map:
    def __init__(self, *args):
        self.rooms = [*args]

    def get_room(self, room_coordinates):
        current_room = ''

        for room in self.rooms:
            if room.coordinates == room_coordinates:
                current_room = room

        return current_room

    def get_rooms(self):
        rooms_str = ''
        for room in self.rooms:
            rooms_str += room.coordinates + "\n"

        return rooms_str

class Room:
    def __init__(self, coordinates, name, actions, items, next_rooms):
        self.coordinates = coordinates
        self.name = name
        self.actions = actions
        self.items = items
        self.next_rooms = next_rooms

    def str_actions(self):
        return ', '.join(self.actions)

    def get_items_types(self):
        items_types = [item.item_type for item in self.items]
        return items_types

    def get_items_names(self):
        items_names = [item.name for item in self.items]
        return items_names

    def __str__(self):
        return self.name

class Item:
    def __init__(self, item_type, name):
        self.item_type = item_type
        self.name = name

class Album(Item):
    def __init__(self, item_type, name, lyrics):
        super().__init__(item_type, name)
        self.lyrics = lyrics

    def __str__(self):
        return self.item_type

ttng_animals = Album("Album", "Animals", 
                     "Let's talk about facts, the very best moment we have")

math_rock_3 = Album("Album", "Math rock 3", 
                    "Doesn't__init__ know the lyrics, play awesome guitar solo with tapping")

sort_of = Album("Album", "A test", 
                    "Doesn't__init__ know the lyrics, play awesome guitar solo with tapping")

standard_actions = ["look", "move", "coordinates", "inventory", "take"]

player = Character("Mabuelalelelejando", "male")

start = Room("0 0 0 0", "Start", standard_actions, [math_rock_3, ttng_animals, sort_of], ['n'])
room_2 = Room("1 0 0 0", "Room 2", standard_actions, [("instrument", "nya")] , ['s'])

game_map = Map(start, room_2)

def update_north():
    if player.coordinates['s'] > 0:
        player.coordinates['s'] -= 1
    else:
        player.coordinates['n'] += 1

def update_east():
    if player.coordinates['w'] > 0:
        player.coordinates['w'] -= 1
    else:
        player.coordinates['e'] += 1

def update_south():
    if player.coordinates['n'] > 0:
        player.coordinates['n'] -= 1
    else:
        player.coordinates['s'] += 1

def update_west():
    if player.coordinates['e'] > 0:
        player.coordinates['e'] -= 1
    else:
        player.coordinates['w'] += 1

def update_coordinates(movement):
    if movement.lower() == 'n':
        return update_north()
    if movement.lower() == 'e':
        return update_east()
    if movement.lower() == 's':
        return update_south()
    if movement.lower() == 'w':
        return update_west()

def ask_for_action():
    current_room = game_map.get_room(player.str_coordinates())
    print(current_room.str_actions())
    action_decision = input('').split()

    while action_decision[0] not in current_room.actions:
        print(current_room.str_actions())
        action_decision = input('').split()

    return action_decision

def move(*args):
    current_room = game_map.get_room(player.str_coordinates())
    directions = current_room.next_rooms

    print("You can move to: {}. A direction alsjeblieft:".format(', '.join(directions)))
    direction = input('')

    while direction.lower() not in directions:
        print("{}. A direction alsjeblieft:".format(directions))
        direction = input('')

    update_coordinates(direction)

def take(*args):
    current_room = game_map.get_room(player.str_coordinates())
    items_types = list(map(str.lower, current_room.get_items_types()))
    items_names = list(map(str.lower, current_room.get_items_names()))

    separated_items_names = []

    for item_name in items_names:
        for word in item_name.split():
            separated_items_names.append(word)

    if args:
        arg_in_name = []
        arg_not_in_name = set()
        for arg in args:
            if arg in separated_items_names:
                arg_in_name.append(arg)
            else:
                arg_not_in_name.add(arg)

        print(arg_in_name, arg_not_in_name)

        if len(arg_not_in_name) > 0 and len(arg_in_name) > 0:
            for i in range(0, len(items_names)):
                if arg_in_name[0] in items_names[i]:
                    item_index = i
            print("Do you want to take the {}?".format(items_names[item_index]))

        if len(arg_not_in_name) == 0 and len(arg_in_name) > 0:
            asked_items = set()
            for i in range(0, len(items_names)):
                for name in arg_in_name:
                    if name in items_names[i]:
                        asked_items.add(items_names[i])
                        item_index = i

            if len(asked_items) > 1:
                print("Do you want to take the {}?".format(items_names[item_index]))
            else:
                player.inventory["albums"].append(current_room.items[item_index])
                player.get_inventory()

actions = {
    "move": move,
    "take": take,
    "inventory": player.get_inventory,
    "coordinates": player.get_coordinates
}

def main():

    while True:
        action = ask_for_action()
        if action[0] in actions:
            args = action[1:]
            if len(args):
                actions[action[0]](*args)
            else:
                actions[action[0]]()
        else:
            print("Sorry please, alsjeblieft ):")

main()

Пожалуйста, дайте мне знать о любом случае вы думаете, что это может быть улучшено. Я не представил такие вещи, как пазлы, поэтому предметы не очень полезно. Я просто реализовал принимать функция.

Кроме того, любые предложения по улучшению дизайна ООП на эту игру, а также мое понимание в ООП будет очень признателен.



Комментарии
1 ответ

Давайте начнем с небольшого замечания по поводу вашего кода :)

Класс персонажа


  • get_invetory, get_coordinates - когда вы создаете класс и вы хотите иметь доступ к некоторым закрытым полям (можно сказать, что ваш инвентарь поле что-то вроде частного поля), затем популярный метод для создания функций, которые известны как геттер и сеттер. Типичные конвенции именования этой функции set_x/setX и get_x/getX. В ваш код, вы создали что-то вроде добытчика (ведь вы назвали это get_invetory), но это не обычный геттер (потому что возвращать нечего).
    Также есть еще одна вещь, в Python создание методов геттер/сеттер в классический способ не так популярен,
    поскольку классы в Python не имеют что-то вроде частного поля (может быть, это не совсем верно, но это не важно сейчас). С помощью поля объекта напрямую не проблема (в общих случаях). Так что, я думаю, что вы должны изменить get_invetory либо возвращать что-то или, может быть, попробовать переименовать эти методы.

класс карты


  • В конструкторе преобразования кортежа в список. Почему? Ваш код не нужен. Это не баг/ошибка, а просто плохой код запах.

  • get_room должна вернуть ни когда ничего не найти, ни пустой строкой.

класс номер


  • Это тоже не ошибка, но я думаю, что изменение get_items_types на один лайнер бы улучшить читабельность этого кода.

Теперь, давайте поговорим о том, как улучшить этот код, чтобы быть более ООП.

Сила объектно-ориентированное программирование-это не то, что вы можете собрать несколько переменных в одном объекте. Истинная сила заключается в том, что вы можете использовать полиморфизм. В коде есть не большое использование (ведь вы использовать наследование только один раз), так что вы не получите большого преимущества использования ООП в этой программе.

Но есть еще одна вещь.

Методы, которые изменяют состояние какого-то объекта должны быть частью этого объекта. Например, такие методы, как update_north должен быть внутри символьного класса (потому что это часть осуществления перемещения вашего персонажа). Это отличный пример того метода, который должен быть частью класса.
Вы должны сделать то же самое для take и move методы.
Поставив этот метод в класс символов, вы сможете использовать self синтаксис, который будет очень полезен, если вы хотите начать использование полиморфизма в коде. Также вы сможете создать более одного игрока и быть в состоянии перенести этого персонажа. Используя глобальную переменную как player это не хорошо и должно в первую очередь нужно исправить, если вы хотите сделать этот код более ООП.

Также:


  • Отсутствуют юнит-тесты ! ;) Это очень очень важная вещь! Хорошие блоки тестов позволит сэкономить ваше время, когда вы будете отладки или, что более важно, рефакторинг кода.

  • Начать использовать logging модуль, не print :)

  • Если я правильно понял код, вам не нужны 4 координаты. Вам нужно только X и y :)

  • В take способ слишком долго и слишком сложно, на мой взгляд. Попробуйте разделить этот метод на более мелкие методы для улучшения читабельности.

6
ответ дан 11 февраля 2018 в 06:02 Источник Поделиться