Last active
April 24, 2023 10:06
-
-
Save joseche/efdff0b1c8ac3fa1a9bb1a049205b418 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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