Продолжаем серию работ по достижению реалистичного интеллекта в RPG играх. Мы уже научили NPC крафту, строительству и пользованию постройками. А теперь замахнёмся на торговлю между NPC, ведь настоящая колония дварфов немыслима без обмена предметами между её обитателями.

Заметим, что при статической цене предметов, почти нет никакого смысла торговать: если одна сторона окажется в плюсе, то другая, автоматически – в минусе. Другое дело, когда полезность (и стоимость предмета) оценивается динамически. Одному не хватает шкуры, другому копья – можно и обменятся. Следовательно, нам надо продумать по каким критериям NPC оценивают предметы. Кроме того, кажется логичным, что NPC оценивает вещи, которые ему предлагают и которые он отдаёт взамен, разными способами. Мы предлагаем следующие факторы динамического ценообразования:

Оценка чужих (покупаемых) предметов

Оценка своих (продаваемых) предметов

Желаемый предмет

Компонент желаемого предмета

Устраняет дефицит

Редкость материала

Убывающая полезность

Компонент желаемого предмета

Редкость

Совсем без стоимости обойтись тяжко, поэтому придётся ввести «базовую стоимость» для каждого предмета, которая будет корректироваться факторами из таблицы выше:

  • Желаемый предмет – тут всё просто, NPC может мечтать о какой-то вещи, её базовая стоимость умножается на коэффициент (выбран 2.0). В правой части таблицы этого пункта нет, NPC не продает то, чем сам хочет обладать.

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

  • Устраняет дефицит – это тема из предыдущего рассказа, про эмбеддинги дефицитов. За каждый устраняемый дефицит цена предмета увеличивается на 10%. Опять же, NPC не продает предметы, которые устраняют имеющийся у него дефицит.

  • Редкость материала – если медная руда валяется повсюду, то зачем её покупать? Предметы, ассоциированные с распространенными материалами, дешевеют, с редкими – дорожают. Для каждого предмета можно указать ассоциированный материал (или несколько) и корректирующий коэффициент. У NPC же хранит у себя редкость для каждого материала в виде чисел от -1 (редкий) до 1 (распространенный). Подразумевается, что эту редкость он получает, наблюдая окружающий мир. Поправка простая, среди всех ассоциированных материалов выбирается наиболее редкий, из стоимости вычитается редкость умноженная на корректирующий коэффициент. Хотя, лучше, наверное, использовать более крутую зависимость, нежели линейную.

  • Убывающая полезность - известный в экономике закон, что каждый последующий экземпляр одного предмета все менее ценен для нас. Его довольно легко понять интуитивно: первая кирка нам нужна, вторая – про запас, а дальше если только на продажу… Реализуется просто, стоимость следующего предмета ниже стоимости предыдущего на постоянную величину (декремент стоимости). Учитывается не только покупаемое количество, но и то, что уже есть в инвентаре. Желаемые предметы и их компоненты в необходимом количестве не снижают стоимость. Для своих предметов этот принцип не применяется.

Общий алгоритм торговли

Торговля начинается с того, что две стороны инициализируют «торговую сессию». Каждая сторона выделяет из своего инвентаря предметы, которыми готова торговать (tradable). В tradable НЕ попадают желаемые предметы, их компоненты (в необходимом количестве, избыток попадает), предметы, устраняющие дефицит, а также лучшее оружие и броня.

Одна из сторон назначается активной и формирует «запрос» - выбирает предметы из tradable другой стороны, которые хочет купить (ровно по тому же принципу, по которому не хочет продавать своё: желаемый предмет, его компоненты и т.д.).

Другая сторона получает «запрос» на свои предметы и оценивает их стоимость для себя, затем выбирает из tradable первой стороны предметы для компенсации этой стоимости. Сначала ранжирует их в следующем порядке: желаемый предмет, его компонент, дефицит, оружие/броня лучше имеющегося, ценности (тип предмета valuables). Затем набирает их, пока суммарная стоимость не превысит стоимость запроса и возвращает первой стороне «контрпредложение», где указаны предметы обеих сторон, подлежащих обмену. Важно, что «контрпредложение», по определению, устраивает ту сторону, которая его отправляет.

Ну а дальше алгоритм итерационный и одинаковый для обеих сторон. Тот, кто получил контрпредложение, проверяет устраивает ли оно его (стоимость своих предметов меньше стоимости чужих). Если да, то сделка состоялась, если нет – выкидывает часть своих предметов и формирует новое «контрпредложение» для противоположной стороны. Если не получается сформировать контрпредложение (приходится выкинуть всё), то торг отменяется.

