質問
私は今日初めに小さなゲームのアイデアについて考えていて、それを実装する方法を思いつきました。このアイデアは、プレイヤーが一連の動きを行うことで小さな効果をもたらすことができますが、特定の順序で実行するとより大きな効果を引き起こすというものです。ここまではうまくいっているので、やり方はわかっています。明らかに、より複雑にする必要がありました (私たちは複雑にするのが大好きなので)。そこで、たとえ異なるものであっても、より大きな効果をもたらすシーケンスの可能なパスが複数ある可能性があると考えました。また、一部のシーケンスの一部が他のシーケンスの始まりになっている場合や、シーケンス全体が他のより大きなシーケンスに含まれている場合もあります。これを実装する最善の方法はわかりません。ただし、いくつかのアイデアがありました。
1) 循環 n 連結リストを実装できます。しかし、動きのリストに終わりはないので、スタック オーバーフロー™ が発生するのではないかと心配しています。このアイデアは、すべてのノードに n 個の子があり、コマンドを受信すると、その子の 1 つに誘導されるか、そのコマンドに使用できる子が存在しない場合は最初に戻るというものです。子に到着すると、いくつかの関数が実行され、大小さまざまな影響が生じます。ただし、これにより、さまざまな効果を持つ特定の動きで終了する可能性のあるすべてのシーケンスに対処するために、ツリー上に多数の重複ノードが発生する可能性があり、維持するのが面倒になる可能性がありますが、私にはわかりません。私はこれほど複雑なことをコード上で試したことはなく、理論的にのみ試しました。このアルゴリズムは存在し、名前はありますか?それは良いアイデアですか?
2) ステートマシンを実装できました。次に、リンクされたリストを歩き回る代わりに、関数を呼び出してそれに応じてマシンの状態を更新する巨大な入れ子になったスイッチを用意します。実装は簡単そうに見えますが...良い...面白くないようです...エレガントでもない。巨大なスイッチはいつも醜く思えますが、これはもっとうまく機能するでしょうか?
3) 提案はありますか?私は上手ですが、まだまだ経験が浅いです。コーディング分野の良い点は、問題がどれほど奇妙なものであっても、過去に誰かがそれを解決しているにもかかわらず、どこを見るべきかを知っていなければならないことです。誰かが私よりも良いアイデアを持っているかもしれないので、私は本当に提案を聞きたかったのです。
解決
あなたが言っていることを正確に理解しているとは完全に確信しているわけではありませんが、類似した状況として、誰かがキーボードで数字の無限のストリームを入力していると言います。 「117」はマジックシーケンス、「468」は別のシーケンス、「411799」は別のシーケンス(最初のシーケンスを含む)です。
ユーザーが次を入力した場合:
55468411799
* sで「マジックイベント」を発生させたい:
55468 * 4117 * 99 *
またはそのようなものですよね?それがあなたが話している問題に類似している場合、(Javaのような擬似コード)のようなものはどうですか:
MagicSequence fireworks = new MagicSequence(new FireworksAction(), 1, 1, 7);
MagicSequence playMusic = new MagicSequence(new MusicAction(), 4, 6, 8);
MagicSequence fixUserADrink = new MagicSequence(new ManhattanAction(), 4, 1, 1, 7, 9, 9);
Collection<MagicSequence> sequences = ... all of the above ...;
while (true) {
int num = readNumberFromUser();
for (MagicSequence seq : sequences) {
seq.handleNumber(num);
}
}
MagicSequenceには次のようなものがあります:
Action action = ... populated from constructor ...;
int[] sequence = ... populated from constructor ...;
int position = 0;
public void handleNumber(int num) {
if (num == sequence[position]) {
// They've entered the next number in the sequence
position++;
if (position == sequence.length) {
// They've got it all!
action.fire();
position = 0; // Or disable this Sequence from accepting more numbers if it's a once-off
}
} else {
position = 0; // missed a number, start again!
}
}
他のヒント
とにかく状態マシンを実装したいかもしれませんが、状態遷移をハードコードする必要はありません。
状態のグラフを作成してみてください。状態Aから状態Bへのリンクは、AがBにつながる可能性があることを意味します。
その後、実行時にグラフをトラバースして、プレーヤーの行く先を見つけることができます。
編集:グラフノードを次のように定義できます:
-state-id
-他の州へのリンクのリスト、
すべてのリンクが定義する場所:
-state-id
-前提条件、この状態に進む前にアクセスする必要がある状態のリスト
あなたが説明していることは、ゲームライブ Civilization のテクノロジー ツリーに非常によく似ています。
Civ の作者がどのように構築したのかは知りませんが、考えられる「動き」を表すにはマルチグラフを使用する傾向があります。「経験」がなくても始められるものもあるでしょう。 、最後まで複数のパスがあります。
ゲームの各段階でどのような潜在的な選択肢があるかを引き出し、いくつかの選択肢から他の選択肢へ向かう線を引きます。
グラフは [比較的] 簡単に実装および利用できる概念であるため、これで実装を始めることができます。
ニューラルネットワークのような音。作成し、トレーニングして、探しているさまざまな効果を引き起こすパターンを認識することができます。
あなたが説明していることは、依存関係グラフまたは単語グラフに似ているように聞こえます。それらを調べてみるとよいでしょう。
@ Cowan、@ Javier:いいアイデアです。追加するのはいいですか?
MagicSequenceオブジェクトがユーザー入力の着信ストリームをリッスンするようにします。つまり、入力(ブロードキャスト)を通知し、各オブジェクトが内部入力ストリームに入力を追加できるようにします。このストリームは、入力がMagicSequenceにアクションを起動させるパターン内の予想される次の入力でない場合にクリアされます。パターンが完了するとすぐに、アクションを起動し、内部入力ストリームをクリアします。
これを待機しているMagicSequencesにのみ入力することにより、これを最適化します。これには2つの方法があります。
-
すべてのMagicSequenceを、パターン内の数字に対応するイベントに接続できるオブジェクトがあります。 MagicSequence(1,1,7)は、自身をgot1およびgot7に追加します。例:
UserInput.got1 + = MagicSequnece [i] .SendMeInput;
-
これを最適化して、MagicSequencesが入力されるたびに無効なイベントから登録を解除し、有効なイベントに登録することができます。
必要なエフェクトごとに小さなステートマシンを作成します。各ユーザーアクションで、すべてのステートマシンに「ブロードキャスト」します。その場合、ほとんどは気にしませんが、一部は前進するか、おそらく後退します。それらの1つが目標に達すると、目的の効果が得られます。
コードをきれいに保つために、状態マシンをハードコーディングせずに、状態グラフをエンコードする単純なデータ構造を構築します。各状態は、興味深いイベントのリストを持つノードであり、それぞれが次の状態のノードを指します。各マシンの状態は、適切な状態ノードへの単なる参照です。
edit:Cowanのアドバイスはこれと同等ですが、ステートマシンを最適化して単純なシーケンスのみを表現します。特定の問題には十分なようですが、より複雑な条件ではより一般的な解決策が必要になる場合があります。