現在一部の記事の写真がリンク切れを起こしています。申し訳ありません。(そのうちどうにかします。)

Go言語とgo-glでライフゲーム

Go言語とgo-glでライフゲーム

f:id:witalosk:20190505140833p:plain
突然Go言語に興味が出てきたので学習しながらいろいろやってみます.
よってプロから見るとダメダメなコードを書いている可能性が大です.悪しからず.

そもそもGo言語とは

Googleが開発したコンパイルするタイプの言語(≠スクリプト言語)です.
文法はめっちゃシンプル.並列処理に強くて高速らしい.

ビルドして実行するときはコンソールで

go run hoge.go

と叩く.

文法

「パッケージ」というものを最初に宣言する.必ずmainパッケージがある必要がある.
プログラムはmainパッケージの中のmain関数から実行される.

変数

varで宣言できる.string, int, float64, bool, nilのデータ型がある.
宣言方法はいろいろ.そして1文字目に
1 文字目が小文字の場合はそのパッケージだけで見える変数(private)で,
大文字の場合は他のパッケージからも見える変数(public).

var hoge int
var hoge2 = 2 // 型名は省略可能
hoge3 := 3 // varも省略可能
var a, b int // 同時に定義して
a, b = 1, 100 // 同時に代入
var (
x = 123
y = "hogey"
) // 複数を同時に
// 配列
var arr [5]int
arr := [3]int{1, 3, 6}

「スライス」という便利機能があるらしい.

定数

constで定義する.

const {
hoge = 1
hoge2 = 2
}

関数

func hogehoge()で宣言.引数と戻り値の方は絶対指定しなければならない.

package main
import "fmt"
func hoge () {
fmt.Println("Hello, world!")
}
func hoge2 (text string) {
fmt.Println(text)
}
func hoge3 (i int) int {
return i*i
}
// 複数の戻り値を与えられる
func hoge4(a int, b int) (int, int) {
return b, a
}

繰り返し・条件分岐

繰り返しはfor文オンリー.while的に使うことも可能.

for i := 0; i < 5; i++ {
if i == 0 {
// hoge
} else if i == 4 {
// hogehoge
break
}
}
// while的
n := 0
for n < 5 {
// hoge
n++
}

GolangOpenGL

さてGo言語で画面描画をしようとするとやっぱりOpenGL
Go用のものは以下です.
github.com
Goではコマンドからサクッとパッケージをインストールできます.( 楽! )
go-glの最新版だといろいろ変わっているので,2,1にしました(甘え)

 $ go get -u github.com/go-gl/gl/v2.1/gl
$ go get -u github.com/go-gl/glfw/v3.2/glfw

今回はglfwもいっしょにインストール.

円を描画

package main
import "github.com/go-gl/gl/v2.1/gl"
import "github.com/go-gl/glfw/v3.2/glfw"
import "log"
import "math"
import "runtime"
func init() {
runtime.LockOSThread()
}
func main() {
windowWidth := 640 //!< 横幅
windowHeight := 640 //!< 縦幅
// glfwを初期化
if err := glfw.Init(); err != nil {
log.Fatalln("[Error] Failed to initialize glfw:", err)
}
defer glfw.Terminate() // main関数が閉じられるときに呼び出される
// Windowを作る
glfw.WindowHint(glfw.Resizable, glfw.False)
glfw.WindowHint(glfw.ContextVersionMajor, 2)
glfw.WindowHint(glfw.ContextVersionMinor, 1)
window, err := glfw.CreateWindow(windowWidth, windowHeight, "GL Test", nil, nil)
if err != nil {
panic(err) // 強制的に終了
}
window.MakeContextCurrent()
// GLの初期化
if err := gl.Init(); err != nil {
panic(err) // 強制的に終了
}
for !window.ShouldClose() { // ウィンドウを閉じるまで繰り返す
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
r := 0.5 //!< 半径
n := 100 //!< 円の分割数
// 円を描く
gl.Begin(gl.POLYGON)
gl.Color4f(0.2, 0.3, 0.7, 1.0)  // 円の色(RGBA)
// 円を描画
for i := 0; i < n; i++ {
x := r * math.Cos(2.0 * 3.14 * (float64(i)/float64(n)))
y := r * math.Sin(2.0 * 3.14 * (float64(i)/float64(n)))
gl.Vertex3f(float32(x), float32(y), 0.0) // 頂点座標
}
gl.End();
gl.Flush();  
window.SwapBuffers()
glfw.PollEvents()
}
}

実行結果:
f:id:witalosk:20190415152600p:plain
やったね!描画できたよ!!

ライフゲームを作る

ライフゲーム (Conway’s Game of Life[1]) は1970年にイギリスの数学者ジョン・ホートン・コンウェイ (John Horton Conway) が考案した生命の誕生、進化、淘汰などのプロセスを簡易的なモデルで再現したシミュレーションゲームである。
ライフゲーム – Wikipedia

  • 誕生 – 死んでいるセルに隣接する生きたセルがちょうど3つあれば、次の世代が誕生する。
  • 生存 – 生きているセルに隣接する生きたセルが2つか3つならば、次の世代でも生存する。
  • 過疎 – 生きているセルに隣接する生きたセルが1つ以下ならば、過疎により死滅する。
  • 過密 – 生きているセルに隣接する生きたセルが4つ以上ならば、過密により死滅する。
