@woosh/meep-engine 2.134.5 → 2.135.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/build/bundle-worker-image-decoder.js +1 -1
  2. package/build/bundle-worker-terrain.js +1 -1
  3. package/editor/tools/v2/TransformControlsGizmo.js +1 -1
  4. package/editor/view/node-graph/NodeGraphEditorView.js +2 -2
  5. package/package.json +1 -1
  6. package/src/core/assert.d.ts +0 -2
  7. package/src/core/assert.d.ts.map +1 -1
  8. package/src/core/assert.js +0 -6
  9. package/src/core/color/Color.d.ts +0 -5
  10. package/src/core/color/Color.d.ts.map +1 -1
  11. package/src/core/color/Color.js +1 -7
  12. package/src/core/geom/2d/hash-grid/SpatialHashGrid.js +386 -386
  13. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_2d.js +1 -1
  14. package/src/core/geom/2d/quad-tree-binary/QuadTree.js +714 -714
  15. package/src/core/geom/3d/triangle/computeTriangleRayIntersection.js +160 -160
  16. package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentric.js +96 -96
  17. package/src/core/geom/packing/max-rect/MaxRectanglesPacker.js +1 -1
  18. package/src/core/geom/packing/max-rect/findBestContainer.js +4 -4
  19. package/src/core/geom/packing/max-rect/packOneBox.js +2 -2
  20. package/src/core/geom/vec3/v3_rigid_align_paired_unit_vectors.js +1 -1
  21. package/src/core/graph/layout/box/BoxLayouter.js +7 -7
  22. package/src/core/graph/layout/box/position_box_next_to_box.js +6 -6
  23. package/src/core/math/computeWholeDivisorLow.js +33 -33
  24. package/src/core/math/linalg/eigen/eigen_values_find_spectral_gap.d.ts.map +1 -0
  25. package/src/core/math/linalg/eigen/matrix_eigenvalues_in_place.d.ts +10 -0
  26. package/src/core/math/linalg/eigen/matrix_eigenvalues_in_place.d.ts.map +1 -0
  27. package/src/core/{graph → math/linalg}/eigen/matrix_eigenvalues_in_place.js +8 -7
  28. package/src/core/math/linalg/eigen/matrix_householder_in_place.d.ts.map +1 -0
  29. package/src/core/{graph → math/linalg}/eigen/matrix_householder_in_place.js +11 -5
  30. package/src/core/math/linalg/eigen/matrix_qr_in_place.d.ts +15 -0
  31. package/src/core/math/linalg/eigen/matrix_qr_in_place.d.ts.map +1 -0
  32. package/src/core/{graph → math/linalg}/eigen/matrix_qr_in_place.js +8 -2
  33. package/src/core/{graph → math/linalg}/eigen/matrix_top_eigenvector_power_iteration.d.ts +0 -3
  34. package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.d.ts.map +1 -0
  35. package/src/core/{graph → math/linalg}/eigen/matrix_top_eigenvector_power_iteration.js +0 -3
  36. package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.d.ts +19 -0
  37. package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.d.ts.map +1 -0
  38. package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.js +161 -0
  39. package/src/core/math/linalg/polynomial_real_roots_in_interval.d.ts +15 -0
  40. package/src/core/math/linalg/polynomial_real_roots_in_interval.d.ts.map +1 -0
  41. package/src/core/math/linalg/polynomial_real_roots_in_interval.js +200 -0
  42. package/src/core/math/solveCubic.d.ts +15 -0
  43. package/src/core/math/solveCubic.d.ts.map +1 -0
  44. package/src/core/math/solveCubic.js +82 -0
  45. package/src/core/math/spline/spline3_hermite_bounds_t.d.ts +23 -0
  46. package/src/core/math/spline/spline3_hermite_bounds_t.d.ts.map +1 -0
  47. package/src/core/math/spline/spline3_hermite_bounds_t.js +109 -0
  48. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts +25 -0
  49. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts.map +1 -0
  50. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.js +44 -0
  51. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.d.ts +16 -0
  52. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.d.ts.map +1 -0
  53. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.js +120 -0
  54. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts +11 -0
  55. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts.map +1 -0
  56. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.js +451 -0
  57. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts +12 -0
  58. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts.map +1 -0
  59. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.js +339 -0
  60. package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.d.ts +15 -0
  61. package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.d.ts.map +1 -0
  62. package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.js +21 -0
  63. package/src/core/math/spline/spline3_hermite_to_monomial.d.ts +24 -0
  64. package/src/core/math/spline/spline3_hermite_to_monomial.d.ts.map +1 -0
  65. package/src/core/math/spline/spline3_hermite_to_monomial.js +37 -0
  66. package/src/core/math/spline/v3_computeCatmullRomSplineUniformDistance.js +1 -1
  67. package/src/core/model/node-graph/visual/NodeGraphVisualData.js +1 -1
  68. package/src/core/model/reactive/model/util/createRandomReactiveExpression.js +185 -185
  69. package/src/core/process/delay.js +16 -16
  70. package/src/engine/animation/async/TimeSeries.js +300 -300
  71. package/src/engine/animation/curve/AnimationCurve.d.ts +0 -4
  72. package/src/engine/animation/curve/AnimationCurve.d.ts.map +1 -1
  73. package/src/engine/animation/curve/AnimationCurve.js +1 -6
  74. package/src/engine/animation/curve/draw/position_canvas_to_curve.js +2 -2
  75. package/src/engine/animation/curve/draw/position_curve_to_canvas.js +2 -2
  76. package/src/engine/ecs/fow/shader/FogOfWarRenderer.js +145 -145
  77. package/src/engine/ecs/gui/position/ViewportPositionSystem.js +2 -2
  78. package/src/engine/ecs/parent/entity_node_compute_bounding_box.js +1 -1
  79. package/src/engine/ecs/transform/Transform.d.ts +0 -10
  80. package/src/engine/ecs/transform/Transform.d.ts.map +1 -1
  81. package/src/engine/ecs/transform/Transform.js +0 -12
  82. package/src/engine/graphics/composit/CompositLayer.js +254 -254
  83. package/src/engine/graphics/ecs/mesh-v2/sg_hierarchy_compute_bounding_box_via_parent_entity.js +1 -1
  84. package/src/engine/graphics/ecs/path/tube/build/build_geometry_linear.js +2 -2
  85. package/src/engine/graphics/material/optimization/MaterialOptimizationContext.js +3 -3
  86. package/src/engine/graphics/particles/particular/engine/utils/volume/AttributeValue.js +201 -201
  87. package/src/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +1 -1
  88. package/src/engine/graphics/render/buffer/slot/parameter/ProgramValueSlotParameterSet.js +2 -2
  89. package/src/engine/graphics/render/forward_plus/LightManager.js +1226 -1226
  90. package/src/engine/graphics/render/forward_plus/model/PointLight.js +1 -1
  91. package/src/engine/graphics/sh3/lpv/lpv_obtain_storage_cached_volume.js +1 -1
  92. package/src/engine/graphics/sh3/path_tracer/texture/sample_material.js +2 -2
  93. package/src/engine/graphics/texture/atlas/TextureAtlasDebugger.js +1 -1
  94. package/src/engine/graphics/texture/sampler/HarmonicDiffusionGrid.js +145 -145
  95. package/src/engine/graphics/texture/sampler/serialization/TextureBinaryBufferSerializer.js +2 -2
  96. package/src/engine/intelligence/behavior/ecs/BehaviorComponent.d.ts +2 -6
  97. package/src/engine/intelligence/behavior/ecs/BehaviorComponent.d.ts.map +1 -1
  98. package/src/engine/intelligence/behavior/ecs/BehaviorComponent.js +0 -10
  99. package/src/engine/intelligence/mcts/MonteCarlo.js +275 -275
  100. package/src/engine/navigation/ecs/path_following/PathFollower.js +222 -222
  101. package/src/generation/grid/GridData.js +220 -220
  102. package/src/generation/grid/generation/GridTaskDensityMarkerDistribution.js +385 -385
  103. package/src/view/elements/image/SvgImageView.js +1 -1
  104. package/src/view/elements/windrose/WindRoseDiagram.js +369 -369
  105. package/src/view/minimap/gl/MinimapFogOfWar.js +3 -3
  106. package/src/view/util/DomSizeObserver.js +1 -1
  107. package/src/core/binary/clz32.d.ts +0 -6
  108. package/src/core/binary/clz32.d.ts.map +0 -1
  109. package/src/core/binary/clz32.js +0 -5
  110. package/src/core/binary/type/dataTypeFromTypedArray.d.ts +0 -8
  111. package/src/core/binary/type/dataTypeFromTypedArray.d.ts.map +0 -1
  112. package/src/core/binary/type/dataTypeFromTypedArray.js +0 -11
  113. package/src/core/collection/array/computeHashIntegerArray.d.ts +0 -1
  114. package/src/core/collection/array/computeHashIntegerArray.d.ts.map +0 -1
  115. package/src/core/collection/array/computeHashIntegerArray.js +0 -7
  116. package/src/core/collection/array/typed/typedArrayToDataType.d.ts +0 -6
  117. package/src/core/collection/array/typed/typedArrayToDataType.d.ts.map +0 -1
  118. package/src/core/collection/array/typed/typedArrayToDataType.js +0 -6
  119. package/src/core/geom/3d/mat4/MATRIX_4_IDENTITY.d.ts +0 -6
  120. package/src/core/geom/3d/mat4/MATRIX_4_IDENTITY.d.ts.map +0 -1
  121. package/src/core/geom/3d/mat4/MATRIX_4_IDENTITY.js +0 -7
  122. package/src/core/graph/eigen/eigen_values_find_spectral_gap.d.ts.map +0 -1
  123. package/src/core/graph/eigen/matrix_eigenvalues_in_place.d.ts +0 -8
  124. package/src/core/graph/eigen/matrix_eigenvalues_in_place.d.ts.map +0 -1
  125. package/src/core/graph/eigen/matrix_householder_in_place.d.ts.map +0 -1
  126. package/src/core/graph/eigen/matrix_qr_in_place.d.ts +0 -9
  127. package/src/core/graph/eigen/matrix_qr_in_place.d.ts.map +0 -1
  128. package/src/core/graph/eigen/matrix_top_eigenvector_power_iteration.d.ts.map +0 -1
  129. package/src/core/math/spline/cubicCurve.d.ts +0 -6
  130. package/src/core/math/spline/cubicCurve.d.ts.map +0 -1
  131. package/src/core/math/spline/cubicCurve.js +0 -6
  132. package/src/core/math/spline/spline_bezier2.d.ts +0 -6
  133. package/src/core/math/spline/spline_bezier2.d.ts.map +0 -1
  134. package/src/core/math/spline/spline_bezier2.js +0 -6
  135. package/src/core/math/spline/spline_bezier3.d.ts +0 -6
  136. package/src/core/math/spline/spline_bezier3.d.ts.map +0 -1
  137. package/src/core/math/spline/spline_bezier3.js +0 -6
  138. package/src/core/math/spline/spline_bezier3_bounds.d.ts +0 -6
  139. package/src/core/math/spline/spline_bezier3_bounds.d.ts.map +0 -1
  140. package/src/core/math/spline/spline_bezier3_bounds.js +0 -6
  141. package/src/core/math/spline/spline_hermite3.d.ts +0 -6
  142. package/src/core/math/spline/spline_hermite3.d.ts.map +0 -1
  143. package/src/core/math/spline/spline_hermite3.js +0 -6
  144. package/src/core/math/spline/spline_hermite3_bounds.d.ts +0 -6
  145. package/src/core/math/spline/spline_hermite3_bounds.d.ts.map +0 -1
  146. package/src/core/math/spline/spline_hermite3_bounds.js +0 -6
  147. package/src/core/math/spline/spline_hermite3_to_bezier.d.ts +0 -2
  148. package/src/core/math/spline/spline_hermite3_to_bezier.d.ts.map +0 -1
  149. package/src/core/math/spline/spline_hermite3_to_bezier.js +0 -6
  150. package/src/engine/intelligence/behavior/decorator/RepeatUntilFailureBehavior.d.ts +0 -37
  151. package/src/engine/intelligence/behavior/decorator/RepeatUntilFailureBehavior.d.ts.map +0 -1
  152. package/src/engine/intelligence/behavior/decorator/RepeatUntilFailureBehavior.js +0 -70
  153. /package/src/core/{graph → math/linalg}/eigen/eigen_values_find_spectral_gap.d.ts +0 -0
  154. /package/src/core/{graph → math/linalg}/eigen/eigen_values_find_spectral_gap.js +0 -0
  155. /package/src/core/{graph → math/linalg}/eigen/matrix_householder_in_place.d.ts +0 -0
