123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- // SPDX-License-Identifier: Apache-2.0
- // SPDX-FileCopyrightText: 2015 Martin Lindhe
- // SPDX-FileCopyrightText: 2016 The Ebitengine Authors
- // The original project is gol (https://github.com/martinlindhe/gol) by Martin Lindhe.
- package main
- import (
- "log"
- "math/rand/v2"
- "github.com/hajimehoshi/ebiten/v2"
- )
- // World represents the game state.
- type World struct {
- area []bool
- width int
- height int
- }
- // NewWorld creates a new world.
- func NewWorld(width, height int, maxInitLiveCells int) *World {
- w := &World{
- area: make([]bool, width*height),
- width: width,
- height: height,
- }
- w.init(maxInitLiveCells)
- return w
- }
- // init inits world with a random state.
- func (w *World) init(maxLiveCells int) {
- for i := 0; i < maxLiveCells; i++ {
- x := rand.IntN(w.width)
- y := rand.IntN(w.height)
- w.area[y*w.width+x] = true
- }
- }
- // Update game state by one tick.
- func (w *World) Update() {
- width := w.width
- height := w.height
- next := make([]bool, width*height)
- for y := 0; y < height; y++ {
- for x := 0; x < width; x++ {
- pop := neighbourCount(w.area, width, height, x, y)
- switch {
- case pop < 2:
- // rule 1. Any live cell with fewer than two live neighbours
- // dies, as if caused by under-population.
- next[y*width+x] = false
- case (pop == 2 || pop == 3) && w.area[y*width+x]:
- // rule 2. Any live cell with two or three live neighbours
- // lives on to the next generation.
- next[y*width+x] = true
- case pop > 3:
- // rule 3. Any live cell with more than three live neighbours
- // dies, as if by over-population.
- next[y*width+x] = false
- case pop == 3:
- // rule 4. Any dead cell with exactly three live neighbours
- // becomes a live cell, as if by reproduction.
- next[y*width+x] = true
- }
- }
- }
- w.area = next
- }
- // Draw paints current game state.
- func (w *World) Draw(pix []byte) {
- for i, v := range w.area {
- if v {
- pix[4*i] = 0xff
- pix[4*i+1] = 0xff
- pix[4*i+2] = 0xff
- pix[4*i+3] = 0xff
- } else {
- pix[4*i] = 0
- pix[4*i+1] = 0
- pix[4*i+2] = 0
- pix[4*i+3] = 0
- }
- }
- }
- // neighbourCount calculates the Moore neighborhood of (x, y).
- func neighbourCount(a []bool, width, height, x, y int) int {
- c := 0
- for j := -1; j <= 1; j++ {
- for i := -1; i <= 1; i++ {
- if i == 0 && j == 0 {
- continue
- }
- x2 := x + i
- y2 := y + j
- if x2 < 0 || y2 < 0 || width <= x2 || height <= y2 {
- continue
- }
- if a[y2*width+x2] {
- c++
- }
- }
- }
- return c
- }
- const (
- screenWidth = 320
- screenHeight = 240
- )
- type Game struct {
- world *World
- pixels []byte
- }
- func (g *Game) Update() error {
- g.world.Update()
- return nil
- }
- func (g *Game) Draw(screen *ebiten.Image) {
- if g.pixels == nil {
- g.pixels = make([]byte, screenWidth*screenHeight*4)
- }
- g.world.Draw(g.pixels)
- screen.WritePixels(g.pixels)
- }
- func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
- return screenWidth, screenHeight
- }
- func main() {
- g := &Game{
- world: NewWorld(screenWidth, screenHeight, int((screenWidth*screenHeight)/10)),
- }
- ebiten.SetWindowSize(screenWidth*2, screenHeight*2)
- ebiten.SetWindowTitle("Game of Life (Ebitengine Demo)")
- if err := ebiten.RunGame(g); err != nil {
- log.Fatal(err)
- }
- }
|