1
0

main.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // SPDX-License-Identifier: Apache-2.0
  2. // SPDX-FileCopyrightText: 2015 Martin Lindhe
  3. // SPDX-FileCopyrightText: 2016 The Ebitengine Authors
  4. // The original project is gol (https://github.com/martinlindhe/gol) by Martin Lindhe.
  5. package main
  6. import (
  7. "log"
  8. "math/rand/v2"
  9. "github.com/hajimehoshi/ebiten/v2"
  10. )
  11. // World represents the game state.
  12. type World struct {
  13. area []bool
  14. width int
  15. height int
  16. }
  17. // NewWorld creates a new world.
  18. func NewWorld(width, height int, maxInitLiveCells int) *World {
  19. w := &World{
  20. area: make([]bool, width*height),
  21. width: width,
  22. height: height,
  23. }
  24. w.init(maxInitLiveCells)
  25. return w
  26. }
  27. // init inits world with a random state.
  28. func (w *World) init(maxLiveCells int) {
  29. for i := 0; i < maxLiveCells; i++ {
  30. x := rand.IntN(w.width)
  31. y := rand.IntN(w.height)
  32. w.area[y*w.width+x] = true
  33. }
  34. }
  35. // Update game state by one tick.
  36. func (w *World) Update() {
  37. width := w.width
  38. height := w.height
  39. next := make([]bool, width*height)
  40. for y := 0; y < height; y++ {
  41. for x := 0; x < width; x++ {
  42. pop := neighbourCount(w.area, width, height, x, y)
  43. switch {
  44. case pop < 2:
  45. // rule 1. Any live cell with fewer than two live neighbours
  46. // dies, as if caused by under-population.
  47. next[y*width+x] = false
  48. case (pop == 2 || pop == 3) && w.area[y*width+x]:
  49. // rule 2. Any live cell with two or three live neighbours
  50. // lives on to the next generation.
  51. next[y*width+x] = true
  52. case pop > 3:
  53. // rule 3. Any live cell with more than three live neighbours
  54. // dies, as if by over-population.
  55. next[y*width+x] = false
  56. case pop == 3:
  57. // rule 4. Any dead cell with exactly three live neighbours
  58. // becomes a live cell, as if by reproduction.
  59. next[y*width+x] = true
  60. }
  61. }
  62. }
  63. w.area = next
  64. }
  65. // Draw paints current game state.
  66. func (w *World) Draw(pix []byte) {
  67. for i, v := range w.area {
  68. if v {
  69. pix[4*i] = 0xff
  70. pix[4*i+1] = 0xff
  71. pix[4*i+2] = 0xff
  72. pix[4*i+3] = 0xff
  73. } else {
  74. pix[4*i] = 0
  75. pix[4*i+1] = 0
  76. pix[4*i+2] = 0
  77. pix[4*i+3] = 0
  78. }
  79. }
  80. }
  81. // neighbourCount calculates the Moore neighborhood of (x, y).
  82. func neighbourCount(a []bool, width, height, x, y int) int {
  83. c := 0
  84. for j := -1; j <= 1; j++ {
  85. for i := -1; i <= 1; i++ {
  86. if i == 0 && j == 0 {
  87. continue
  88. }
  89. x2 := x + i
  90. y2 := y + j
  91. if x2 < 0 || y2 < 0 || width <= x2 || height <= y2 {
  92. continue
  93. }
  94. if a[y2*width+x2] {
  95. c++
  96. }
  97. }
  98. }
  99. return c
  100. }
  101. const (
  102. screenWidth = 320
  103. screenHeight = 240
  104. )
  105. type Game struct {
  106. world *World
  107. pixels []byte
  108. }
  109. func (g *Game) Update() error {
  110. g.world.Update()
  111. return nil
  112. }
  113. func (g *Game) Draw(screen *ebiten.Image) {
  114. if g.pixels == nil {
  115. g.pixels = make([]byte, screenWidth*screenHeight*4)
  116. }
  117. g.world.Draw(g.pixels)
  118. screen.WritePixels(g.pixels)
  119. }
  120. func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
  121. return screenWidth, screenHeight
  122. }
  123. func main() {
  124. g := &Game{
  125. world: NewWorld(screenWidth, screenHeight, int((screenWidth*screenHeight)/10)),
  126. }
  127. ebiten.SetWindowSize(screenWidth*2, screenHeight*2)
  128. ebiten.SetWindowTitle("Game of Life (Ebitengine Demo)")
  129. if err := ebiten.RunGame(g); err != nil {
  130. log.Fatal(err)
  131. }
  132. }