@@ -1,275 +1,275 @@
1
- import { assert } from "../../../core/assert.js";
2
- import { returnZero } from "../../../core/function/returnZero.js";
3
- import { mix } from "../../../core/math/mix.js";
4
- import { seededRandom } from "../../../core/math/random/seededRandom.js";
5
- import { MoveEdge } from "./MoveEdge.js";
6
- import { StateNode, StateType } from "./StateNode.js";
7
-
8
- /**
9
- * From: A Survey of Monte Carlo Tree Search Methods
10
- * The value Cp = 1/√2 was shown by Kocsis and Szepesvari [120] to satisfy the Hoeffding ineqality with rewards in the range [0, 1]
11
- * @type {number}
12
- */
13
- const C_ks = 1 / Math.sqrt(2);
14
-
15
- /**
16
- *
17
- * @param {StateNode} parent
18
- * @param {StateNode} child
19
- * @returns {number}
20
- */
21
- function computeNodeSelectionScore(parent, child) {
22
- const playouts = child.playouts;
23
-
24
- if (playouts === 0) {
25
- // child has 0 playouts, this can only happen if it ended up with no moves, avoid division by 0
26
- return 0;
27
- }
28
-
29
- // Exploitation heuristic
30
- const Q = mix((child.wins + 1) / playouts, child.heuristicValue, 0.65);
31
-
32
- // Based on UCB1
33
- // exploration heuristic
34
- const u = Math.sqrt((2 * Math.log(parent.playouts)) / playouts);
35
-
36
- return Q + C_ks * u;
37
- }
38
-
39
- /**
40
- * @template S
41
- * @author Alex Goldring
42
- * @copyright Company Named Limited (c) 2025
43
- */
44
- export class MonteCarloTreeSearch {
45
-
46
- /**
47
- *
48
- * @type {S}
49
- */
50
- rootState = null;
51
-
52
- /**
53
- *
54
- * @type {StateNode|null}
55
- */
56
- root = null;
57
-
58
- /**
59
- *
60
- * @type {function(state:S, source:StateNode):MoveEdge[]}
61
- */
62
- computeValidMoves = null;
63
-
64
- /**
65
- *
66
- * @type {function(state:S):StateType}
67
- */
68
- computeTerminalFlag = null;
69
-
70
- /**
71
- * Depth to which plays will be explored
72
- * @type {number}
73
- */
74
- maxExplorationDepth = 1000;
75
-
76
- /**
77
- *
78
- * @type {Function}
79
- */
80
- random = seededRandom(0);
81
-
82
- /**
83
- * @param {S} rootState
84
- * @param {function(state:S, source:StateNode):MoveEdge[]} computeValidMoves
85
- * @param {function(state:S):StateType} computeTerminalFlag
86
- * @param {function(S):S} cloneState
87
- * @param {function(StateNode, S):number} heuristic Estimation function for evaluation of intermediate stated, guides exploration
88
- */
89
- initialize(
90
- {
91
- rootState,
92
- computeValidMoves,
93
- computeTerminalFlag,
94
- cloneState,
95
- heuristic = returnZero
96
- }
97
- ) {
98
- assert.isFunction(computeValidMoves, `computeValidMoves`);
99
- assert.isFunction(computeTerminalFlag, `computeTerminalFlag`);
100
- assert.isFunction(cloneState, `cloneState`);
101
-
102
- this.computeValidMoves = computeValidMoves;
103
- this.computeTerminalFlag = computeTerminalFlag;
104
- this.cloneState = cloneState;
105
- this.heuristic = heuristic;
106
-
107
- this.rootState = rootState;
108
-
109
- this.root = new StateNode();
110
- }
111
-
112
- /**
113
- *
114
- * @param {StateNode} node
115
- * @param {S} state
116
- * @returns {StateNode}
117
- */
118
- selectRandom(node, state) {
119
- let score;
120
-
121
- let i, bestScore, bestMove;
122
-
123
- const random = this.random;
124
-
125
- while (
126
- node.isExpanded() &&
127
- node.moves.length > 0 &&
128
- !node.isTerminal()
129
- ) {
130
-
131
- bestScore = Number.NEGATIVE_INFINITY;
132
- bestMove = null;
133
-
134
- const moves = node.moves;
135
-
136
- const numMoves = moves.length;
137
-
138
- assert.notEqual(numMoves, 0, 'number of moves is 0, this is invalid state');
139
-
140
- for (i = 0; i < numMoves; i++) {
141
-
142
- const move = moves[i];
143
-
144
- const randomRoll = random();
145
-
146
- if (move.isTargetMaterialized()) {
147
-
148
- const child = move.target;
149
-
150
- const s = computeNodeSelectionScore(node, child);
151
-
152
- assert.notNaN(s, 'computed Node score');
153
- assert.isFiniteNumber(s, `computed Node score`);
154
-
155
- score = s + randomRoll;
156
- } else {
157
-
158
- //use a constant value for unexplored nodes
159
- score = randomRoll * 100;
160
-
161
- }
162
-
163
-
164
- if (score > bestScore) {
165
- bestScore = score;
166
- bestMove = move;
167
- }
168
-
169
- }
170
-
171
- if (!bestMove.isTargetMaterialized()) {
172
- //materialize the target state
173
- materializedEdgeTarget(state, node, bestMove, this.computeTerminalFlag, this.heuristic);
174
-
175
- } else {
176
- //just follow the edge
177
- bestMove.move(state);
178
- }
179
-
180
- node = bestMove.target;
181
- }
182
-
183
-
184
- return node;
185
- }
186
-
187
- /**
188
- * Perform a playout from the root node
189
- * @returns {S} final state of the playout
190
- */
191
- playout() {
192
- const computeValidMoves = this.computeValidMoves;
193
-
194
- const computeTerminalFlag = this.computeTerminalFlag;
195
-
196
- const state = this.cloneState(this.rootState);
197
-
198
- assert.notEqual(state, this.rootState, 'cloneState must produce a new state object, instead it produced the same one');
199
-
200
- let node = this.root;
201
-
202
- while (!node.isTerminal() && node.depth < this.maxExplorationDepth) {
203
-
204
- if (!node.isExpanded()) {
205
- node.expand(state, computeValidMoves, computeTerminalFlag);
206
- }
207
-
208
- const child = this.selectRandom(node, state);
209
-
210
- if (child === node) {
211
- // prevent infinite recursion
212
- // this should not happen?
213
- break;
214
- }
215
-
216
- node = child;
217
- }
218
-
219
- if (!node.isTerminal() && node.depth >= this.maxExplorationDepth) {
220
- //cap the state by depth, propagate heuristic score
221
- node.type = StateType.DepthCapped;
222
- }
223
-
224
- // record play-through
225
- const terminalFlag = node.type;
226
-
227
- if (terminalFlag === StateType.Win) {
228
- node.addPlayouts(1, 1, 0);
229
- } else if (terminalFlag === StateType.Loss) {
230
- node.addPlayouts(1, 0, 1);
231
- } else if (terminalFlag === StateType.Tie || terminalFlag === StateType.DepthCapped) {
232
- node.addPlayouts(1, 0, 0);
233
- }
234
-
235
- return state;
236
- }
237
- }
238
-
239
- /**
240
- * @template S
241
- * @param {S} state
242
- * @param {StateNode} source
243
- * @param {MoveEdge} edge
244
- * @param {function(S):StateType} computeTerminalFlag
245
- * @param {function(StateNode, S)} heuristic
246
- */
247
- function materializedEdgeTarget(state, source, edge, computeTerminalFlag, heuristic) {
248
-
249
- const child = new StateNode();
250
- child.parent = source;
251
- child.depth = source.depth + 1;
252
-
253
- const computedState = edge.move(state);
254
-
255
- const terminalFlag = computeTerminalFlag(computedState);
256
-
257
- assert.enum(terminalFlag, StateType, 'terminalFlag');
258
-
259
- child.type = terminalFlag;
260
-
261
- edge.target = child;
262
-
263
- const childHeuristicScore = heuristic(child, computedState);
264
-
265
- assert.notNaN(childHeuristicScore, 'childHeuristicScore');
266
-
267
- child.heuristicValue = childHeuristicScore;
268
-
269
-
270
- // bubble the heuristic score up the tree
271
- // child.bubbleUpHeuristicScore(); // heuristic value changes sign depending on the team making the move, so aggregation becomes tricky
272
-
273
- return computedState;
274
- }
275
-
1
+ import { assert } from "../../../core/assert.js";
2
+ import { returnZero } from "../../../core/function/returnZero.js";
3
+ import { mix } from "../../../core/math/mix.js";
4
+ import { seededRandom } from "../../../core/math/random/seededRandom.js";
5
+ import { MoveEdge } from "./MoveEdge.js";
6
+ import { StateNode, StateType } from "./StateNode.js";
7
+
8
+ /**
9
+ * From: A Survey of Monte Carlo Tree Search Methods
10
+ * The value Cp = 1/√2 was shown by Kocsis and Szepesvari [120] to satisfy the Hoeffding ineqality with rewards in the range [0, 1]
11
+ * @type {number}
12
+ */
13
+ const C_ks = 1 / Math.sqrt(2);
14
+
15
+ /**
16
+ *
17
+ * @param {StateNode} parent
18
+ * @param {StateNode} child
19
+ * @returns {number}
20
+ */
21
+ function computeNodeSelectionScore(parent, child) {
22
+ const playouts = child.playouts;
23
+
24
+ if (playouts === 0) {
25
+ // child has 0 playouts, this can only happen if it ended up with no moves, avoid division by 0
26
+ return 0;
27
+ }
28
+
29
+ // Exploitation heuristic
30
+ const Q = mix((child.wins + 1) / playouts, child.heuristicValue, 0.65);
31
+
32
+ // Based on UCB1
33
+ // exploration heuristic
34
+ const u = Math.sqrt((2 * Math.log(parent.playouts)) / playouts);
35
+
36
+ return Q + C_ks * u;
37
+ }
38
+
39
+ /**
40
+ * @template S
41
+ * @author Alex Goldring
42
+ * @copyright Company Named Limited (c) 2025
43
+ */
44
+ export class MonteCarloTreeSearch {
45
+
46
+ /**
47
+ *
48
+ * @type {S}
49
+ */
50
+ rootState = null;
51
+
52
+ /**
53
+ *
54
+ * @type {StateNode|null}
55
+ */
56
+ root = null;
57
+
58
+ /**
59
+ *
60
+ * @type {function(state:S, source:StateNode):MoveEdge[]}
61
+ */
62
+ computeValidMoves = null;
63
+
64
+ /**
65
+ *
66
+ * @type {function(state:S):StateType}
67
+ */
68
+ computeTerminalFlag = null;
69
+
70
+ /**
71
+ * Depth to which plays will be explored
72
+ * @type {number}
73
+ */
74
+ maxExplorationDepth = 1000;
75
+
76
+ /**
77
+ *
78
+ * @type {Function}
79
+ */
80
+ random = seededRandom(0);
81
+
82
+ /**
83
+ * @param {S} rootState
84
+ * @param {function(state:S, source:StateNode):MoveEdge[]} computeValidMoves
85
+ * @param {function(state:S):StateType} computeTerminalFlag
86
+ * @param {function(S):S} cloneState
87
+ * @param {function(StateNode, S):number} heuristic Estimation function for evaluation of intermediate stated, guides exploration
88
+ */
89
+ initialize(
90
+ {
91
+ rootState,
92
+ computeValidMoves,
93
+ computeTerminalFlag,
94
+ cloneState,
95
+ heuristic = returnZero
96
+ }
97
+ ) {
98
+ assert.isFunction(computeValidMoves, `computeValidMoves`);
99
+ assert.isFunction(computeTerminalFlag, `computeTerminalFlag`);
100
+ assert.isFunction(cloneState, `cloneState`);
101
+
102
+ this.computeValidMoves = computeValidMoves;
103
+ this.computeTerminalFlag = computeTerminalFlag;
104
+ this.cloneState = cloneState;
105
+ this.heuristic = heuristic;
106
+
107
+ this.rootState = rootState;
108
+
109
+ this.root = new StateNode();
110
+ }
111
+
112
+ /**
113
+ *
114
+ * @param {StateNode} node
115
+ * @param {S} state
116
+ * @returns {StateNode}
117
+ */
118
+ selectRandom(node, state) {
119
+ let score;
120
+
121
+ let i, bestScore, bestMove;
122
+
123
+ const random = this.random;
124
+
125
+ while (
126
+ node.isExpanded() &&
127
+ node.moves.length > 0 &&
128
+ !node.isTerminal()
129
+ ) {
130
+
131
+ bestScore = Number.NEGATIVE_INFINITY;
132
+ bestMove = null;
133
+
134
+ const moves = node.moves;
135
+
136
+ const numMoves = moves.length;
137
+
138
+ assert.notEqual(numMoves, 0, 'number of moves is 0, this is invalid state');
139
+
140
+ for (i = 0; i < numMoves; i++) {
141
+
142
+ const move = moves[i];
143
+
144
+ const randomRoll = random();
145
+
146
+ if (move.isTargetMaterialized()) {
147
+
148
+ const child = move.target;
149
+
150
+ const s = computeNodeSelectionScore(node, child);
151
+
152
+ assert.notNaN(s, 'computed Node score');
153
+ assert.isFinite(s, `computed Node score`);
154
+
155
+ score = s + randomRoll;
156
+ } else {
157
+
158
+ //use a constant value for unexplored nodes
159
+ score = randomRoll * 100;
160
+
161
+ }
162
+
163
+
164
+ if (score > bestScore) {
165
+ bestScore = score;
166
+ bestMove = move;
167
+ }
168
+
169
+ }
170
+
171
+ if (!bestMove.isTargetMaterialized()) {
172
+ //materialize the target state
173
+ materializedEdgeTarget(state, node, bestMove, this.computeTerminalFlag, this.heuristic);
174
+
175
+ } else {
176
+ //just follow the edge
177
+ bestMove.move(state);
178
+ }
179
+
180
+ node = bestMove.target;
181
+ }
182
+
183
+
184
+ return node;
185
+ }
186
+
187
+ /**
188
+ * Perform a playout from the root node
189
+ * @returns {S} final state of the playout
190
+ */
191
+ playout() {
192
+ const computeValidMoves = this.computeValidMoves;
193
+
194
+ const computeTerminalFlag = this.computeTerminalFlag;
195
+
196
+ const state = this.cloneState(this.rootState);
197
+
198
+ assert.notEqual(state, this.rootState, 'cloneState must produce a new state object, instead it produced the same one');
199
+
200
+ let node = this.root;
201
+
202
+ while (!node.isTerminal() && node.depth < this.maxExplorationDepth) {
203
+
204
+ if (!node.isExpanded()) {
205
+ node.expand(state, computeValidMoves, computeTerminalFlag);
206
+ }
207
+
208
+ const child = this.selectRandom(node, state);
209
+
210
+ if (child === node) {
211
+ // prevent infinite recursion
212
+ // this should not happen?
213
+ break;
214
+ }
215
+
216
+ node = child;
217
+ }
218
+
219
+ if (!node.isTerminal() && node.depth >= this.maxExplorationDepth) {
220
+ //cap the state by depth, propagate heuristic score
221
+ node.type = StateType.DepthCapped;
222
+ }
223
+
224
+ // record play-through
225
+ const terminalFlag = node.type;
226
+
227
+ if (terminalFlag === StateType.Win) {
228
+ node.addPlayouts(1, 1, 0);
229
+ } else if (terminalFlag === StateType.Loss) {
230
+ node.addPlayouts(1, 0, 1);
231
+ } else if (terminalFlag === StateType.Tie || terminalFlag === StateType.DepthCapped) {
232
+ node.addPlayouts(1, 0, 0);
233
+ }
234
+
235
+ return state;
236
+ }
237
+ }
238
+
239
+ /**
240
+ * @template S
241
+ * @param {S} state
242
+ * @param {StateNode} source
243
+ * @param {MoveEdge} edge
244
+ * @param {function(S):StateType} computeTerminalFlag
245
+ * @param {function(StateNode, S)} heuristic
246
+ */
247
+ function materializedEdgeTarget(state, source, edge, computeTerminalFlag, heuristic) {
248
+
249
+ const child = new StateNode();
250
+ child.parent = source;
251
+ child.depth = source.depth + 1;
252
+
253
+ const computedState = edge.move(state);
254
+
255
+ const terminalFlag = computeTerminalFlag(computedState);
256
+
257
+ assert.enum(terminalFlag, StateType, 'terminalFlag');
258
+
259
+ child.type = terminalFlag;
260
+
261
+ edge.target = child;
262
+
263
+ const childHeuristicScore = heuristic(child, computedState);
264
+
265
+ assert.notNaN(childHeuristicScore, 'childHeuristicScore');
266
+
267
+ child.heuristicValue = childHeuristicScore;
268
+
269
+
270
+ // bubble the heuristic score up the tree
271
+ // child.bubbleUpHeuristicScore(); // heuristic value changes sign depending on the team making the move, so aggregation becomes tricky
272
+
273
+ return computedState;
274
+ }
275
+