~eliasnaur/gio-example

ref: ecd2a626cd1977c7f6ea4722d985cde140a94021 gio-example/life/board.go -rw-r--r-- 2.5 KiB
ecd2a626Egon Elbre life: add game of life 3 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// SPDX-License-Identifier: Unlicense OR MIT

package main

import (
	"image"
	"math/rand"
)

// Board implements game of life logic.
type Board struct {
	// Size is the count of cells in a particular dimension.
	Size image.Point
	// Cells contains the alive or dead cells.
	Cells []byte

	// buffer is used to avoid reallocating a new cells
	// slice for every update.
	buffer []byte
}

// NewBoard returns a new game of life with the defined size.
func NewBoard(size image.Point) *Board {
	return &Board{
		Size:   size,
		Cells:  make([]byte, size.X*size.Y),
		buffer: make([]byte, size.X*size.Y),
	}
}

// Randomize randomizes each cell state.
func (b *Board) Randomize() {
	rand.Read(b.Cells)
	for i, v := range b.Cells {
		if v < 0x30 {
			b.Cells[i] = 1
		} else {
			b.Cells[i] = 0
		}
	}
}

// Pt returns the coordinate given a index in b.Cells.
func (b *Board) Pt(i int) image.Point {
	x, y := i%b.Size.X, i/b.Size.Y
	return image.Point{X: x, Y: y}
}

// At returns the b.Cells index, given a wrapped coordinate.
func (b *Board) At(c image.Point) int {
	if c.X < 0 {
		c.X += b.Size.X
	}
	if c.X >= b.Size.X {
		c.X -= b.Size.X
	}
	if c.Y < 0 {
		c.Y += b.Size.Y
	}
	if c.Y >= b.Size.Y {
		c.Y -= b.Size.Y
	}
	return b.Size.Y*c.Y + c.X
}

// SetWithoutWrap sets a cell to alive.
func (b *Board) SetWithoutWrap(c image.Point) {
	if !c.In(image.Rectangle{Max: b.Size}) {
		return
	}

	b.Cells[b.At(c)] = 1
}

// Advance advances the board state by 1.
func (b *Board) Advance() {
	next, cur := b.buffer, b.Cells
	defer func() { b.Cells, b.buffer = next, cur }()

	for i := range next {
		next[i] = 0
	}

	for y := 0; y < b.Size.Y; y++ {
		for x := 0; x < b.Size.X; x++ {
			var t byte
			t += cur[b.At(image.Pt(x-1, y-1))]
			t += cur[b.At(image.Pt(x+0, y-1))]
			t += cur[b.At(image.Pt(x+1, y-1))]
			t += cur[b.At(image.Pt(x-1, y+0))]
			t += cur[b.At(image.Pt(x+1, y+0))]
			t += cur[b.At(image.Pt(x-1, y+1))]
			t += cur[b.At(image.Pt(x+0, y+1))]
			t += cur[b.At(image.Pt(x+1, y+1))]

			// Any live cell with fewer than two live neighbours dies, as if by underpopulation.
			// Any live cell with two or three live neighbours lives on to the next generation.
			// Any live cell with more than three live neighbours dies, as if by overpopulation.
			// Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.

			p := b.At(image.Pt(x, y))
			switch {
			case t < 2:
				t = 0
			case t == 2:
				t = cur[p]
			case t == 3:
				t = 1
			case t > 3:
				t = 0
			}

			next[p] = t
		}
	}
}