ゲーム「人生」。 また。 今回は3Dで

この1週間で、Habrにはゲーム「Life」に関するいくつかの記事が補充されました。 それでは、このトピックに関するベストプラクティスを共有します。



まえがき



昨年の夏、たまたまNSUが実施した並列プログラミングでサマースクールに参加しました。 学校内では、各生徒が講義で表明されたトピックの1つに関するプロジェクトを準備する必要がありました。 セルオートマトンに興味がありました。 「細胞オートマトン」というフレーズとの最初の関連は、まさに「生命」です。

誰もスクリーンに住んでいる黒い細胞を見ることに興味がないことに気づきました。 そして、そのようなプロジェクトには単純すぎます。 根本的に新しい何かを考え出す必要がありました。 私は自分の考えの範囲を広げ、二次元空間を超えることにしました。 文字通りの意味で。 このゲームを立体化してみませんか? 結局のところ、それははるかに興味深いです!





ステージ1.セルラーオートマトン



ここでは、誰もが(まあ、またはほぼ全員)セルオートマトンを想像していると思います。 したがって、理論を掘り下げることはせず、すぐに実践に移ります。 このセルオートマトンを最も原始的なものと区別する唯一のことは、計算を並列化する必要があったことです。 ただし、将来的には何か他のものが追加されますが、今のところは説明しません。 私たちの目的のためのセルオートマトン(以下KA)は同期している必要があります。 幸いなことに、アルゴリズムの並列実装を使用しても、これにより不都合が生じることはありません。 OpenMPを使用する場合、#pragma omp for {}ブロックに宇宙船の値を計算するループを配置するだけで十分です。

このようなもの


#pragma omp parallel shared(Temp, Space, Size) private(Num_of_nbr, x, y, z) { #pragma omp for for (x = 0; x < Size; x++) for (y = 0; y < Size; y++) for (z = 0; z < Size; z++) { // Neighbors(x, y, z)  -  //    x, y  z Num_of_nbr = Neighbors(x, y, z); if (Space[x][y][z] == 1) if (Num_of_nbr <= Smid + Sdiff * Koeff[Int_Temp[x][y][z]] \ && Num_of_nbr >= Smid - Sdiff * Koeff[Int_Temp[x][y][z]]) Temp[x][y][z] = 1; else Temp[x][y][z] = 0; else if (Num_of_nbr <= Bmid + Bdiff * Koeff[Int_Temp[x][y][z]] \ && Num_of_nbr >= Bmid - Bdiff * Koeff[Int_Temp[x][y][z]]) Temp[x][y][z] = 1; else Temp[x][y][z] = 0; } #pragma omp for for (x = 0; x < Size; x++) for (y = 0; y < Size; y++) for (z = 0; z < Size; z++) Space[x][y][z] = Temp[x][y][z]; }
      
      









ステージ2.ビジュアライザー



これらすべてを直接見ることができれば、悪くないでしょう。 幸運なことに、文字通り1か月前には、freeglutライブラリを使用してOpenGLを操作する能力がほとんどありませんでした。 また、ゲームスペースを回転、ズームイン、ズームアウトできるようにしたかったのです。 管理は、マウスとキーボードを使用して実行されます。 マウスが回転し、キーボードが動きます。 キーボードはLife自体も制御します。 3次元空間の認識には2次元空間よりも時間がかかるため、キーを押して次のステップに進むことにしました。

ビジュアライザーの最初のテストはバタンと合格しました。 しかし、彼に100 * 100 * 100(またはそれ以上、正確には覚えていません)のセルを並べようとすると、各フレームは最大8秒描画されました。 そうではありません。 それから、私は視界のピラミッドを思い出しました。 あいにく、この非常にピラミッドでの描画には、ほとんどの時間でスペースが完全に見えるため、ほぼ同じ時間がかかりました。 この空間の中心に近づき、それを超えてさらに移動すると、レンダリング時間が大幅に短縮されました。



ステージ3.外部の影響



まだ時間があり、プロジェクトはすでに配信の準備ができていました。 何かが欠けていることに気づきました。 しかし、正確に言うと、質問はもっと複雑です。 しばらく考えた後、私は自分の宇宙船が実際の生活とはまったく異なると判断しました。 細胞の生殖に影響を与える外部要因は十分ではありません。 そのような要因として温度が選択されました。

簡単に言うと、つまり、一定の温度があり、生存率と出生率が最大になる最適温度と呼ばれます。 温度の低下と上昇により、生存はより困難になります。 しかし、温度を設定するだけでは面白くないでしょう。 そして再び、宇宙船に頼る。 初期状態では、温度は空間の中心に集中しています。 時間が経つにつれて、「暖かい空気」が空間全体に均等に分散されます。 これは拡散によって達成されます。 はい、単純ではなく、整数です。 この場合、これは、セル内の温度の一部が残り、隣接するセルが残りを交換するという事実から成ります。 そのようなセルオートマトンは、すべてのセルが同時に変更された場合は奇妙だったので、もはや同期することはできません...しかし、いくつかのプロセスのためにプログラムするには、トリックに頼る必要があります-非同期宇宙船からブロック同期のものに切り替える必要があります。 ポイントは、各プロセスにブロックが割り当てられることです。 このブロック内では、宇宙船は非同期です。 ただし、ブロックは互いに同期されます。 図では、同じ色のセルが一度に計算されます。



