Scalaでライフゲームを書いてみました
ライフゲームって?
簡単に言うと、セルを生物に見立てて、ある一定のルールを与え、進化、退化、淘汰などのプロセスを眺めるゲームです。
セルの初期配置等によっては生き物の様に見える所がとても楽しいです。
詳しくはWikiを参照して下さい。
勉強がてら、Scalaでライフゲームのアルゴリズムを代表的なルール(23/3)で書いてみました。
ライフゲームのルール
Wikiを見れば載っている事ですが、代表的なルール(23/3)とは
- ある死んでいるセルの周りに、生きているセルが3つ存在する場合、そのセルは生きているセルとして誕生する
- ある生きているセルの周りに、生きているセルが2〜3存在する場合、そのセルは生存する
- ある生きているセルの周りに、生きているセルが1つ、あるいは4つ以上存在する場合、そのセルは死滅する
というもので、生存に必要なセルが2〜3、誕生に必要なセルが3ということで、(23/3)と表現します。
ソース
import scala.annotation.tailrec object LifeGame { def main(args: Array[String]) = { queenBee } /** * ライフゲーム */ @tailrec def execute(nowGeneration: List[List[Int]]) { // 現世代を次世代にコピー var nextGeneration: List[List[Int]] = nowGeneration drow(nowGeneration) for (rowIndex <- nowGeneration.zipWithIndex) { for (cellIndex <- nowGeneration(rowIndex._2).zipWithIndex) { val i: Int = roule(rowIndex._2, cellIndex._2, nowGeneration) nextGeneration = nextGeneration.updated(rowIndex._2, nextGeneration(rowIndex._2).updated(cellIndex._2, i)) } } Thread.sleep(250); // 次世代の処理へ execute(nextGeneration) } /** * ライフゲーム ルール */ def roule(x: Int, y: Int, target: List[List[Int]]): Int = { // ■座標の場合、◆座標から◇座標までを取得してyieldで返却し、その和をもって次世代での状態(0→死,1→生)を返却する // 現在対象となっている座標のセルは処理しない 「if !(x == xx && y == yy))」 // ◆□□ // □■□ // □□◇ (for (xx <- List.range(x - 1, x + 2); yy <- List.range(y - 1, y + 2) if !(x == xx && y == yy)) yield { try { target(xx)(yy) } catch { case e: IndexOutOfBoundsException => 0 // 何もしない } }).sum match { case i if i >= 4 || i <= 1 => 0 // 死 case i if i == 3 => 1 // 誕生 case _ => target(x)(y) // 変異なし } } /** * コンソール出力 */ def drow(generation: List[List[Int]]) { println("") generation map { cellList => cellList map { case 1 => print("■") case _ => print("□") } println("") } } // * // * 以下、テンプレート // * // ブリンカー def blinker = LifeGame.execute(List( List(0,0,0) ,List(1,1,1) ,List(0,0,0) )) // ヒキガエル def glosbe = LifeGame.execute(List( List(0,1,1,0) ,List(1,0,0,0) ,List(0,0,0,1) ,List(0,1,1,0) )) // ビーコン def becon = LifeGame.execute(List( List(1,1,0,0) ,List(1,0,0,0) ,List(0,0,0,1) ,List(0,0,1,1) )) // 時計 def clock = LifeGame.execute(List( List(0,1,0,0) ,List(0,1,0,1) ,List(1,0,1,0) ,List(0,0,1,0) )) // 八角形 def octagon = LifeGame.execute(List( List(0,0,0,1,1,0,0,0) ,List(0,0,1,0,0,1,0,0) ,List(0,1,0,0,0,0,1,0) ,List(1,0,0,0,0,0,0,1) ,List(1,0,0,0,0,0,0,1) ,List(0,1,0,0,0,0,1,0) ,List(0,0,1,0,0,1,0,0) ,List(0,0,0,1,1,0,0,0) )) // パルサー def pulser = LifeGame.execute(List( List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,1,0,0,0,1,0,0,0,0,0) ,List(0,0,0,0,0,1,0,1,0,1,0,0,0,0,0) ,List(0,0,0,0,0,1,0,0,0,1,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) )) // 銀河 def galaxy = LifeGame.execute(List( List(0,0,0,0,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,0) ,List(0,0,1,1,1,1,1,1,0,1,1,0,0) ,List(0,0,1,1,1,1,1,1,0,1,1,0,0) ,List(0,0,0,0,0,0,0,0,0,1,1,0,0) ,List(0,0,1,1,0,0,0,0,0,1,1,0,0) ,List(0,0,1,1,0,0,0,0,0,1,1,0,0) ,List(0,0,1,1,0,0,0,0,0,1,1,0,0) ,List(0,0,1,1,0,0,0,0,0,0,0,0,0) ,List(0,0,1,1,0,1,1,1,1,1,1,0,0) ,List(0,0,1,1,0,1,1,1,1,1,1,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,0) )) // 女王蜂 def queenBee = LifeGame.execute(List( List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0) ,List(1,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,1,1) ,List(1,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1) ,List(0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0) ,List(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) )) }
実行
通称「女王蜂」と呼ばれる物体の実行例です。(queenBeeメソッドを実行)
$ scalac LifeGame.scala $ scala LifeGame □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□■□□□□□□□□□ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□□■■□□□□■■□□□□ ■■□□□□□□□■■■□□□□■■□□■■ ■■□□□□□□□□■■□□□□■■□□■■ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□□□□■□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□□■□■□□□□□□□□□ □□□□□□□□□■□□□□□□■■□□□□ ■■□□□□□□□■□□■□□■□□■□■■ ■■□□□□□□□■□□□□□□■■□□■■ □□□□□□□□□□■□■□□□□□□□□□ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□□■□■□□□□□□□□□ □□□□□□□□□■■■□□□□■■□□□□ ■■□□□□□□■■■□□□□■□□■■■■ ■■□□□□□□□■■■□□□□■■□■■■ □□□□□□□□□□■□■□□□□□□□□□ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□■□□■□□□□□□□□□ □□□□□□□□■□□□□□□□■■■■■□ ■■□□□□□□■□□□□□□■□□□□□■ ■■□□□□□□■□□□□□□□■■□□□■ □□□□□□□□□■□□■□□□□□□□■□ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□□□■■□□□□■■■□□ □□□□□□□□■■□□□□□□■■■■■□ ■■□□□□□■■■□□□□□■□□□■□■ ■■□□□□□□■■□□□□□□■□□□■■ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□■■□□□□□■□□□ □□□□□□□□□□■■■□□□■□□□■□ □□□□□□□■□■■□□□□□■□□□□□ ■■□□□□□■□□■□□□□■□□□□□■ ■■□□□□□■□■■□□□□□□□□□■■ □□□□□□□□□□■■■□□□□□□□□□ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□■□■□□□□□□□□□ □□□□□□□□□■□□■□□□□■□□□□ □□□□□□□□■■□□□□□■■□□□□□ ■■□□□□■■□□□■□□□□□□□□■■ ■■□□□□□□■■□□□□□□□□□□■■ □□□□□□□□□■□□■□□□□□□□□□ □□□□□□□□□□■□■□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□■□□□□□□□□□□ □□□□□□□□■■■■□□□□■□□□□□ □□□□□□□■■■■□□□□□■□□□□□ ■■□□□□□■□□■□□□□□□□□□■■ ■■□□□□□■■■■□□□□□□□□□■■ □□□□□□□□■■■■□□□□□□□□□□ □□□□□□□□□□□■□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□■□■□□□□□□□□□□ □□□□□□□■□□□■□□□□□□□□□□ □□□□□□□■□□□□□□□□□□□□□□ ■■□□□□■□□□□■□□□□□□□□■■ ■■□□□□□■□□□□□□□□□□□□■■ □□□□□□□■□□□■□□□□□□□□□□ □□□□□□□□□■□■□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□■□□□□□□□□□□□ □□□□□□□□■□■□□□□□□□□□□□ □□□□□□■■□□□□□□□□□□□□□□ ■■□□□□■■□□□□□□□□□□□□■■ ■■□□□□■■□□□□□□□□□□□□■■ □□□□□□□□■□■□□□□□□□□□□□ □□□□□□□□□□■□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□■□□□□□□□□□□□□ □□□□□□□■□■□□□□□□□□□□□□ □□□□□□■□■□□□□□□□□□□□□□ ■■□□□■□□■□□□□□□□□□□□■■ ■■□□□□■□■□□□□□□□□□□□■■ □□□□□□□■□■□□□□□□□□□□□□ □□□□□□□□□■□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□■□□□□□□□□□□□□□ □□□□□□□■□■□□□□□□□□□□□□ □□□□□□■□■■□□□□□□□□□□□□ ■■□□□■■□■■□□□□□□□□□□■■ ■■□□□□■□■■□□□□□□□□□□■■ □□□□□□□■□■□□□□□□□□□□□□ □□□□□□□□■□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□■□□□□□□□□□□□□□ □□□□□□□■□■□□□□□□□□□□□□ □□□□□■■□□□■□□□□□□□□□□□ ■■□□□■■□□□■□□□□□□□□□■■ ■■□□□■■□□□■□□□□□□□□□■■ □□□□□□□■□■□□□□□□□□□□□□ □□□□□□□□■□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□■□□□□□□□□□□□□□ □□□□□□■■■■□□□□□□□□□□□□ □□□□□■□■□■■□□□□□□□□□□□ ■■□□■□□■□■■■□□□□□□□□■■ ■■□□□■□■□■■□□□□□□□□□■■ □□□□□□■■■■□□□□□□□□□□□□ □□□□□□□□■□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□■■□□□□□□□□□□□□ □□□□□□■□□□■□□□□□□□□□□□ □□□□□■□□□□□■□□□□□□□□□□ ■■□□■■□■□□□■□□□□□□□□■■ ■■□□□■□□□□□■□□□□□□□□■■ □□□□□□■□□□■□□□□□□□□□□□ □□□□□□□□■■□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□■□□□□□□□□□□□□ □□□□□□□□□■■□□□□□□□□□□□ □□□□■■□□□□■■□□□□□□□□□□ ■■□□■■□□□□■■■□□□□□□□■■ ■■□□■■□□□□■■□□□□□□□□■■ □□□□□□□□□■■□□□□□□□□□□□ □□□□□□□□□■□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□■■□□□□□□□□□□□ □□□□□□□□□■□■□□□□□□□□□□ □□□□■■□□□□□□■□□□□□□□□□ ■■□■□□■□□■□□■□□□□□□□■■ ■■□□■■□□□□□□■□□□□□□□■■ □□□□□□□□□■□■□□□□□□□□□□ □□□□□□□□□■■□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□■■□□□□□□□□□□□ □□□□□□□□□■□■□□□□□□□□□□ □□□□■■□□□□■■■□□□□□□□□□ ■■■■□□■□□□□■■■□□□□□□■■ ■■■□■■□□□□■■■□□□□□□□■■ □□□□□□□□□■□■□□□□□□□□□□ □□□□□□□□□■■□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□■■□□□□□□□□□□□ □□□□□□□□□■□□■□□□□□□□□□ □■■■■■□□□□□□□■□□□□□□□□ ■□□□□□■□□□□□□■□□□□□□■■ ■□□□■■□□□□□□□■□□□□□□■■ □■□□□□□□□■□□■□□□□□□□□□ □□□□□□□□□■■□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□■■□□□□□□□□□□□ □□■■■□□□□■■□□□□□□□□□□□ □■■■■■□□□□□□■■□□□□□□□□ ■□■□□□■□□□□□■■■□□□□□■■ ■■□□□■□□□□□□■■□□□□□□■■ □□□□□□□□□■■□□□□□□□□□□□ □□□□□□□□□■■□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□■□□□□□■■□□□□□□□□□□□ □■□□□■□□□■■■□□□□□□□□□□ □□□□□■□□□□□■■□■□□□□□□□ ■□□□□□■□□□□■□□■□□□□□■■ ■■□□□□□□□□□■■□■□□□□□■■ □□□□□□□□□■■■□□□□□□□□□□ □□□□□□□□□■■□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□■□■□□□□□□□□□□ □□□□■□□□□■□□■□□□□□□□□□ □□□□□■■□□□□□■■□□□□□□□□ ■■□□□□□□□□■□□□■■□□□□■■ ■■□□□□□□□□□□■■□□□□□□■■ □□□□□□□□□■□□■□□□□□□□□□ □□□□□□□□□■□■□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□■□□□□□□□□□□□ □□□□□■□□□□■■■■□□□□□□□□ □□□□□■□□□□□■■■■□□□□□□□ ■■□□□□□□□□□■□□■□□□□□■■ ■■□□□□□□□□□■■■■□□□□□■■ □□□□□□□□□□■■■■□□□□□□□□ □□□□□□□□□□■□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□■□■□□□□□□□□□ □□□□□□□□□□■□□□■□□□□□□□ □□□□□□□□□□□□□□■□□□□□□□ ■■□□□□□□□□■□□□□■□□□□■■ ■■□□□□□□□□□□□□■□□□□□■■ □□□□□□□□□□■□□□■□□□□□□□ □□□□□□□□□□■□■□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□■□□□□□□□□□□ □□□□□□□□□□□■□■□□□□□□□□ □□□□□□□□□□□□□□■■□□□□□□ ■■□□□□□□□□□□□□■■□□□□■■ ■■□□□□□□□□□□□□■■□□□□■■ □□□□□□□□□□□■□■□□□□□□□□ □□□□□□□□□□□■□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□■□□□□□□□□□ □□□□□□□□□□□□■□■□□□□□□□ □□□□□□□□□□□□□■□■□□□□□□ ■■□□□□□□□□□□□■□□■□□□■■ ■■□□□□□□□□□□□■□■□□□□■■ □□□□□□□□□□□□■□■□□□□□□□ □□□□□□□□□□□□■□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□■□□□□□□□□ □□□□□□□□□□□□■□■□□□□□□□ □□□□□□□□□□□□■■□■□□□□□□ ■■□□□□□□□□□□■■□■■□□□■■ ■■□□□□□□□□□□■■□■□□□□■■ □□□□□□□□□□□□■□■□□□□□□□ □□□□□□□□□□□□□■□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□■□□□□□□□□ □□□□□□□□□□□□■□■□□□□□□□ □□□□□□□□□□□■□□□■■□□□□□ ■■□□□□□□□□□■□□□■■□□□■■ ■■□□□□□□□□□■□□□■■□□□■■ □□□□□□□□□□□□■□■□□□□□□□ □□□□□□□□□□□□□■□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□■□□□□□□□□ □□□□□□□□□□□□■■■■□□□□□□ □□□□□□□□□□□■■□■□■□□□□□ ■■□□□□□□□□■■■□■□□■□□■■ ■■□□□□□□□□□■■□■□■□□□■■ □□□□□□□□□□□□■■■■□□□□□□ □□□□□□□□□□□□□■□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□■■□□□□□□□□ □□□□□□□□□□□■□□□■□□□□□□ □□□□□□□□□□■□□□□□■□□□□□ ■■□□□□□□□□■□□□■□■■□□■■ ■■□□□□□□□□■□□□□□■□□□■■ □□□□□□□□□□□■□□□■□□□□□□ □□□□□□□□□□□□■■□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□■□□□□□□□□□ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□□■■□□□□■■□□□□ ■■□□□□□□□■■■□□□□■■□□■■ ■■□□□□□□□□■■□□□□■■□□■■ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□□□□■□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□□■□■□□□□□□□□□ □□□□□□□□□■□□□□□□■■□□□□ ■■□□□□□□□■□□■□□■□□■□■■ ■■□□□□□□□■□□□□□□■■□□■■ □□□□□□□□□□■□■□□□□□□□□□ □□□□□□□□□□□■■□□□□□□□□□ □□□□□□□□□□□□□□□□□□□□□□
セルの定義
- 0:死
- 1:生
としてセルの生死を表現してます。
それらをListにつめ、さらにそのListをListにつめて、2次元配列の様にデータを保持し、運用してます。
対象のセルの前後左右のセルの値を集計し、ルールに当てはめて、対象のセルが生→死(あるいはその逆)となった場合は、Listのupdateメソッドで値の更新を行っています。
テンプレート
代表的な物体をテンプレートとして定義しています。
その他
Scalaの勉強の一環で書いてみたので、「ここをこうしたら良いよ」といったご意見ありましたら、どしどしお寄せ下さい!
次は自由にセルを置ける様に、Play+WebSocketで作ってみようかな〜