Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 16481] ファイルの概要

このコミットは、Go言語の公式リポジトリにおける変更であり、Go Playgroundに「Conway's Game of Life(コンウェイのライフゲーム)」のサンプルコードを追加するものです。具体的には、doc/play/life.goという新しいファイルとしてライフゲームの実装が追加され、doc/root.htmlが更新されて、Go Playgroundのサンプルプログラム選択リストにこの新しいライフゲームが追加されています。

コミット

commit 850d1026adfb0546bd0a6491ee95890257454282
Author: Andrew Gerrand <adg@golang.org>
Date:   Tue Jun 4 12:59:28 2013 +1000

    doc: add Game of Life to playground toys
    
    R=golang-dev, dsymonds, r
    CC=golang-dev
    https://golang.org/cl/9961043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/850d1026adfb0546bd0a6491ee95890257454282

元コミット内容

doc: add Game of Life to playground toys

R=golang-dev, dsymonds, r
CC=golang-dev
https://golang.org/cl/9961043

変更の背景

このコミットの背景には、Go Playgroundの機能強化と、Go言語の学習者や開発者に対して、より多様で興味深いサンプルコードを提供したいという意図があります。Go Playgroundは、Go言語のコードをブラウザ上で簡単に実行・共有できる非常に便利なツールです。ここにConway's Game of Lifeのような古典的かつ視覚的に魅力的なアルゴリズムを追加することで、Go言語の並行処理能力や構造体、メソッドといった基本的な概念を、実践的な例を通して学ぶ機会を提供しています。特に、ライフゲームはシンプルなルールから複雑なパターンが生成されることで知られており、プログラミングの学習題材としても非常に人気があります。

前提知識の解説

Conway's Game of Life(コンウェイのライフゲーム)

コンウェイのライフゲームは、1970年にイギリスの数学者ジョン・ホートン・コンウェイによって考案された、セル・オートマトンの一種です。これは「生命の誕生、生存、死」をシミュレートするもので、非常にシンプルなルールに基づいて、セルの状態が時間とともに変化していく様子を観察します。

ライフゲームの基本ルール:

  1. 誕生: 死んでいるセルに隣接する生きたセルがちょうど3つあれば、そのセルは誕生(生きた状態になる)。
  2. 生存: 生きているセルに隣接する生きたセルが2つまたは3つならば、そのセルは生存(生きた状態を維持)。
  3. 過疎: 生きているセルに隣接する生きたセルが1つ以下ならば、そのセルは過疎により死亡。
  4. 過密: 生きているセルに隣接する生きたセルが4つ以上ならば、そのセルは過密により死亡。

これらのルールは、各セルに同時に適用され、世代が進行するごとにグリッド上のパターンが変化していきます。初期状態によっては、静止するパターン(静物)、移動するパターン(移動物体)、周期的に変化するパターン(振動子)など、様々な興味深い挙動が見られます。

Go Playground

Go Playgroundは、Go言語の公式ウェブサイト(golang.org)で提供されているオンラインツールです。ユーザーはブラウザ上でGoのコードを記述し、コンパイルして実行することができます。主な特徴は以下の通りです。

  • 手軽な実行環境: Go言語の環境をローカルにセットアップすることなく、すぐにコードを試すことができます。
  • コードの共有: 記述したコードはURLとして共有できるため、他の人と簡単にコードを共有したり、質問したりする際に便利です。
  • 標準ライブラリの利用: Goの標準ライブラリのほとんどを利用できます。
  • 制限: セキュリティ上の理由から、ネットワークアクセスやファイルシステムへのアクセスなど、一部の機能には制限があります。また、実行時間やメモリ使用量にも制限があります。

Go Playgroundは、Go言語の学習、プロトタイピング、短いコードスニペットのテストなどに広く利用されています。

技術的詳細

このコミットで追加されたライフゲームの実装は、Go言語の基本的なデータ構造とメソッドを効果的に利用しています。

  1. Field 構造体:

    • s [][]bool: ライフゲームのグリッドを表す二次元のスライスです。trueが生きたセル、falseが死んだセルを示します。
    • w, h int: グリッドの幅と高さを表します。
    • NewField(w, h int) *Field: 指定された幅と高さで新しい空のフィールドを初期化するコンストラクタ関数です。
    • Set(x, y int, b bool): 指定された座標のセルの状態を設定します。
    • Alive(x, y int) bool: 指定された座標のセルが生きているかどうかを返します。このメソッドの重要な特徴は、トーラス(toroidal)境界条件を実装している点です。これは、グリッドの端が反対側の端に繋がっているかのように振る舞うことを意味します。例えば、右端のセルの右隣は左端のセルになり、下端のセルの下隣は上端のセルになります。これにより、パターンがグリッドの端で消滅することなく、連続的に動き続けることができます。実装では、x += f.w; x %= f.w; y += f.h; y %= f.h; という算術演算によって、座標をグリッド内にラップアラウンドさせています。
    • Next(x, y int) bool: 指定された座標のセルが次の世代でどのような状態になるかを計算します。これはライフゲームのルールに基づいて、周囲8つの隣接セルの生きた数を数え、それに応じてセルの次の状態を決定します。
  2. Life 構造体:

    • a, b *Field: ライフゲームの現在の状態と次の状態を保持するための2つのFieldポインタです。これは、次の世代の状態を計算する際に、現在の世代の状態を破壊しないようにするための一般的なテクニック(ダブルバッファリング)です。
    • w, h int: グリッドの幅と高さ。
    • NewLife(w, h int) *Life: 新しいライフゲームの状態を初期化し、ランダムな初期状態を生成します。グリッドの約1/4のセルをランダムに生きた状態に設定します。
    • Step(): ゲームを1ステップ進めます。現在のフィールドaの状態から次のフィールドbの状態を計算し、その後abをスワップすることで、bが次の現在の状態となります。
    • String() string: 現在のゲームボードの状態を文字列として整形して返します。生きたセルは*、死んだセルは (スペース)で表現され、各行の終わりに改行が追加されます。
  3. main 関数:

    • l := NewLife(40, 15): 幅40、高さ15の新しいライフゲームインスタンスを作成します。
    • for i := 0; i < 300; i++: 300世代にわたってゲームをシミュレートします。
    • l.Step(): ゲームを1世代進めます。
    • fmt.Print("\x0c", l): 画面をクリアし(\x0cはフォームフィード文字で、多くのターミナルで画面クリアとして解釈されます)、現在のゲームボードの状態を表示します。lString()メソッドを実装しているため、fmt.Printに直接渡すことができます。
    • time.Sleep(time.Second / 30): 各世代の表示間に約30ミリ秒の遅延を入れ、アニメーションのように見えるようにします。

