0

我正在尝试在我的游戏中实现 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. Goto(将代理置于适当位置)
  2. 拾取(将物品归入所有)
  3. Goto(将代理置于适当位置)
  4. 放下(将物品放在某个位置) 目标达成!

但是我如何使用Goto这里的动作两次呢?效果是一样的,设置“isAtPosition”变量。我需要为“isAtItemPosition”和“isAtDestinationPosition”创建一个新的状态变量吗?

因为那似乎我会为每个可能的目标有效地制定特定的行动,其效果是我确定了所有可能的行动序列。

如何对状态信息进行编码,以便池中的相同操作可以应用于不同阶段,产生不同的效果?(转到项目位置与转到目标位置)。

4

1 回答 1

0

是的,如果您只有布尔值,那么您需要对每个位置进行单独的操作。

如果可以的话,我会考虑使用更强大的表示,例如STRIPSPDDL。这将使描述更复杂的场景变得更加容易,并且您可以使用现成的求解器为您的代理制定计划,这将带来额外的好处。

在 PDDL 中,您可以有一个动作(move ?agent ?from ?to),前提是代理在位置,并且在执行动作后?from他们将在位置。?to为了拾取一个对象,您可以(pick-up ?agent ?object)使用代理和对象都在同一位置的预编码。这比固定的布尔操作更通用,并且允许以后的扩展更容易。

于 2020-05-29T16:30:40.727 回答