-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlife.go
More file actions
162 lines (141 loc) · 3.27 KB
/
life.go
File metadata and controls
162 lines (141 loc) · 3.27 KB
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package main
import (
"engo.io/ecs"
"engo.io/engo/common"
)
const (
LifeStepInterval float32 = 0.1
)
// AliveComponent represents each cell's dot
type AliveComponent struct {
neighbors []*Cell
status bool
lastStatus bool
updated bool
ecs.BasicEntity
common.RenderComponent
common.SpaceComponent
}
// updateStatus just update the status of each cell based on the number of
// active neighbors.
func (c *AliveComponent) updateStatus() {
total := 0
for _, neighbor := range c.neighbors {
if neighbor.updated && neighbor.lastStatus {
total += 1
} else if !neighbor.updated && neighbor.status {
total += 1
}
}
/*
* ##################################
* # Game Of Life Rules evaluation. #
* ##################################
*/
c.lastStatus = c.status
if c.status {
// Any live cell with fewer than two live neighbours dies, as if caused
// by underpopulation.
if total < 2 {
c.status = false
}
// Any live cell with two or three live neighbours lives on to the next
// generation.
// if total == 2 || total == 3 { s.status = true }
// Any live cell with more than three live neighbours dies, as if by
// overpopulation.
if total > 3 {
c.status = false
}
} else {
// Any dead cell with exactly three live neighbours becomes a live
// cell, as if by reproduction.
if total == 3 {
c.status = true
}
}
c.updated = true
}
// AliveEntity is a simple cell
type AliveEntity struct {
*ecs.BasicEntity
*common.RenderComponent
*common.SpaceComponent
*AliveComponent
}
// LifeSystem is used to manage each cell's status on each step
type LifeSystem struct {
since float32
gen int
isFirstTime bool
world *ecs.World
entities []AliveEntity
}
// New initialize the system
func (l *LifeSystem) New(w *ecs.World) {
l.world = w
l.isFirstTime = true
}
// Add append an entity to the system
func (l *LifeSystem) Add(
basic *ecs.BasicEntity,
render *common.RenderComponent,
space *common.SpaceComponent,
alive *AliveComponent,
) {
l.entities = append(l.entities, AliveEntity{
basic,
render,
space,
alive,
})
}
// Remove deletes an entity
func (l *LifeSystem) Remove(basic ecs.BasicEntity) {
deleteIndex := -1
for index, e := range l.entities {
if e.BasicEntity.ID() == basic.ID() {
deleteIndex = index
break
}
}
if deleteIndex >= 0 {
l.entities = append(l.entities[:deleteIndex], l.entities[deleteIndex+1:]...)
}
}
// clearEntitiesStatus set each cell's updated status to false
func (l *LifeSystem) clearEntitiesStatus() {
for _, entity := range l.entities {
entity.updated = false
}
}
// Update process each cell to get the new status and shows or hide the dot
func (l *LifeSystem) Update(dt float32) {
l.since += dt
if l.since > LifeStepInterval || l.isFirstTime {
l.gen += 1
l.clearEntitiesStatus()
var sys *common.RenderSystem
for _, system := range l.world.Systems() {
switch system.(type) {
case *common.RenderSystem:
sys = system.(*common.RenderSystem)
break
}
}
for _, entity := range l.entities {
if !l.isFirstTime {
entity.updateStatus()
}
if entity.status {
sys.AddByInterface(entity.AliveComponent)
} else {
sys.Remove(entity.AliveComponent.BasicEntity)
}
}
l.since = 0
}
if l.isFirstTime {
l.isFirstTime = false
}
}