Files
golsky/main.go

154 lines
3.5 KiB
Go
Raw Normal View History

2024-05-20 20:19:11 +02:00
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)
}
}