Как использовать словари для хранения нескольких функций с разными требованиями к аргументам

Я пытаюсь создать словарь, который выглядит так (для забавного проекта):

_cardLibrary = {
    
    
    1: {'name': 'Strike',
        'cost': 1,
        'type': 'attack',
        'rarity': 'common',
        'action': [deal_damage]
       },
    
    2: {'name': 'Defend',
        'cost': 1,
        'type': 'skill',
        'rarity': 'common',
        'action': [give_block]
       },
    
    3: {'name': 'Bash',
        'cost': 2,
        'type': 'attack',
        'rarity': 'common',
        'action': 
            [deal_damage,
             apply_status] 

       }

}

Я хотел бы создать функцию " use (i) ", которая берет конкретную карту и выполняет код, хранящийся внутри "action". Как вы можете видеть в третьем элементе, «действие» должно содержать несколько функций, каждая из которых требует определенных аргументов.

Например, Deal_damage требует 3 аргумента. Применить статус требует 4.

Deal_damage (заклинатель, цель, базовый урон) apply_status (заклинатель, цель, статус, интенсивность)

Есть ли способ создать функцию, которая вызывает все функции, хранящиеся внутри каждой конкретной карты?

Мне также нужен какой-то способ передать фактические аргументы каждой из функций... Некоторые из аргументов могут быть постоянными значениями. Например, для этой конкретной карты статус всегда равен 1, а интенсивность равна 1. Однако цель и заклинатель действительно должны быть определены в функции начального использования (заклинатель, цель)...

До сих пор я пытался перебрать каждую функцию, однако это оставляет меня с проблемой невозможности передать конкретные аргументы каждой функции...

Я также попробовал эту ссылку: Python: итерация функций с разными аргументами

Но по какой-то причине я не могу заставить код работать

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

Любые идеи о том, как я могу подойти к этому?

Спасибо вам всем

Откуда вы берете эти аргументы? То есть, если бы у вас была только одна функция в списке actions, как бы вы ее назвали? Все ли они имеют одни и те же переменные аргументы, и только постоянные аргументы меняются между функциями?

Pranav Hosangadi 17.02.2023 19:47

Когда очередь начинается, так сказать, вы сами определяете аргументы. Таким образом, если вы хотите нанести врагу урон, вы просто выполняете Deal_damage('self', 0, 10), чтобы нанести 10 единиц урона врагу с номером 0. Когда враг атакует, его формулы будут выглядеть как Deal_damage(0 ,'я',10) вместо этого. Если это действие должно было применить статус, аргументы статуса также должны быть определены... Карты должны попытаться сгруппировать действия. Таким образом, только определяя заклинателя и цель, другие аргументы уже будут определяться самой картой ... я ответил на ваш вопрос?

Lilac Wine 17.02.2023 20:09

Итак, если я правильно понимаю, у всех функций есть аргументы caster и target, но другие аргументы не совпадают между функциями? И эти другие аргументы остаются неизменными на протяжении всей игры?

Pranav Hosangadi 17.02.2023 20:11

Нет, они меняются. Они специфичны для каждой карты. возможно, я мог бы сохранить эти определенные аргументы в самой карте. Например, Bash должен применить 2 интенсивности статуса 1. Каждая другая карта будет чем-то похожа.

Lilac Wine 17.02.2023 20:13

«Итак, если вы хотите нанести урон врагу, вы просто выполните Deal_damage('self', 0, 10), чтобы нанести 10 единиц урона врагу с номером 0». Нет, это не вопрос. Предположим, что код выбирает карту 'Strike'. Как вы узнаете значения, которые должны быть переданы для вызова deal_damage? Когда вы используете карту 'Bash', я предполагаю, что caster и target будут одинаковыми и что они выбираются извне в зависимости от того, кто разыграл карту, верно? А вот урон, который будет нанесен цели - откуда он? Какой статус будет применяться, с какой интенсивностью?

Karl Knechtel 17.02.2023 21:47

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

Karl Knechtel 17.02.2023 21:48

Ты прав Карл, я строю систему как бы "на лету". Пока работает, но я все еще думаю об общей игровой петле. Вместо того, чтобы быть «карточной игрой», это должен быть ИИ, который выбирает лучшую игровую карту в любое время, учитывая вероятность того, что может произойти в следующие ходы. Чтобы полностью ответить на ваш вопрос. Конкретные аргументы функции нанесения урона действительно присущи каждой карте. Удар, всегда наносит 6 единиц урона. Bash всегда применяет к цели один и тот же статус и наносит 8 единиц урона. и т.д. То же самое для блочных карт

Lilac Wine 18.02.2023 20:31

Однако одна и та же функция Deal_damage() ведет себя по-разному в зависимости от цели и заклинателя. Например, если заклинатель имеет «+3 к силе», то атака нанесет цели урон +3. Если цель в данный момент находится под действием «уязвимости», она получит на 50% больше урона и так далее…

