-
Notifications
You must be signed in to change notification settings - Fork 2
/
life-engine.js
158 lines (137 loc) · 4.9 KB
/
life-engine.js
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
module.exports = function createInstance(game, opts) {
opts = opts || {}
var on_material = opts.on_material || 2
var off_material = opts.off_material || 0
var boardPos = opts.boardPosition || [-32, 1, -32]
var boardSize = opts.boardSize || 64
var board = { x: boardSize, y: 1, z: boardSize } // used to wrap live cell patterns
var frequency = opts.frequency || 500
var timeSinceTick = 0
var paused = false
var cells = []
var num_live_voxels = 0
function addCell(voxel) {
//cells[cell.x * boardSize + cell.z] = cell
cells.push(voxel)
}
function pause() {
if (paused) return
paused = true
console.log("total live cell count: " + num_live_voxels)
}
function togglePause() {
paused ? resume() : pause()
}
function resume() {
if (!paused) return
paused = false
}
function reset() {
pause()
cells.forEach(function (cell) {
game.setBlock([cell.x, cell.y, cell.z], off_material)
})
cells.length = 0 // clear
// add glider cells
addCell( { x: 0, y: 1, z: 0, on: true } )
addCell( { x: 0, y: 1, z: 1, on: true } )
addCell( { x: 0, y: 1, z: 2, on: true } )
addCell( { x: 1, y: 1, z: 2, on: true } )
addCell( { x: 2, y: 1, z: 1, on: true } )
}
function speedUp() {
frequency = Math.floor(frequency * 0.9)
if (frequency < 10) frequency = 10
console.log("frequency: " + frequency)
}
function speedDown() {
frequency = Math.floor(frequency * 1.1)
if (frequency > 1000) frequency = 1000
console.log("frequency: " + frequency)
}
// an empty/inactive neighbor is a candidate for a new live cell
//
function getEmptyNeighbor(x, y, z, dx, dy, dz) {
var material = game.getBlock([x + dx, y + dy, z + dz])
if (material === on_material) return false // there is a live neighbor there, not empty
return { x: x + dx, y: y + dy, z: z + dz, on: false, off_material: material } // return new inactive cell, candidate for activation
// keeping track of the previous material in case we want to restore it when the cell goes inactive
}
var counter = 0
function update(dt) {
if (paused) return;
timeSinceTick += dt
if (timeSinceTick < frequency) {
return; // still waiting, better way to get called at the proper frequency?
}
console.log("iteration " + counter);
timeSinceTick = 0
var possibleNewCells = []
var new_num_live_voxels = 0;
cells.forEach(function (cell) {
if (!cell) {
console.log("warning: found bad cell in 'cells' array even after cleanup")
return;
}
new_num_live_voxels++
// check each of 8 neighbors:
// if a neighbor is active, increment counter
// otherwise, save for later to check for new cell birth
var liveNeighbors = 0 // number of live neighbors
for (var dx = -1; dx < 2; dx++) {
//for (var dy = -1; dy < 2; dy++) { // 3D?
for (var dy = 0; dy < 1; dy++) { // mono-layer
for (var dz = -1; dz < 2; dz++) {
if (dx === 0 && dy === 0 && dz === 0) continue // skip self
var empty = getEmptyNeighbor(cell.x, cell.y, cell.z, dx, dy, dz)
if (empty) possibleNewCells.push(empty)
else liveNeighbors++
}
}
}
if (liveNeighbors < 2 || liveNeighbors > 3) cell.on = false
})
num_live_voxels = new_num_live_voxels
possibleNewCells.forEach(function (cell, index, array) {
var liveNeighbors = 0 // number of live neighbors
for (var dx = -1; dx < 2; dx++) {
//for (var dy = -1; dy < 2; dy++) { // 3D?
for (var dy = 0; dy < 1; dy++) { // mono-layer
for (var dz = -1; dz < 2; dz++) {
if (dx === 0 && dy === 0 && dz === 0) continue // skip self
if (!getEmptyNeighbor(cell.x, cell.y, cell.z, dx, dy, dz)) liveNeighbors++ // count liviing neighbors
}
}
}
if (liveNeighbors !== 3) delete array[index] // this cell didn't make it
})
possibleNewCells.forEach(function (cell) {
if (!cell) {
console.log("warning: found bad cell in 'possibleNewCells' array even after cleanup")
return;
}
cell.on = true
addCell(cell)
})
// update voxels (both on and off), then remove off cells from array
cells = cells.filter(function (cell) {
var pos = [cell.x, cell.y, cell.z]
if (!cell) {
console.log("warning: found bad cell in 'cells' array during updateVoxels")
return false;
}
game.setBlock([cell.x, cell.y, cell.z], cell.on ? on_material : off_material)
return cell.on
})
}
// public api:
return {
pause: pause,
togglePause: togglePause,
resume: resume,
reset: reset,
update: update,
speedUp: speedUp,
speedDown: speedDown
}
}