この投稿では、長方形平面上での単細胞生物の進化のシミュレーションゲームを書いた私の経験についてお話します。 シミュレーターアルゴリズムの開発に加えて、このプロジェクトをC#で開発する際に遭遇した問題、およびブラウザーで動作するように移植することについて説明します。 記事の最後に、ゲームの準備ができているバージョンとすべてのソースコードへのリンクがあります。 私があなたに興味があるなら、猫をお願いします。
ひらめき
始める前に-このシミュレーターを書くきっかけになったプロジェクトについて簡単に話したいと思います。 私のプロジェクトは1970年に数学者ジョン・コンベックスによって発明されたゲーム「Life」に非常に似ているため、投稿のヘッダーのスクリーンショットから最初の類推を描くことができます。 私はこれと、 この記事から他のhabrowserにさらにインスピレーションを受けました。 尊敬される読者をリンクから追い込まないために、これらの記事で説明されていることを簡単に説明します。1つ目は自然選択を追加したConwayのゲームの修正、2つ目はニューラルネットワークに基づくインテリジェントライフのシミュレーションです。
要件の定義
ゲーム「Life」の別のクローンを書きたくなかったので、自然選択のアイデアがとても好きだったので、進化に集中することにしました。 名前はそれ自身で来ました-「最強は生き残ります。」
開発中に自分で設定した要件は次のとおりです。
- 進化のシミュレーターを開発するために、そこには多くの種類の生物があります-細胞;
- 細胞と細胞との相互作用のためのいくつかのオブジェクト(食物、毒、死細胞)を追加します;
- 最初からシミュレーションの生産性を高めて、長期間にわたって結果をすばやく確認できるようにします。
- シミュレーションは可能な限りリアルでなければなりません。
- 生物の生活条件を変える可能性があるはずです。
TSSでは、セルには次の状態に依存する多くのインジケータがあります。 環境を分析し、これらのデータに基づいて意思決定を行い、生き残るのに役立ちます。 周囲を検査するとき、それらの優先順位は、ゲノムに記録された修飾子によって影響を受けます。 たとえば、攻撃性パラメータがあります。 細胞が外来ゲノムを持つ別の細胞を見ると、この修飾子(攻撃性)がその方向に移動したいという欲求に追加されます。 空腹、集団性、毒への渇望、死んだ細胞への渇望についても同じです。 計算後、修飾子の合計が最も高い側が選択されます。
ゲームの基本的なルール :
- ゲームの宇宙には独自の時間単位があります-ティック;
- 各セルには特定のレベルのエネルギーがあり、ティックごとに減少します。
- 細胞の主な目標は、生き残り、増殖することです。 これを行うには、彼女はできるだけ多くのエネルギーを収集する必要があります。
- すべてのセルには最大年齢があります。
- 細胞は生存戦略に影響する異なるゲノムを持つことができますが、物理的能力は同等です。
- 各ティックの間、各セルは4つの選択されたサイドのいずれかで1ステップ実行できます。 新しい位置にあるオブジェクトに応じて、ステップの方向を選択すると、何らかのイベントが発生します。 空の場所がある場合-食べ物、毒、または死んだ細胞がある場合、単にそこに移動します-それは彼らのエネルギーレベルを受け取り、またそこに移動します。 生きている細胞がある場合、それは所定の場所に残り、特定のイベントがある細胞の種類によって異なります。 同じゲノムを持つ細胞がある場合、両方がエネルギーを受け取り、異なるゲノムがある場合、この細胞はそこからエネルギーの一部を受け取ります。
セルはティックごとに1回移動できます。 より正確には、彼女は写真に示すように周囲の領域を検査し、優先方向を選択します:上、下、左、右、または静止します。
また、セルは乗算できます。 セルが十分なエネルギーを持っている場合、複製ごとに、ダニごとに1回、複製が発生します。 同時に、近くに空のスペースがあり、同じゲノムを持つ新しいセルが作成されるか、変異したゲノムを持つセルがあります。 同時に、そのエネルギーレベルは、親細胞と娘細胞の間で均等に分割されます。 再生に必要なエネルギーレベルはユーザーが決定しますが、パラメーター「再生」はそれを修正する役割を果たします。 このパラメーターはゲノムに保存されます。 それが高いほど、細胞が再生するのに必要なエネルギーが少なくなります。
どういうわけか私はその時にこのゲームを想像しました。 一般的に、同様のことが起こりました。
ゲームプレイの本質
ゲームプレイは、ユーザーに宇宙の定数の編集者が与えられ、生物の生活条件を変更し、それらがどのように適応するかを見ることができるという事実にあります。 あまり印象的ではありませんが、プレーヤーが使用できる値は非常に多く(約30)、結果の多くのユニークなバリエーションを取得できるように変更されています。 各定数の詳細については、ゲームフォルダーまたはスポイラーのマニュアルを参照してください。
宇宙の定数
MaxCountOfCellTypes-細胞ゲノムの最大数。
Mutation_Enable-再生中の細胞突然変異を有効または無効にすることができます。
Mutation_AttackChildrenMutantsOfFirstGeneration-このプロパティが無効になっている場合、セルは突然変異したときに第一世代の子孫を攻撃できません。
Mutation_AttackParentIfCellIsYouMutant-このプロパティが無効になっている場合、変異セルは第一世代の祖先を攻撃できません。
Mutation_ChangedValuesAtOne-セルの動作に関与する値が突然変異ごとに変化する回数を示します。 たとえば、値が10の場合、動作を担当するセルのランダムプロパティの10倍が選択され、1に変更されます。 1〜200の範囲で指定できます。
Mutation_ChancePercent-生殖中の突然変異の可能性。
CellAge_Max-セルの最大経過時間 。
CellAge_AdultCell-成人の細胞年齢。 子のセルでは、攻撃性のレベルはCellGenome_Child_Aggressionより高くなく、乗算できません。
EnergyLevel_CreatingCell-セルが開始時に生成されるときのセルのエネルギーレベル。
EnergyLevel_NeededForReproduction-セルが増殖できるエネルギーのレベル。 CellGenome_ReproductionRangeにより異なる場合があります。
EnergyLevel_MaxForCell-セルが蓄積できるエネルギーの最大レベル。
EnergyLevel_DeadCell-死細胞から受け取ったエネルギーレベル。
EnergyLevel_DefFood-食物から受け取ったエネルギーのレベル。
EnergyLevel_PoisonedFood-毒から受け取ったエネルギーのレベル。
EnergyLevel_MovesFriendly-相対物がセルに向かって移動したとき、またはセルに向かって移動したときにセルが受け取るエネルギーのレベル。 原則として、それは普通の食物のエネルギーレベルよりはるかに少ないです。 コロニーの作成を促進します。
EnergyLevel_MovesAggression-攻撃中にセルが外来セルからどれだけのエネルギーを消費するかを示します。
CellsCount_MaxWithOneType-同じゲノムを持つフィールド内のセルの最大数。
CellGenome_Child_Aggression-子セルの攻撃性のレベル。
CellsCount_MaxAtField-フィールド上のセルの最大数。
EnergyEntropyPerSecond-ティックごとにセルが失うエネルギーのレベル。
Special_FoodCountForTick-宇宙の各ティックで生成される食物の量。
Special_PoisonCountForTick-宇宙の各ティックで生成される毒の量。
CellGenome_HungerRange-セル生成中の空腹値の範囲。
CellGenome_AggressionRange-セル生成中の攻撃性の値の範囲。
CellGenome_ReproductionRange-セル生成中の生殖値の範囲。 値が高いほど、細胞が再生するのに必要なエネルギーが少なくなります。
CellGenome_FriendlyRange-セル生成中の収集値の範囲。
CellGenome_PoisonRange-セル生成中のポイズンへの引力の値の範囲。 通常は負。
CellGenome_CorpseRange-セル生成中の死細胞への引力の値の範囲。
Mutation_Enable-再生中の細胞突然変異を有効または無効にすることができます。
Mutation_AttackChildrenMutantsOfFirstGeneration-このプロパティが無効になっている場合、セルは突然変異したときに第一世代の子孫を攻撃できません。
Mutation_AttackParentIfCellIsYouMutant-このプロパティが無効になっている場合、変異セルは第一世代の祖先を攻撃できません。
Mutation_ChangedValuesAtOne-セルの動作に関与する値が突然変異ごとに変化する回数を示します。 たとえば、値が10の場合、動作を担当するセルのランダムプロパティの10倍が選択され、1に変更されます。 1〜200の範囲で指定できます。
Mutation_ChancePercent-生殖中の突然変異の可能性。
CellAge_Max-セルの最大経過時間 。
CellAge_AdultCell-成人の細胞年齢。 子のセルでは、攻撃性のレベルはCellGenome_Child_Aggressionより高くなく、乗算できません。
EnergyLevel_CreatingCell-セルが開始時に生成されるときのセルのエネルギーレベル。
EnergyLevel_NeededForReproduction-セルが増殖できるエネルギーのレベル。 CellGenome_ReproductionRangeにより異なる場合があります。
EnergyLevel_MaxForCell-セルが蓄積できるエネルギーの最大レベル。
EnergyLevel_DeadCell-死細胞から受け取ったエネルギーレベル。
EnergyLevel_DefFood-食物から受け取ったエネルギーのレベル。
EnergyLevel_PoisonedFood-毒から受け取ったエネルギーのレベル。
EnergyLevel_MovesFriendly-相対物がセルに向かって移動したとき、またはセルに向かって移動したときにセルが受け取るエネルギーのレベル。 原則として、それは普通の食物のエネルギーレベルよりはるかに少ないです。 コロニーの作成を促進します。
EnergyLevel_MovesAggression-攻撃中にセルが外来セルからどれだけのエネルギーを消費するかを示します。
CellsCount_MaxWithOneType-同じゲノムを持つフィールド内のセルの最大数。
CellGenome_Child_Aggression-子セルの攻撃性のレベル。
CellsCount_MaxAtField-フィールド上のセルの最大数。
EnergyEntropyPerSecond-ティックごとにセルが失うエネルギーのレベル。
Special_FoodCountForTick-宇宙の各ティックで生成される食物の量。
Special_PoisonCountForTick-宇宙の各ティックで生成される毒の量。
CellGenome_HungerRange-セル生成中の空腹値の範囲。
CellGenome_AggressionRange-セル生成中の攻撃性の値の範囲。
CellGenome_ReproductionRange-セル生成中の生殖値の範囲。 値が高いほど、細胞が再生するのに必要なエネルギーが少なくなります。
CellGenome_FriendlyRange-セル生成中の収集値の範囲。
CellGenome_PoisonRange-セル生成中のポイズンへの引力の値の範囲。 通常は負。
CellGenome_CorpseRange-セル生成中の死細胞への引力の値の範囲。
これは、このエディターがWebバージョンでどのように見えるかです。
また、Windows用のバージョンでは、食べ物や毒が生成される場所を編集することもできます。
開発と結果
数か月の開発の結果、ゲームの4つのバージョンを作成しました。 最初の2つはシミュレータユニバースのロジックのテストとデバッグ用、3つ目はWindows用のリリースバージョン、4つ目はブラウザ用のバージョンです。 ここで、最初の2つについて簡単に説明し、その後でメインバージョンの機能について説明します。
Windowsフォームバージョン
これは私のプログラムの最初の作業バージョンです。 このバージョンでは、ほとんどのゲームロジックはまだ実装されておらず、多くのバグがあります。 ここにはレンダリングはありません。すべてが文字としてテキストボックスに表示されます。 このようなテキスト出力は非常にゆっくり(1秒あたり1〜2ティック)動作しましたが、それでも、チューブのようなものがあります。
テキストコンソールバージョン
奇妙なことに、プログラムの2番目のバージョン。 私は実験室のためにそれを書きました。 これ以降のバージョンでは、ゲームロジックは同じです。
WPFバージョン
私はこのバージョンに最も時間を費やしましたが、最も開発されたバージョンです。 ここでのフレームのレンダリングには20〜30ミリ秒かかり、フィールドのサイズに依存せず、ティック自体は非常に迅速に処理されます。 ユーザーは巨大な競技場(少なくとも2000x2000セル)を作成できます。 クリックするだけで、各セルに関する情報を表示できます。
プログラムのデモ。
私のプロジェクトのこのバージョンを書くとき、私は多くの問題を解決しなければなりませんでした。 ここで説明する最も記憶に残る決定。
1) 手動レンダリング 。 私は自転車を書くことにしたので、プリミティブをレンダリングするための標準のWPFツールを使用して、エンジンなしでレンダリングを行いました。 しかし、これにより、プログラム用にレンダリングを可能な限り最適化することができました。
レンダリングを開始したばかりのとき、キャンバス全体がすべてのフレームでゼロから再描画されたため、パフォーマンスが大幅に低下しました。 しかし、その後、バッファに最後のフレームを保存するアルゴリズムを作成し、新しいフレームを描画するときに-オブジェクトがあった場所に塗りつぶされましたが、現在ではありません。 これがエンジンでどのくらい早く機能するかはわかりませんが、ここでは生産性が大幅に向上し、フィールドのサイズに依存しなくなりました。
エンジンがないため、同じアルゴリズムを使用したブラウザーへの移植も簡単になりました。
しかし、この方法には欠点もあります-多くの場合、画像に干渉があります。これは特にWPFバージョンで顕著です。
2) 動的クラスエディター 。 ユニバースの設定の編集フィールドが表示されたフォームを何百回もやり直さないように、リフレクションを使用して、送信されたオブジェクト(この場合は設定のあるオブジェクト)のフィールドを読み取る動的エディターを作成し、それらを編集するためのフォームを作成しました。
これは、このエディターがWPFバージョンでどのように見えるかです。
ウェブ版
WPFバージョンを作成してからしばらくして、それをブラウザーに移植したいと思いました。 記事のヘッダーで結果を確認するか、個人的にサイトにアクセスして試してみてください。
私は少しJavascriptを知っていましたが、1.5行のゲームロジックコードを書き直したくありませんでした。 しかし、解決策がありました-JavascriptのオープンC#コンパイラーであるBridge.NETを使用することにしました。 はい、親愛なる読者、あなたは正しく理解しました-今、私たちはC#のブラウザの下で書いています。 このコンパイラのおかげで、私はよく知っている言語を使用することができました。 残ったのは、レンダリングとディスプレイに関連する残りのコードを書き直すことだけでした。
それにもかかわらず、ブラウザの特性のため、JavascriptとHTMLの知識は私にとって非常に役に立ちました。
まとめ
このプロジェクトを開発するとき、私は一般にプログラミングスキルを大幅に強化しました。 現時点では、ゲームのすべてのバージョンがこのサイトtss-game.cc.uaで利用可能です。 あなた自身が開発を開始したい場合、または単にコードを確認したい場合-ここに私のgithubがあります、ここでは広範なコメント付きの私のプログラムのすべてのバージョンがあります 私の経験を読んで興味を持っていただけたことを願っています。おそらく、この記事が似たようなことをするきっかけになると思います。