Lilac Wine 18.02.2023 20:32
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Веб-скрейпинг, как мы все знаем, это дисциплина, которая развивается с течением времени. Появляются все более сложные средства борьбы с ботами, а...
Библиотека для работы с мороженым
Библиотека для работы с мороженым
Лично я попрощался с операторами print() в python. Без шуток.
Эмиссия счетов-фактур с помощью Telegram - Python RPA (BotCity)
Эмиссия счетов-фактур с помощью Telegram - Python RPA (BotCity)
Привет, люди RPA, это снова я и я несу подарки! В очередном моем приключении о том, как создавать ботов для облегчения рутины. Вот, думаю, стоит...
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
Учебник по веб-скрапингу
Учебник по веб-скрапингу
Привет, ребята... В этот раз мы поговорим о веб-скрейпинге. Целью этого обсуждения будет узнать и понять, что такое веб-скрейпинг, а также узнать, как...
Тонкая настройка GPT-3 с помощью Anaconda
Тонкая настройка GPT-3 с помощью Anaconda
Зарегистрируйте аккаунт Open ai, а затем получите ключ API ниже.
2
8
59
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете использовать functools.partial, чтобы предварительно определить некоторые аргументы для каждой функции. Затем, когда вам нужно вызвать функции, вам нужно будет указать только аргументы caster и target:

from functools import partial

def deal_damage(caster, target, basedamage):
    print(f"{caster} dealt {basedamage} damage to {target}")

def give_block(caster, target, defense):
    print(f"{caster} blocked {defense} damage from {target}")

def apply_status(caster, target, status, intensity):
    print(f"{caster} applied {intensity}x{status} to {target}")


_cardLibrary = {
    "strike": {'name': 'Strike',
        'cost': 1,
        'type': 'attack',
        'rarity': 'common',
        'actions': [partial(deal_damage, basedamage=10)]
       },
    
    "defend": {'name': 'Defend',
        'cost': 1,
        'type': 'skill',
        'rarity': 'common',
        'actions': [partial(give_block, defense=5)]
       },
    
    "bash": {'name': 'Bash',
        'cost': 2,
        'type': 'attack',
        'rarity': 'common',
        'actions': 
            [partial(deal_damage, basedamage=5),
             partial(apply_status, status='stun', intensity=5)] 

       }
}

Обратите внимание, что я изменил ключи словаря на строки, а ключ 'action' в каждой карточке на 'actions', потому что на одной карточке может быть несколько действий.

Затем в игровом цикле вы можете вызывать эти partial объекты как обычную функцию:

while True:
    print(f"You have {len(_cardLibrary)} cards: ")
    print(", ".join(_cardLibrary))
    u_in = input("What would you like to do? (or quit) ").lower()
    if u_in == "quit":
        print("Goodbye")
        break
    
    try:
        card = _cardLibrary[u_in]
    except KeyError:
        print("Invalid choice. Please choose again")
        continue
    
    print(f"You played the {card['name']} card")
    for action in card['actions']:
        print("ACTION: ", end = "")
        action(caster = "You", target = "Enemy")

Это дает следующий результат:

You have 3 cards: 
strike, defend, bash

What would you like to do? (or quit) strike
You played the Strike card
ACTION: You dealt 10 damage to Enemy
You have 3 cards: 
strike, defend, bash

What would you like to do? (or quit) bash
You played the Bash card
ACTION: You dealt 5 damage to Enemy
ACTION: You applied 5xstun to Enemy
You have 3 cards: 
strike, defend, bash

What would you like to do? (or quit) defend
You played the Defend card
ACTION: You blocked 5 damage from Enemy
You have 3 cards: 
strike, defend, bash

What would you like to do? (or quit) quit
Goodbye

Я буду пробовать это прямо сейчас. Большое спасибо!! Кроме того, как вы пишете код так быстро!? Вы еле ответили за 5 минут. Большое спасибо!!!! :)

Lilac Wine 17.02.2023 20:40

Сделал именно это. Еще раз спасибо, приятель.

Lilac Wine 18.02.2023 20:36

Вы можете сделать так, чтобы все ваши списки параметров функций заканчивались ,**_, что позволит им получать (и игнорировать) параметры, которые они на самом деле не обрабатывают. Это позволит вам добавлять параметры с фиксированными значениями непосредственно в словарь _cardLibrary и использовать их в качестве параметров функций. Затем определите свою функцию use(), чтобы добавить свой собственный список именованных аргументов к фиксированным значениям словаря в качестве переопределения:

def deal_damage(caster,target,basedamage,**_):
    # ...


def use(card, **kwargs):
    for function in _cardLibrary[card]['action']:
        function(**{**_cardLibrary[card],**kwargs})

Применение:

use(1,caster = "orc",target = "human") 

# assuming _cardLibrary supplies the default value for "baseDamage" on card #1

Чувак, я действительно иногда чувствую себя таким нубом. Я даже не знал, что ты можешь это сделать. Это супер полезно. Большое спасибо!

Lilac Wine 18.02.2023 20:33

Другие вопросы по теме