この実装は、Go言語の構造体、メソッド、スライス、そして標準ライブラリ(bytes, fmt, math/rand, time)の基本的な使い方を示す良い例となっています。特に、Aliveメソッドにおけるトーラス境界条件の実装や、Stepメソッドにおけるダブルバッファリングの利用は、効率的かつ正確なライフゲームシミュレーションのための重要なテクニックです。

コアとなるコードの変更箇所

このコミットによる変更は主に以下の2つのファイルにあります。

  1. doc/play/life.go (新規追加)

    • このファイルは、Conway's Game of LifeのGo言語による完全な実装を含んでいます。
    • Field構造体とそのメソッド(NewField, Set, Alive, Next)が定義されています。
    • Life構造体とそのメソッド(NewLife, Step, String)が定義されています。
    • main関数が含まれており、ライフゲームのシミュレーションを実行し、結果をコンソールに出力します。
  2. doc/root.html (変更)

    • Go PlaygroundのウェブインターフェースのHTMLファイルです。
    • <select>タグ内に新しい<option>要素が追加され、life.goがGo Playgroundのサンプルプログラムの選択肢として表示されるようになりました。
    --- a/doc/root.html
    +++ b/doc/root.html
    @@ -31,6 +31,7 @@ Hello, 世界
     <div class="toys">
     <select>
     	<option value="hello.go">Hello, World!</option>
    +	<option value="life.go">Conway's Game of Life</option>
     	<option value="fib.go">Fibonacci Closure</option>
     	<option value="peano.go">Peano Integers</option>
     	<option value="pi.go">Concurrent pi</option>
    

コアとなるコードの解説

doc/play/life.go

このファイルは、ライフゲームのロジックを完全にカプセル化したものです。

  • Field構造体と関連メソッド:

    • Fieldはゲームボードの単一の世代の状態を保持します。
    • Aliveメソッドは、トーラス境界条件を適用することで、グリッドの端を越えても隣接セルを正しく参照できるようにします。これは、xy座標をグリッドの幅と高さで割った余りを取ることで実現されます。例えば、x = -1の場合、x += f.wxf.w - 1となり、x %= f.wf.w - 1のままとなり、グリッドの右端のセルを参照します。
    • Nextメソッドは、ライフゲームの4つの基本ルールを直接実装しています。隣接する生きたセルの数を数え、その数と現在のセルの状態に基づいて次の状態を決定します。
  • Life構造体と関連メソッド:

    • Lifeはゲーム全体の状態(現在のフィールドと次のフィールド)を管理します。
    • Stepメソッドは、ゲームの進行を司る中心的なロジックです。現在のフィールドaの各セルについてNextメソッドを呼び出し、その結果を次のフィールドbに設定します。全てのセルが計算された後、abをスワップすることで、bが新しい現在のフィールドとなり、aは次のステップで計算結果を格納するための空のフィールドとして再利用されます。このダブルバッファリングにより、世代間の状態遷移がスムーズかつ正確に行われます。
    • Stringメソッドは、fmt.PrintLifeオブジェクトを文字列として表示できるようにするためのGoの慣習的なインターフェース(Stringerインターフェース)の実装です。これにより、ゲームボードの視覚的な表現が容易になります。
  • main関数:

    • Go Playgroundで実行されるエントリポイントです。
    • NewLifeでゲームを初期化し、ループ内でStepと表示を繰り返すことで、ライフゲームのアニメーションを実現しています。time.Sleepは、アニメーションの速度を制御するために使用されます。

doc/root.html

この変更は非常にシンプルで、Go Playgroundのユーザーインターフェースに新しいオプションを追加するだけです。これにより、ユーザーはドロップダウンメニューから直接「Conway's Game of Life」を選択し、そのコードをGo Playgroundで実行できるようになります。これは、Go Playgroundが提供するサンプルコードの多様性を高め、ユーザーエクスペリエンスを向上させるための変更です。

関連リンク

参考にした情報源リンク