Skip to content

Instantly share code, notes, and snippets.

@joseche
Last active April 24, 2023 10:06
Show Gist options
  • Select an option

  • Save joseche/efdff0b1c8ac3fa1a9bb1a049205b418 to your computer and use it in GitHub Desktop.

Select an option

Save joseche/efdff0b1c8ac3fa1a9bb1a049205b418 to your computer and use it in GitHub Desktop.
package main
// https://ebitengine.org/en/examples/life.html
import (
"fmt"
"math/rand"
"strconv"
"time"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/hajimehoshi/ebiten/v2/vector"
"golang.org/x/image/colornames"
)
const TITLE = "Game of Life written in Go!"
const (
CELL_STATE_ALIVE = 10
CELL_STATE_JUST_DIED = 9
CELL_STATE_DEAD = 8
)
var (
CELL_STATE_ALIVE_COLOR = colornames.Green
CELL_STATE_JUST_DIED_COLOR = colornames.Yellow
CELL_STATE_DEAD_COLOR = colornames.Black
GRID_COLOR = colornames.Grey
)
const (
CELL_COLS = 128
CELL_ROWS = 128
)
const drawGrid = true
var (
SCREEN_WIDTH = 1024
SCREEN_HEIGHT = 768
)
var (
PIXELS_BY_CELL_X = float32(SCREEN_WIDTH / CELL_COLS)
PIXELS_BY_CELL_Y = float32(SCREEN_HEIGHT / CELL_ROWS)
)
type Game struct {
cells []byte
random_source *rand.Rand
}
func newGame() *Game {
return &Game{
cells: make([]byte, CELL_COLS*CELL_ROWS),
random_source: rand.New(rand.NewSource(time.Now().UnixMicro() % 1024)),
}
}
func address(col, row int) int {
return row*CELL_COLS + col
}
func (g *Game) RandomizeWorld() {
for x := 0; x < CELL_COLS; x++ {
for y := 0; y < CELL_ROWS; y++ {
fmt.Println(x, y)
cellAddr := address(x, y)
fmt.Println(cellAddr)
rndn := g.random_source.Intn(100)
if rndn >= 50 {
g.cells[cellAddr] = CELL_STATE_ALIVE
}
}
}
}
func cellStateChange(prev byte, neighbours int) byte {
switch {
case neighbours < 2:
if prev == CELL_STATE_ALIVE {
return CELL_STATE_JUST_DIED
}
case (neighbours == 2 || neighbours == 3) && prev == CELL_STATE_ALIVE:
return CELL_STATE_ALIVE
case neighbours > 3:
if prev == CELL_STATE_ALIVE {
return CELL_STATE_JUST_DIED
}
case neighbours == 3:
return CELL_STATE_ALIVE
}
return CELL_STATE_DEAD
}
func (g *Game) WorldStep() {
newCols := make([]byte, CELL_COLS*CELL_ROWS)
for x := 0; x < CELL_COLS; x++ {
for y := 0; y < CELL_ROWS; y++ {
cellAddr := address(x, y)
c := neighbourCount(g.cells, x, y, CELL_COLS, CELL_ROWS)
prevState := g.cells[cellAddr]
newCols[cellAddr] = cellStateChange(prevState, c)
}
}
copy(g.cells, newCols)
}
func neighbourCount(cells []byte, x, y, x_max, y_max int) int {
c := 0
for i := -1; i <= 1; i++ {
for j := -1; j <= 1; j++ {
if i == 0 && j == 0 {
continue
}
x2 := x + i
y2 := y + j
if x2 < 0 {
x2 = x_max - 1
}
if y2 < 0 {
y2 = y_max - 1
}
if x2 >= x_max {
x2 = 0
}
if y2 >= y_max {
y2 = 0
}
addr := address(x2, y2)
if cells[addr] == CELL_STATE_ALIVE {
c++
}
}
}
return c
}
func (g *Game) Update() error {
g.WorldStep()
fmt.Print("U")
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
fmt.Print("D")
var fsp string = strconv.FormatFloat(ebiten.ActualFPS(), 'f', 3, 32)
ebitenutil.DebugPrint(screen, fsp)
for x := 0; x < CELL_COLS; x++ {
for y := 0; y < CELL_ROWS; y++ {
cellAddr := address(x, y)
c := CELL_STATE_DEAD_COLOR
if g.cells[cellAddr] == CELL_STATE_ALIVE {
c = CELL_STATE_ALIVE_COLOR
} else if g.cells[cellAddr] == CELL_STATE_JUST_DIED {
c = CELL_STATE_JUST_DIED_COLOR
}
if drawGrid {
vector.StrokeLine(screen, float32(x)*PIXELS_BY_CELL_X, float32(y)*PIXELS_BY_CELL_Y, float32(x+1)*PIXELS_BY_CELL_X, float32(y)*PIXELS_BY_CELL_Y, 1, GRID_COLOR, false)
vector.StrokeLine(screen, float32(x)*PIXELS_BY_CELL_X, float32(y)*PIXELS_BY_CELL_Y, float32(x)*PIXELS_BY_CELL_X, float32(y+1)*PIXELS_BY_CELL_Y, 1, GRID_COLOR, false)
}
vector.DrawFilledRect(
screen,
float32(x)*PIXELS_BY_CELL_X,
float32(y)*PIXELS_BY_CELL_Y,
PIXELS_BY_CELL_X,
PIXELS_BY_CELL_Y,
c, false)
}
}
}
func (g *Game) Layout(outsideWidth, OutsideHeight int) (screenWidth, screenHeight int) {
// the game is automatically scaled
fmt.Print("L")
var x_max, y_max = ebiten.ScreenSizeInFullscreen()
PIXELS_BY_CELL_X = float32(x_max / CELL_COLS)
PIXELS_BY_CELL_Y = float32(y_max / CELL_ROWS)
return x_max, y_max
}
func main() {
ebiten.SetWindowTitle(TITLE)
ebiten.SetFullscreen(true)
var x_max, y_max = ebiten.ScreenSizeInFullscreen()
ebiten.SetWindowSize(x_max, y_max)
PIXELS_BY_CELL_X = float32(x_max / CELL_COLS)
PIXELS_BY_CELL_Y = float32(y_max / CELL_ROWS)
game := newGame()
game.RandomizeWorld()
if err := ebiten.RunGame(game); err != nil {
panic(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment