12

私は現在、c++ で書かれたゲームの AI を書いています。AI は概念的にはかなり単純で、意思決定ツリーを実行して適切なアクションを選択するだけです。以前は意思決定エンジンにプロローグを使用していましたが、他の開発者が c++ を使用しており、プロローグ コードの統合に関するいくつかの問題があるため、現在は c++ に移植しようとしています。

現在、プロローグ (100 以上) には多くの事実とルールがあります。if game_state then do action xyz という形で多くのことを表現します。ほとんどのルールは非常に単純ですが、いくつかはかなり複雑です。私は有限状態マシンのアプローチを検討しましたが、それはより大きな状況にうまく対応できていないようでした。これを C++ でコーディングする最初の試みは、if then else case ステートメントの巨大な悪夢でした。この種のコードがどこにでも現れました:

    if( this->current_game_state->some_condition == true ){
        if( this->current_game_state->some_other_condition == false ){      
                //some code
        }else{
            return do_default_action();
        }
    }else if( this->current_game->another_condition ){
        //more code
    }

複雑さはすぐに手に負えなくなりました。

この種の問題を C++ でコーディングする良い方法があれば教えてください。この種の状況に対処するための適切な設計パターンはありますか? ロジックがソース内に含まれている必要はありません。C++ からアクセスできる必要があるだけです。唯一の実際の要件は、適度に高速であることです。

また、ルール エンジンも調べましたが、十分に高速であれば適切である可能性があります。適切なオープン ソースの C++ ルール エンジンがあるかどうか知っていますか?

4

6 に答える 6

10

コードはデータ、データはコードです。動作するコードができました。コンパイルできるように C++ に公開するだけで、最小限のインタープリターを実装して評価することができます。

1 つの可能性は、Prolog ルールを取得し、可能な限り直接的な方法でデータ構造に変換することです。おそらく、次のような単純なテーブルを設計できます。

struct {
    State coming_from;
    Event event;
    void (*func)(some, args);
    State going_to;
} rules[] = {
    { WANDERING_AROUND, HEAR_SOUND, look_around, ENEMY_SEEN },
    { ENEMY_SEEN,       GUN_LOADED, fire_gun,    SNEEK_AWAY },
    { next, rule, goes, here },
    etc... 
}

同様に、関数呼び出しは、元の Prolog に似た方法でデータ構造を設定できます。

void init_rules () {
    rule("Parent", "Bill", "John");
    rule("Parent", "Paul", "Bill");
    // 99 more rules go here...
}

次に、単純なインタープリターを実装して、そのデータ構造をトラバースし、必要な答えを見つけます。ルールが 1000 未満の場合、検索のブルート フォース アプローチで十分に高速である可能性がありますが、後でいつでも巧妙になり、実際の Prolog 環境と同じように物事を行うことができます。

于 2010-09-14T08:09:42.733 に答える
5

ポリモーフィズムを使用できます。仮想関数の呼び出しは、事実上、コンパイラによって実行および最適化された大掛かりなスイッチ/ケースです。

class GameState {
    virtual void do_something() { std::cout << "GameState!"; }
    // some functions
    virtual ~GameState() {}
};
class SomeOtherState : public GameState {
    // some other functions
    virtual void do_something() { std::cout << "SomeOtherState!"; }
};
class MyFinalState : public GameState {
    virtual void do_something() { std::cout << "MyOtherState!"; }
};
class StateMachine {
    std::auto_ptr<GameState> curr_state;
public:
    StateMachine()
        : curr_state(NULL) {}
    void DoSomething() { curr_state->DoSomething(); }
    void SetState(GameState* ptr) { curr_state = ptr; }
    template<typename T> void SetState() { curr_state = new T; }
};
int main() {
    StateMachine sm;
    sm.SetState(new SomeOtherState());
    sm.SetState<SomeOtherState>();
    sm.DoSomething(); // prints "SomeOtherState!"
    sm.SetState<MyFinalState>();
    sm.DoSomething(); // prints "MyFinalState!"
}

上記の例では、どの状態についても切り替える必要はありませんでした。また、さまざまな状態が存在することや、それらが何をするか (とにかく StateMachine クラスで) を知る必要さえありませんでした。選択ロジックはコンパイラによって行われました。

于 2010-09-15T14:53:38.210 に答える
3

プロローグ コードを C++ コードに変換する場合は、C++ でのロジック プログラミングを有効にする Castor ライブラリ (C++) を参照してください: http://www.mpprogramming.com/Cpp/Default.aspx

私は自分でそれを試したことがないので、パフォーマンスについては何も知りません。

ステート マシンを使用する場合は、Boost.Meta State Machine をご覧ください。

于 2010-09-15T11:38:25.367 に答える
2

有限ステート マシンがゲームに十分でない理由がよくわかりません。やりたいことをやるのが一般的な方法です。具体的なアクションからコードをクリーンに保つために、データ駆動型にすることができます。有限状態 m. O'Reilly (David M. Bourg & Glenn Seemann) の「AI for Game Dev」でも説明されています。マシンを小さく理解しやすいように、ルールをいくつかの小さなルール セットに分割したい場合があります。

于 2010-09-11T15:36:49.767 に答える
1

水銀を使ってみてはどうですか?その基本的には、C コードとインターフェイスするように構築されています。

于 2010-09-15T14:41:50.387 に答える
0

Prologの表現力をステートマシンと一致させようとすることは、自転車で車を追い越そうとするようなものです。

キャスターはおそらく行く方法です。非常に軽量で、ロジックプログラミングとその他のC++間のスムーズな相互運用を可能にします。http://www.mpprogramming.com/cppのチュートリアルビデオをご覧ください

于 2010-09-24T07:37:58.867 に答える