package main
import "github.com/go-gl/gl/v2.1/gl"
import "github.com/go-gl/glfw/v3.2/glfw"
import "log"
//import "math"
import "runtime"
var windowWidth = 1305 //!< 横幅
var windowHeight = 1305 //!< 縦幅
var n = 32 //!< セルサイズ
var cells [32][32]int //!< セルの配列
var prevCells [32][32]int //!< 前ステップのセルの配列
var count = 0 //!< フレームカウント
func init() {
runtime.LockOSThread()
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
cells[i][j] = 0
prevCells[i][j] = 0
}
}
//penta(10,15)
//penta(5,5)
//glider(25,20)
hansyoku(15,15)
}
func penta(x int, y int) {
cells[x][y] = 1
cells[x+3][y] = 1
cells[x+5][y] = 1
cells[x+6][y] = 1
cells[x+8][y] = 1
cells[x+11][y] = 1
cells[x][y+1] = 1
cells[x+1][y+1] = 1
cells[x+2][y+1] = 1
cells[x+3][y+1] = 1
cells[x+5][y+1] = 1
cells[x+6][y+1] = 1
cells[x+8][y+1] = 1
cells[x+9][y+1] = 1
cells[x+10][y+1] = 1
cells[x+11][y+1] = 1
cells[x][y+2] = 1
cells[x+3][y+2] = 1
cells[x+5][y+2] = 1
cells[x+6][y+2] = 1
cells[x+8][y+2] = 1
cells[x+11][y+2] = 1
}
func glider(x int, y int) {
cells[x][y] = 1
cells[x][y+1] = 1
cells[x][y+2] = 1
cells[x+1][y] = 1
cells[x+2][y+1] = 1
}
func hansyoku(x int, y int) {
cells[x][y] = 1
cells[x+1][y] = 1
cells[x+2][y] = 1
cells[x+4][y] = 1
cells[x][y+1] = 1
cells[x+3][y+2] = 1
cells[x+4][y+2] = 1
cells[x+1][y+3] = 1
cells[x+2][y+3] = 1
cells[x+4][y+3] = 1
cells[x][y+4] = 1
cells[x+2][y+4] = 1
cells[x+4][y+4] = 1
}
func display() {
prevCells = cells
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
// セルをカウント
num := 0
if i-1>=0 && j-1>=0 {num += prevCells[i-1][j-1]}
if j-1>=0 {num += prevCells[i][j-1]}
if i+1<n && j-1>=0 {num += prevCells[i+1][j-1]}
if i-1>=0 {num += prevCells[i-1][j]}
if i+1<n {num += prevCells[i+1][j]}
if i-1>=0 && j+1<n {num += prevCells[i-1][j+1]}
if j+1<n {num += prevCells[i][j+1]}
if i+1<n && j+1<n {num += prevCells[i+1][j+1]}
// セルを設定
if num <=1 {
cells[i][j] = 0
} else if num <= 2 {
if prevCells[i][j] == 1 {
cells[i][j] = 1
} else {
cells[i][j] = 0
}
} else if num == 3 {
cells[i][j] = 1
} else {
cells[i][j] = 0
}
// 色を設定
if cells[i][j] == 1{
gl.Color4f(0.2, 0.3, 0.7, 1.0)
} else {
gl.Color4f(0.1, 0.1, 0.1, 1.0)
}
gl.PushMatrix()
gl.Translatef(float32(i)*float32(windowWidth/n), float32(j)*float32(windowHeight/n), 0.0)
rect(10.0)
gl.PopMatrix()
}
}
gl.Flush();  
count = count + 1
}
func rect(ext float32) {
gl.Begin(gl.POLYGON)
gl.Vertex3f(-float32(ext), float32(ext), 0.0)
gl.Vertex3f(float32(ext), float32(ext), 0.0)
gl.Vertex3f(float32(ext), -float32(ext), 0.0)
gl.Vertex3f(-float32(ext), -float32(ext), 0.0)
gl.End()
}
func main() {
// glfwを初期化
if err := glfw.Init(); err != nil {
log.Fatalln("[Error] Failed to initialize glfw:", err)
}
defer glfw.Terminate() // main関数が閉じられるときに呼び出される
// Windowを作る
glfw.WindowHint(glfw.Resizable, glfw.False)
glfw.WindowHint(glfw.ContextVersionMajor, 2)
glfw.WindowHint(glfw.ContextVersionMinor, 1)
window, err := glfw.CreateWindow(windowWidth, windowHeight, "Life Game", nil, nil)
if err != nil {
panic(err) // 強制的に終了
}
window.MakeContextCurrent()
// GLの初期化
if err := gl.Init(); err != nil {
panic(err) // 強制的に終了
}
// 座標変換 (画面幅*画面幅の座標にする)
gl.Scalef(2.0/float32(windowWidth), 2.0/float32(windowHeight), 1.0)
gl.Translatef(-float32(windowWidth)/2.0 + 25.0, -float32(windowHeight)/2.0 + 25.0, 0.0)
// タイマーのセッティング
fps, currentTime, lastTime, elapsedTime := 4.0, 0.0, 0.0, 0.0
glfw.SetTime(0.0)
// メインループ
for !window.ShouldClose() { // ウィンドウを閉じるまで繰り返す
// タイマー
currentTime = glfw.GetTime()
elapsedTime = currentTime - lastTime
if(elapsedTime >= 1.0/fps){
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
display()
window.SwapBuffers()
glfw.PollEvents()
lastTime = glfw.GetTime()
}
}
}

実行結果

f:id:witalosk:20190505141810p:plain

未分類カテゴリの最新記事