10
3

More than 3 years have passed since last update.

Goでライフゲームを実装してみた

Last updated at Posted at 2019-12-12

こちらは Go4 Advent Calendar 2019 12日目の記事です。


この記事ではGoでライフゲームを実装したコードの紹介とその解説をおこないます。

Motivation

最近Twitter上でセル・オートマトンというのを発見しまして、言葉では表現しづらいのですが、物凄く心に響くものがありました。ロマンを感じた。

自分もやってみたいなと思い調べてみると、代表的な実装例としてライフゲーム(Game of Life)があるというのを知りました。

すでにGoを含め色々な言語で実装したものがネット上には溢れていたのでN番煎じではありますが、やりたいのでやってみました。

Output

Repository: mqtsuo02/go-game-of-life

lifegame4.gif

Design

CLIの動作は下記のような流れとしました。

RUN→セルをランダムに初期化→輪廻転生→任意のキー押下で終了

Code


main.go (click to open)
main.go
package main

import (
    "bufio"
    "fmt"
    "math/rand"
    "os"
    "time"
)

const (
    width    = 60
    height   = 20
    lifespan = 100
    duration = 100
    clear    = "\033[2J"
    head     = "\033[1;1H"
)

type cells [width + 2][height + 2]int

var cs, ncs cells

func main() {
    initialize()
    for i := 0; i < lifespan; i++ {
        render()
        update()
        time.Sleep(100 * time.Millisecond)
    }
    end()
}

func initialize() {
    fmt.Print(clear)
    rand.Seed(time.Now().UnixNano())
    for y := 1; y < height+1; y++ {
        for x := 1; x < width+1; x++ {
            cs[x][y] = rand.Intn(2)
        }
    }
}

func render() {
    var screen string
    for y := 0; y < height+2; y++ {
        for x := 0; x < width+2; x++ {
            c := " "
            if cs[x][y] == 1 {
                c = "▉"
            }
            screen += c
        }
        screen += "\n"
    }
    fmt.Print(head)
    fmt.Print(screen)
}

func update() {
    for y := 1; y < height+1; y++ {
        for x := 1; x < width+1; x++ {
            ncs[x][y] = 0
            cnt := cs[x-1][y-1] + cs[x][y-1] + cs[x+1][y-1] + cs[x-1][y] + cs[x+1][y] + cs[x-1][y+1] + cs[x][y+1] + cs[x+1][y+1]
            if cnt == 2 && cs[x][y] == 1 || cnt == 3 {
                ncs[x][y] = 1
            }
        }
    }
    cs = ncs
}

func end() {
    fmt.Print("Press any key to end. ")
    bufio.NewScanner(os.Stdin).Scan()
    fmt.Print(clear)
    fmt.Print(head)
}

設計にあたっては下記のサイトを参考にさせていただきました。

Explanation

処理の流れとしては以下です。

  • 初期化
  • forループ
    • 描画
    • 状態判定
    • スリープ
  • 入力待ち
  • 終了

初期化

初期化ではターミナルのクリアと二次元配列にランダムで 0 | 1 を代入する処理を行っています。

描画

描画では二次元配列の内容をもとに文字列を生成し、ターミナルのカーソルを頭に合わせたうえでfmt.Printしている感じです。

状態判定

状態判定では対象セルの周囲のセルをカウントして、それに合わせて次の状態を決定しています。ここで集計しやすいように二次元配列の型をintにしています。(はじめはboolでやっていた)

まだやってないことリスト

  • 世界を上下左右でぶったぎっているので、繋げてあげたい
  • 文字列で表現しているのでセルが縦長。図形的に解決したい
  • ライフゲームでおなじみのグライダー銃を打つ機能を追加したい

Thank you

以上です。ここまで読んでいただき、ありがとうございました。

これをきっかけにセル・オートマトンの世界を楽しんでいきたいと思います。


2019年のアドベントカレンダーは3つ参加してます。よかったら他の記事も見てください。

10
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
3