From b03e2d57e9dce281a4adafa7cb536d8db213e16e Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Mon, 20 May 2024 20:19:11 +0200 Subject: [PATCH] works --- .gitignore | 1 + go.mod | 15 ++++++ go.sum | 18 +++++++ main.go | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 .gitignore create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d47fe14 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +gameoflife diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..410cbde --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module gameoflife + +go 1.22 + +require github.com/hajimehoshi/ebiten/v2 v2.7.3 + +require ( + github.com/alecthomas/repr v0.4.0 // indirect + github.com/ebitengine/gomobile v0.0.0-20240329170434-1771503ff0a8 // indirect + github.com/ebitengine/hideconsole v1.0.0 // indirect + github.com/ebitengine/purego v0.7.0 // indirect + github.com/jezek/xgb v1.1.1 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.18.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..a27cc0a --- /dev/null +++ b/go.sum @@ -0,0 +1,18 @@ +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/ebitengine/gomobile v0.0.0-20240329170434-1771503ff0a8 h1:5e8X7WEdOWrjrKvgaWF6PRnDvJicfrkEnwAkWtMN74g= +github.com/ebitengine/gomobile v0.0.0-20240329170434-1771503ff0a8/go.mod h1:tWboRRNagZwwwis4QIgEFG1ZNFwBJ3LAhSLAXAAxobQ= +github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= +github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= +github.com/ebitengine/purego v0.7.0 h1:HPZpl61edMGCEW6XK2nsR6+7AnJ3unUxpTZBkkIXnMc= +github.com/ebitengine/purego v0.7.0/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= +github.com/hajimehoshi/ebiten/v2 v2.7.3 h1:lDpj8KbmmjzwD19rsjXNkyelicu0XGvklZW6/tjrgNs= +github.com/hajimehoshi/ebiten/v2 v2.7.3/go.mod h1:1vjyPw+h3n30rfTOpIsbWRXSxZ0Oz1cYc6Tq/2DKoQg= +github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= +github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= +golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= +golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..9f2df34 --- /dev/null +++ b/main.go @@ -0,0 +1,153 @@ +package main + +import ( + "image/color" + "log" + "math/rand" + + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/vector" +) + +type Grid struct { + Data [][]int +} + +type Game struct { + Grids []*Grid // 2 grids: one current, one next + Index int // points to current grid + Width, Height, Cellsize int + ScreenWidth, ScreenHeight int + Black, White color.RGBA +} + +func (game *Game) Layout(outsideWidth, outsideHeight int) (int, int) { + return game.ScreenWidth, game.ScreenHeight +} + +func (game *Game) Update() error { + // compute cells + next := game.Index ^ 1 // next grid index, we just xor 0|1 to 1|0 + + for y := 0; y < game.Height; y++ { + for x := 0; x < game.Width; x++ { + state := game.Grids[game.Index].Data[y][x] // 0|1 == dead or alive + neighbors := CountNeighbors(game, x, y) // alive neighbor count + var nextstate int + + // the actual game of life rules + if state == 0 && neighbors == 3 { + nextstate = 1 + } else if state == 1 && (neighbors < 2 || neighbors > 3) { + nextstate = 0 + } else { + nextstate = state + } + + // change state of current cell in next grid + game.Grids[next].Data[y][x] = nextstate + } + } + + // switch grid for rendering + game.Index ^= 1 + + return nil +} + +func (game *Game) Draw(screen *ebiten.Image) { + for y := 0; y < game.Height; y++ { + for x := 0; x < game.Width; x++ { + currentcolor := game.White + if game.Grids[game.Index].Data[y][x] == 1 { + currentcolor = game.Black + } + + vector.DrawFilledRect(screen, + float32(x*game.Cellsize), + float32(y*game.Cellsize), + float32(game.Cellsize), + float32(game.Cellsize), + currentcolor, false) + + if currentcolor == game.White { + // draw black + vector.DrawFilledRect(screen, + float32(x*game.Cellsize), + float32(y*game.Cellsize), + float32(game.Cellsize), + float32(game.Cellsize), + game.Black, false) + // then fill with 1px lesser rect in white + // thus creating grid lines + vector.DrawFilledRect(screen, + float32(x*game.Cellsize+1), + float32(y*game.Cellsize+1), + float32(game.Cellsize-1), + float32(game.Cellsize-1), + game.White, false) + } + } + } +} + +func (game *Game) Init() { + // setup the game + game.ScreenWidth = game.Cellsize * game.Width + game.ScreenHeight = game.Cellsize * game.Height + + grid := &Grid{Data: make([][]int, game.Height)} + gridb := &Grid{Data: make([][]int, game.Height)} + + for y := 0; y < game.Height; y++ { + grid.Data[y] = make([]int, game.Width) + gridb.Data[y] = make([]int, game.Width) + for x := 0; x < game.Width; x++ { + grid.Data[y][x] = rand.Intn(2) + } + } + + game.Grids = []*Grid{ + grid, + gridb, + } + + game.Black = color.RGBA{0, 0, 0, 0xff} + game.White = color.RGBA{0xff, 0xff, 0xff, 0xff} + + game.Index = 0 +} + +func CountNeighbors(game *Game, x, y int) int { + sum := 0 + + // so we look ad all 8 neighbors surrounding us. In case we are on + // an edge, then we'll look at the neighbor on the other side of + // the grid, thus wrapping lookahead around. + for i := -1; i < 2; i++ { + for j := -1; j < 2; j++ { + col := (x + i + game.Width) % game.Width + row := (y + j + game.Height) % game.Height + sum += game.Grids[game.Index].Data[row][col] + } + } + + // don't count ourselfes though + sum -= game.Grids[game.Index].Data[y][x] + + return sum +} + +func main() { + game := &Game{Width: 180, Height: 160, Cellsize: 15} + game.Init() + + ebiten.SetWindowSize(game.ScreenWidth, game.ScreenHeight) + ebiten.SetWindowTitle("Game of life") + ebiten.SetTPS(30) + + if err := ebiten.RunGame(game); err != nil { + log.Fatal(err) + } + +}