整数拡散


 #pragma omp parallel shared(Size, BlockSize, Int_Temp, PosCell, PosNbr, DiffKoeff)\ private(CellNum, NbrNum, x, y, z, Cell1, Cell2, Rem1, Rem2, Mov1, Mov2) { CellNum = rand() % 27; #pragma omp for for (x = 1; x < Size; x += BlockSize) for (y = 1; y < Size; y += BlockSize) for (z = 1; z < Size; z += BlockSize) { NbrNum = rand() % 6; Cell1 = Int_Temp[(x + PosCell[CellNum][0] + Size) % Size] \ [(y + PosCell[CellNum][1] + Size) % Size] \ [(z + PosCell[CellNum][2] + Size) % Size]; Cell2 = Int_Temp[(x +PosCell[CellNum][0] + PosNbr[NbrNum][0] + Size) % Size] \ [(y + PosCell[CellNum][1] + PosNbr[NbrNum][1] + Size) % Size] \ [(z + PosCell[CellNum][2] + PosNbr[NbrNum][2] + Size) % Size]; //DiffKoeff ,    //  ,     . Rem1 = Cell1 * (float)(1.0 - DiffKoeff); Rem2 = Cell2 * (float)(1.0 - DiffKoeff); Mov1 = Cell1 * DiffKoeff; Mov2 = Cell2 * DiffKoeff; if ((float)Cell1 * DiffKoeff - (float)Mov1 > (float)(rand() % 10) / 10.0) Rem1++; else if ((float)Cell1 * DiffKoeff - (float)Mov1) Mov1++; if ((float)Cell2 * DiffKoeff - (float)Mov2 > (float)(rand() % 10) / 10.0) Rem2++; else if ((float)Cell2 * DiffKoeff - (float)Mov2) Mov2++; Int_Temp[(x + PosCell[CellNum][0] + Size) % Size] \ [(y + PosCell[CellNum][1] + Size) % Size] \ [(z + PosCell[CellNum][2] + Size) % Size] = Mov2 + Rem1; Int_Temp[(x +PosCell[CellNum][0] + PosNbr[NbrNum][0] + Size) % Size] \ [(y + PosCell[CellNum][1] + PosNbr[NbrNum][1] + Size) % Size] \ [(z + PosCell[CellNum][2] + PosNbr[NbrNum][2] + Size) % Size] \ = Mov1 + Rem2; } }
      
      









さらなる展望



もちろん、この実装は理想的ではありません。 コードは決して最適化されていません。 まず、ビジュアライザーを改善できます。 可視性ピラミッドに沿ったクリッピングに加えて、Zバッファに沿ったクリッピングを追加して、前景セルによって隠されているセルが描画されないようにします。

一般に、プロジェクトは宇宙の規模まで膨張させることができます。 たとえば、レイヤーを追加したいです。 外部の影響についてのみ話せば、それは例えば安reliefになります。 標高が高いと、セルの可能性は低くなります。 また、そこはもっと寒くなります。 食物として役立つ植生の層を追加できます。 また、セル自体を使用してレイヤーを複製し、相互に食べる複数のタイプの生物を作成できます。 突然変異などを追加できます。 それは欲望でしょう。



結果



さて、最後に、最も興味深いものに移ることができます。

最初は、温度層なしでプログラムがテストされました。それまではプログラムが存在しなかったからです。 残念ながら、プログラムには通常のインターフェイスがなく、テストジェネレーターを作成する必要がありました。 彼は特に興味がありません。 彼の唯一の仕事は、プレイスペースの中央にセルの立方体を作ることです。 以下は、3x3x3、4x4x4、および5x4x5のキューブです。 ここでは、何が起こっているのかを全体的に把握するためのいくつかの手順を示します。



3x3x3、ステップ1:





3x3x3、ステップ4:





3x3x3、ステップ5および6:



それはかなり面白いフラッシャーが判明しました。



4x4x4ステップ1:





4x4x4、ステップ10:





4x4x4ステップ100:



さらに実質的には何も変わりません。



5x5x5、ステップ1:





5x5x5、ステップ4:





5x5x5、ステップ7:





5x5x5、ステップ8:



次のステップでは、単一の生細胞は残りません。



次に、4x4x4キューブに温度を追加します。 温度レイヤーは中央カットとして表示されます。 赤は最適な温度であり、黄色は通常よりわずかに低く、ピンクは低くなっています。 ゲーム空間の寸法は50x50x50で、中央には最適な温度の40x40x40の立方体があります。 これが何が起こったのかです。



ステップ1:





ステップ10:





ステップ50:





ステップ150:





ステップ200:



すべてが非常に明確です。空気が冷え、生命が停止します。



残念ながら、入力データはジェネレーターでハードコーディングされているため、プログラム自体を共有することはできません。 しかし、ソースは共有できますが、すぐに警告します:コードはひどいです。

ソースコード


発電機

KA

ビジュアライザー




All Articles