Общий алгоритм торговли
Общий алгоритм торговли

Детали:

Чтобы определить выгодно предложение или нет, стоимость своих предметов умножается на параметр «жадность», стоимость чужих предметов должна превысить эту величину. Т.е. теперь жадность — это не какая-то абстрактная характеристика, а вполне понятный критерий.

Для быстрого понимания, является ли предмет компонентом желаемого предмета используется нейросетевое решение из прошлой работы (вычисляется эмбеддинг всех компонентов и сравнивается с эмбеддингом предмета). Это на самом деле не такая тривиальная задача, определить, например, что уголь нужен для создания кирки (через цепочку: уголь + руда -> металл -> наконечник -> кирка).

Все свойства предметов и рецепты теперь подтягиваются из *.xlsx файла, что позволяет редактировать крафтовую и торговую систему, не прикасаясь к коду.

Пример технологической схемы для создания кирки (подробнее, в прошлой статье).
Cиние стрелки – крафт, красные – результат работы зданий, серый пунктир – используемые здания.
Cиние стрелки – крафт, красные – результат работы зданий, серый пунктир – используемые здания.

Результат

Несколько примеров сделок между NPC. Сначала заполняем инвентарь случайными предметами, затем инициализируем торговлю. Желаемый предмет для каждого NPC выбирается случайно из некоторого множества (бронзовая кирка, бронзовый топор, костяное копье и т.д.). Параметр жадность задан 1.1 для каждой стороны. В таблице для каждого NPC в верхней строчке приведено то, чем он согласен торговать (tradable), а в нижней, то что он отдаст в результате торговли.

№ эксперимента

NPC 1

NPC 2

1

['copper_ore', 2], ['vein', 29], ['bone_spear', 1], ['coal', 15]

 ['bone_spear', 1]

[['small_leather', 19], ['coal', 8], ['flint_stone', 4], ['clay', 11], ['rope', 19]

['flint_stone', 4], ['clay', 11], ['small_leather', 15]

2

['big_leather', 8], ['vein', 15], ['tin_ore', 10], ['tin', 6], ['clay', 4]

['tin_ore', 2], ['clay', 4]

['copper_ore', 4], ['copper_coin', 11]

 ['copper_ore', 4]

3

['copper', 17], ['rope', 23], ['bones', 10], ['wooden_stick', 1], ['clay', 21]

 ['wooden_stick', 1], ['clay', 17]

['big_leather', 5], ['small_leather', 13], ['bone_spear', 1]

 [['big_leather', 3]

4

['copper', 23], ['flint_stone', 22], ['copper_coin', 1], ['small_leather', 3]

 ['small_leather', 2]

['wooden_chips', 19], ['tin', 11], ['big_leather', 5]

 ['big_leather', 1]

5

['clay', 50], ['tin_ore', 7]       

 ['clay', 17], ['tin_ore', 2]

['copper_ore', 42]

 ['copper_ore', 6]

6

['copper_ore', 2], ['ruby', 1], ['flint_stone', 1] 

['copper_ore', 2], ['flint_stone', 1]

['coal', 27]

 ['coal', 2]

Заключение

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

  • Асимметрия в оценке стоимости предметов позволяет NPC совершать взаимовыгодные обмены.

  • В примерах использована константная жадность. Но в принципе она может быть разной у разных NPC и даже динамической. Она может зависеть от отношений между персонажами. А может быть ей можно управлять – например, запугивать, заставляя отдавать предметы дешевле.

  • Схема торговли итерационная и её легко разносить на разные такты игры. Это снижает вычислительную нагрузку и добавляет правдоподобия. К тому же если в эту схему поставить человеческого игрока, он всё равно будет тормозить, обдумывая что же он хочет купить. Поэтому не надо требовать, чтобы все итерации торговли были выполнены за раз.

  • Следующая цель – общение между NPC.

Пишите в комментариях про игры, где реализована торговля между NPC и как она там сделана.

Комментарии (2)


  1. Shado_vi
    16.11.2025 03:48

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


  1. sSindiKk
    16.11.2025 03:48

    Спасибо за статью!

    Получается алгоритм по сути своей детерминированный и не подразумевает, что в той или иной ситуации NPC выберет другой предмет.

    Возможна вот такая ситуация?

    NPC нужен предмет A (денег у него на предмет хватает), но из соображений выгоды он берёт предметы B+C по эффективности, которые в сумме незначительно уступают предмету А, но стоят в разы дешевле.