Как определить мой пул действий таким образом, чтобы они зависели от переменных в GOAP?

Я пытаюсь внедрить GOAP в свою игру. Игра представляет собой игру типа симулятора, которая не только выиграет от GOAP, но и почти по существу является требованием, основанным на объеме того, что может делать агент.

У меня есть следующий код. Здесь нет планировщика, только определения действий, состояния и т. д.

#include <vector>
#include <string>
#include <unordered_map>

class Condition
{
public:

    bool operator()(const int value) const
    {
        return m_min <= value && value <= m_max;
    }

    enum class OP : std::uint8_t
    {
        EE, LT, GT, LE, GE
    };

    Condition(const int min, const int max, const OP op)
    :
        m_op(op),
        m_min(min),
        m_max(max)
    {}

    static Condition CreateEE(const int goal)
    {
        return Condition(goal, goal, OP::EE);
    }

    static Condition CreateGT(const int goal)
    {
        return Condition(goal+1, std::numeric_limits<int>::max(), OP::GT);
    }

    static Condition CreateLT(const int goal)
    {
        return Condition(std::numeric_limits<int>::min(), goal-1, OP::LT);
    }

    static Condition CreateGE(const int goal)
    {
        return Condition(goal, std::numeric_limits<int>::max(), OP::GT);
    }

    static Condition CreateLE(const int goal)
    {
        return Condition(std::numeric_limits<int>::min(), goal, OP::LT);
    }

private:

    OP m_op;
    int m_min;
    int m_max;
};

class Effect
{
public:

    enum class OP : std::uint8_t
    {
        ADD, SUB, MUL, DIV, EQ
    };

    Effect(OP op, int value)
    :
        m_op(op),
        m_value(value)
    {

    }

protected:
    OP m_op;
    int m_value;
};

class Action
{
public:
    void AddPrereq(const std::string& name, Condition value)
    {
        m_prereqs.emplace_back(name,value);
    }

    void AddEffect(const std::string& name, Effect effect)
    {
        m_effects.emplace_back(name,effect);
    }

    void SetCost(const float cost)
    {
        m_cost = cost;
    }

private:

    float m_cost;

    std::vector<std::pair<std::string,Condition>> m_prereqs;
    std::vector<std::pair<std::string,Effect>> m_effects;
};

class WorldState
{
public:

    void AddFact(const std::string& name, const int value)
    {
        m_facts[name] = value;
    }

private:

    std::unordered_map<std::string,int> m_facts;
};

class GoalState
{
public:

    void AddCondition(const std::string& name, const Condition condition)
    {
        m_conditions.emplace_back(name, condition);
    }

private:
    std::vector<std::pair<std::string,Condition>> m_conditions;
};

int main()
{
    Action goTo;
    goTo.AddEffect("isAtPosition", Effect(Effect::OP::EQ, 1));

    Action pickUp;
    pickUp.AddPrereq("isAtPosition", Condition::CreateEE(1));
    pickUp.AddEffect("itemInPossession", Effect(Effect::OP::EQ, 1));

    Action drop;
    drop.AddPrereq("itemInPossession", Condition::CreateEE(1));
    drop.AddEffect("itemAtPosition", Effect(Effect::OP::EQ, 1));

    WorldState ws;
    ws.AddFact("itemAtPosition", 0);

    GoalState gs;
    gs.AddCondition("itemAtPosition", Condition::CreateGE(1));

    return 0;
}

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

Действия будут такими:

  1. Гото (ставит агента на место)
  2. PickUp (передаёт предмет во владение)
  3. Гото (ставит агента на место)
  4. Бросить (кладет предмет на место) Цель достигнута!

Но как мне использовать здесь действие Goto дважды? Эффект такой же, установка переменной "isAtPosition". Нужно ли мне создавать новую переменную состояния для «isAtItemPosition» и «isAtDestinationPosition»?

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

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


person NeomerArcana    schedule 29.05.2020    source источник


Ответы (1)


Да, если у вас есть только логические значения, вам нужно отдельное действие для каждого местоположения.

Если вы можете, я бы рассмотрел возможность использования немного более мощного представления, такого как STRIPS или PDDL. Это значительно облегчит описание несколько более сложных сценариев, а также будет иметь дополнительное преимущество, заключающееся в том, что вы можете использовать готовые решатели для разработки планов для своих агентов.

В PDDL у вас может быть действие (move ?agent ?from ?to) с предварительным условием, что агент находится в местоположении ?from, и эффектом, что он будет в местоположении ?to после выполнения действия. Для подбора объекта у вас может быть (pick-up ?agent ?object) с предварительным условием, что и агент, и объект находятся в одном и том же месте. Это гораздо более общий подход, чем фиксированные логические действия, и упрощает последующие расширения.

person Oliver Mason    schedule 29.05.2020
comment
спасибо за ваш ответ, но на самом деле он не объясняет, как использовать переменную информацию в планировщике. PDDL выглядит как формализованная грамматика, я ничего не вижу о реализации. - person NeomerArcana; 30.05.2020
comment
@NeomerArcana Для реализации посмотрите любой современный планировщик. Одним из вариантов, с которым я сейчас работаю, является GraphPlan. - person Oliver Mason; 30.05.2020