diff --git a/_docs/gen.go b/_docs/gen.go index 306570555..f29d094d2 100644 --- a/_docs/gen.go +++ b/_docs/gen.go @@ -174,6 +174,7 @@ var examples = []example{ {"hue", 320, 240}, {"gamepad", 320, 240}, {"keyboard", 320, 240}, + {"life", 320, 240}, {"masking", 320, 240}, {"mosaic", 320, 240}, {"noise", 320, 240}, diff --git a/docs/examples/life.content.html b/docs/examples/life.content.html new file mode 100644 index 000000000..483689b36 --- /dev/null +++ b/docs/examples/life.content.html @@ -0,0 +1,24 @@ + + diff --git a/docs/examples/life.html b/docs/examples/life.html new file mode 100644 index 000000000..25f62caea --- /dev/null +++ b/docs/examples/life.html @@ -0,0 +1,231 @@ + + + + +Ebiten example - life + + + + + + + +
+ +

Ebiten example - life

+ +
// +build example
+
+package main
+
+import (
+        "image"
+        "log"
+        "math/rand"
+        "time"
+
+        "github.com/hajimehoshi/ebiten"
+
+        "os"
+        "image/png"
+)
+
+var (
+        randSource = rand.NewSource(time.Now().UnixNano())
+        rnd        = rand.New(randSource)
+)
+
+// World represents the game state
+type World struct {
+        area [][]bool
+}
+
+// NewWorld creates a new world
+func NewWorld(width, height int) *World {
+        world := World{}
+        world.area = makeArea(width, height)
+        return &world
+}
+
+// RandomSeed inits world with a random state
+func (w *World) RandomSeed(limit int) {
+        height := len(w.area)
+        width := len(w.area[0])
+
+        for i := 0; i < limit; i++ {
+                x := rnd.Intn(width)
+                y := rnd.Intn(height)
+                w.area[y][x] = true
+        }
+}
+
+// Progress game state by one tick
+func (w *World) Progress() {
+        height := len(w.area)
+        width := len(w.area[0])
+
+        next := makeArea(width, height)
+
+        for y := 0; y < height; y++ {
+                for x := 0; x < width; x++ {
+
+                        pop := neighbourCount(w.area, 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][x] = false
+
+                        case (pop == 2 || pop == 3) && w.area[y][x]:
+                                // rule 2. Any live cell with two or three live neighbours
+                                // lives on to the next generation.
+                                next[y][x] = true
+
+                        case pop > 3:
+                                // rule 3. Any live cell with more than three live neighbours
+                                // dies, as if by over-population.
+                                next[y][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][x] = true
+                        }
+                }
+        }
+        w.area = next
+}
+
+// DrawImage paints current game state
+func (w *World) DrawImage(img *image.RGBA) {
+        height := len(w.area)
+        width := len(w.area[0])
+
+        for y := 0; y < height; y++ {
+                for x := 0; x < width; x++ {
+                        pos := 4*y*width + 4*x
+                        if w.area[y][x] {
+                                img.Pix[pos] = 0xff
+                                img.Pix[pos+1] = 0xff
+                                img.Pix[pos+2] = 0xff
+                                img.Pix[pos+3] = 0xff
+                        } else {
+                                img.Pix[pos] = 0
+                                img.Pix[pos+1] = 0
+                                img.Pix[pos+2] = 0
+                                img.Pix[pos+3] = 0
+                        }
+                }
+        }
+}
+
+// neighbourCount calculates the Moore neighborhood of x, y
+func neighbourCount(a [][]bool, x, y int) int {
+        height := len(a)
+        width := len(a[0])
+
+        lowX := 0
+        if x > 0 {
+                lowX = x - 1
+        }
+
+        lowY := 0
+        if y > 0 {
+                lowY = y - 1
+        }
+
+        highX := width - 1
+        if x < width-1 {
+                highX = x + 1
+        }
+
+        highY := height - 1
+        if y < height-1 {
+                highY = y + 1
+        }
+
+        near := 0
+        for pY := lowY; pY <= highY; pY++ {
+                for pX := lowX; pX <= highX; pX++ {
+                        if !(pX == x && pY == y) && a[pY][pX] {
+                                near++
+                        }
+                }
+        }
+
+        return near
+}
+
+func makeArea(width, height int) [][]bool {
+        area := make([][]bool, height)
+        for i := 0; i < height; i++ {
+                area[i] = make([]bool, width)
+        }
+        return area
+}
+
+const (
+        screenWidth  = 320
+        screenHeight = 240
+)
+
+var (
+        world      *World
+        noiseImage *image.RGBA
+)
+
+func update(screen *ebiten.Image) error {
+        world.Progress()
+        world.DrawImage(noiseImage)
+        if err := screen.ReplacePixels(noiseImage.Pix); err != nil {
+                return err
+        }
+        if ebiten.IsKeyPressed(ebiten.KeyQ) {
+                f, err := os.Create("screenshot.png")
+                if err != nil {
+                        return err
+                }
+                defer f.Close()
+                if err := png.Encode(f, screen); err != nil {
+                        return err
+                }
+                os.Exit(0)
+        }
+        return nil
+}
+
+func main() {
+        population := int((screenWidth * screenHeight) / 10)
+        scale := 2.0
+
+        world = NewWorld(screenWidth, screenHeight)
+        world.RandomSeed(population)
+
+        noiseImage = image.NewRGBA(image.Rect(0, 0, screenWidth, screenHeight))
+        if err := ebiten.Run(update, screenWidth, screenHeight, scale, "Game of Life (Ebiten Demo)"); err != nil {
+                log.Fatal(err)
+        }
+}
+
+ +
+ + + + + diff --git a/docs/images/examples/life.png b/docs/images/examples/life.png new file mode 100644 index 000000000..17d7ac61a Binary files /dev/null and b/docs/images/examples/life.png differ diff --git a/docs/index.html b/docs/index.html index 0d1d3ce6d..3fb44d749 100644 --- a/docs/index.html +++ b/docs/index.html @@ -69,6 +69,8 @@ Ebiten example: gamepad
Ebiten example: keyboard +
+ Ebiten example: life
Ebiten example: masking