@woosh/meep-engine 2.134.4 → 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 (156) 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.d.ts +23 -0
  21. package/src/core/geom/vec3/v3_rigid_align_paired_unit_vectors.d.ts.map +1 -0
  22. package/src/core/geom/vec3/v3_rigid_align_paired_unit_vectors.js +96 -0
  23. package/src/core/graph/layout/box/BoxLayouter.js +7 -7
  24. package/src/core/graph/layout/box/position_box_next_to_box.js +6 -6
  25. package/src/core/math/computeWholeDivisorLow.js +33 -33
  26. package/src/core/math/linalg/eigen/eigen_values_find_spectral_gap.d.ts.map +1 -0
  27. package/src/core/math/linalg/eigen/matrix_eigenvalues_in_place.d.ts +10 -0
  28. package/src/core/math/linalg/eigen/matrix_eigenvalues_in_place.d.ts.map +1 -0
  29. package/src/core/{graph → math/linalg}/eigen/matrix_eigenvalues_in_place.js +8 -7
  30. package/src/core/math/linalg/eigen/matrix_householder_in_place.d.ts.map +1 -0
  31. package/src/core/{graph → math/linalg}/eigen/matrix_householder_in_place.js +11 -5
  32. package/src/core/math/linalg/eigen/matrix_qr_in_place.d.ts +15 -0
  33. package/src/core/math/linalg/eigen/matrix_qr_in_place.d.ts.map +1 -0
  34. package/src/core/{graph → math/linalg}/eigen/matrix_qr_in_place.js +8 -2
  35. package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.d.ts +17 -0
  36. package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.d.ts.map +1 -0
  37. package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.js +107 -0
  38. package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.d.ts +19 -0
  39. package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.d.ts.map +1 -0
  40. package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.js +161 -0
  41. package/src/core/math/linalg/polynomial_real_roots_in_interval.d.ts +15 -0
  42. package/src/core/math/linalg/polynomial_real_roots_in_interval.d.ts.map +1 -0
  43. package/src/core/math/linalg/polynomial_real_roots_in_interval.js +200 -0
  44. package/src/core/math/solveCubic.d.ts +15 -0
  45. package/src/core/math/solveCubic.d.ts.map +1 -0
  46. package/src/core/math/solveCubic.js +82 -0
  47. package/src/core/math/spline/spline3_hermite_bounds_t.d.ts +23 -0
  48. package/src/core/math/spline/spline3_hermite_bounds_t.d.ts.map +1 -0
  49. package/src/core/math/spline/spline3_hermite_bounds_t.js +109 -0
  50. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts +25 -0
  51. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts.map +1 -0
  52. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.js +44 -0
  53. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.d.ts +16 -0
  54. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.d.ts.map +1 -0
  55. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.js +120 -0
  56. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts +11 -0
  57. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts.map +1 -0
  58. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.js +451 -0
  59. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts +12 -0
  60. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts.map +1 -0
  61. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.js +339 -0
  62. package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.d.ts +15 -0
  63. package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.d.ts.map +1 -0
  64. package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.js +21 -0
  65. package/src/core/math/spline/spline3_hermite_to_monomial.d.ts +24 -0
  66. package/src/core/math/spline/spline3_hermite_to_monomial.d.ts.map +1 -0
  67. package/src/core/math/spline/spline3_hermite_to_monomial.js +37 -0
  68. package/src/core/math/spline/v3_computeCatmullRomSplineUniformDistance.js +1 -1
  69. package/src/core/model/node-graph/visual/NodeGraphVisualData.js +1 -1
  70. package/src/core/model/reactive/model/util/createRandomReactiveExpression.js +185 -185
  71. package/src/core/process/delay.js +16 -16
  72. package/src/engine/animation/async/TimeSeries.js +300 -300
  73. package/src/engine/animation/curve/AnimationCurve.d.ts +3 -2
  74. package/src/engine/animation/curve/AnimationCurve.d.ts.map +1 -1
  75. package/src/engine/animation/curve/AnimationCurve.js +3 -2
  76. package/src/engine/animation/curve/draw/position_canvas_to_curve.js +2 -2
  77. package/src/engine/animation/curve/draw/position_curve_to_canvas.js +2 -2
  78. package/src/engine/ecs/fow/shader/FogOfWarRenderer.js +145 -145
  79. package/src/engine/ecs/gui/position/ViewportPositionSystem.js +2 -2
  80. package/src/engine/ecs/parent/entity_node_compute_bounding_box.js +1 -1
  81. package/src/engine/ecs/transform/Transform.d.ts +0 -10
  82. package/src/engine/ecs/transform/Transform.d.ts.map +1 -1
  83. package/src/engine/ecs/transform/Transform.js +0 -12
  84. package/src/engine/graphics/composit/CompositLayer.js +254 -254
  85. package/src/engine/graphics/ecs/mesh-v2/sg_hierarchy_compute_bounding_box_via_parent_entity.js +1 -1
  86. package/src/engine/graphics/ecs/path/tube/build/build_geometry_linear.js +2 -2
  87. package/src/engine/graphics/material/optimization/MaterialOptimizationContext.js +3 -3
  88. package/src/engine/graphics/particles/particular/engine/utils/volume/AttributeValue.js +201 -201
  89. package/src/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +1 -1
  90. package/src/engine/graphics/render/buffer/slot/parameter/ProgramValueSlotParameterSet.js +2 -2
  91. package/src/engine/graphics/render/forward_plus/LightManager.js +1226 -1226
  92. package/src/engine/graphics/render/forward_plus/model/PointLight.js +1 -1
  93. package/src/engine/graphics/sh3/lpv/lpv_obtain_storage_cached_volume.js +1 -1
  94. package/src/engine/graphics/sh3/path_tracer/texture/sample_material.js +2 -2
  95. package/src/engine/graphics/texture/atlas/TextureAtlasDebugger.js +1 -1
  96. package/src/engine/graphics/texture/sampler/HarmonicDiffusionGrid.js +145 -145
  97. package/src/engine/graphics/texture/sampler/serialization/TextureBinaryBufferSerializer.js +2 -2
  98. package/src/engine/intelligence/behavior/ecs/BehaviorComponent.d.ts +2 -6
  99. package/src/engine/intelligence/behavior/ecs/BehaviorComponent.d.ts.map +1 -1
  100. package/src/engine/intelligence/behavior/ecs/BehaviorComponent.js +0 -10
  101. package/src/engine/intelligence/mcts/MonteCarlo.js +275 -275
  102. package/src/engine/navigation/ecs/path_following/PathFollower.js +222 -222
  103. package/src/generation/grid/GridData.js +220 -220
  104. package/src/generation/grid/generation/GridTaskDensityMarkerDistribution.js +385 -385
  105. package/src/view/elements/image/SvgImageView.js +1 -1
  106. package/src/view/elements/windrose/WindRoseDiagram.js +369 -369
  107. package/src/view/minimap/gl/MinimapFogOfWar.js +3 -3
  108. package/src/view/util/DomSizeObserver.js +1 -1
  109. package/src/core/binary/clz32.d.ts +0 -6
  110. package/src/core/binary/clz32.d.ts.map +0 -1
  111. package/src/core/binary/clz32.js +0 -5
  112. package/src/core/binary/type/dataTypeFromTypedArray.d.ts +0 -8
  113. package/src/core/binary/type/dataTypeFromTypedArray.d.ts.map +0 -1
  114. package/src/core/binary/type/dataTypeFromTypedArray.js +0 -11
  115. package/src/core/collection/array/computeHashIntegerArray.d.ts +0 -1
  116. package/src/core/collection/array/computeHashIntegerArray.d.ts.map +0 -1
  117. package/src/core/collection/array/computeHashIntegerArray.js +0 -7
  118. package/src/core/collection/array/typed/typedArrayToDataType.d.ts +0 -6
  119. package/src/core/collection/array/typed/typedArrayToDataType.d.ts.map +0 -1
  120. package/src/core/collection/array/typed/typedArrayToDataType.js +0 -6
  121. package/src/core/geom/3d/mat4/MATRIX_4_IDENTITY.d.ts +0 -6
  122. package/src/core/geom/3d/mat4/MATRIX_4_IDENTITY.d.ts.map +0 -1
  123. package/src/core/geom/3d/mat4/MATRIX_4_IDENTITY.js +0 -7
  124. package/src/core/graph/eigen/eigen_values_find_spectral_gap.d.ts.map +0 -1
  125. package/src/core/graph/eigen/matrix_eigenvalues_in_place.d.ts +0 -8
  126. package/src/core/graph/eigen/matrix_eigenvalues_in_place.d.ts.map +0 -1
  127. package/src/core/graph/eigen/matrix_householder_in_place.d.ts.map +0 -1
  128. package/src/core/graph/eigen/matrix_qr_in_place.d.ts +0 -9
  129. package/src/core/graph/eigen/matrix_qr_in_place.d.ts.map +0 -1
  130. package/src/core/math/spline/cubicCurve.d.ts +0 -6
  131. package/src/core/math/spline/cubicCurve.d.ts.map +0 -1
  132. package/src/core/math/spline/cubicCurve.js +0 -6
  133. package/src/core/math/spline/spline_bezier2.d.ts +0 -6
  134. package/src/core/math/spline/spline_bezier2.d.ts.map +0 -1
  135. package/src/core/math/spline/spline_bezier2.js +0 -6
  136. package/src/core/math/spline/spline_bezier3.d.ts +0 -6
  137. package/src/core/math/spline/spline_bezier3.d.ts.map +0 -1
  138. package/src/core/math/spline/spline_bezier3.js +0 -6
  139. package/src/core/math/spline/spline_bezier3_bounds.d.ts +0 -6
  140. package/src/core/math/spline/spline_bezier3_bounds.d.ts.map +0 -1
  141. package/src/core/math/spline/spline_bezier3_bounds.js +0 -6
  142. package/src/core/math/spline/spline_hermite3.d.ts +0 -6
  143. package/src/core/math/spline/spline_hermite3.d.ts.map +0 -1
  144. package/src/core/math/spline/spline_hermite3.js +0 -6
  145. package/src/core/math/spline/spline_hermite3_bounds.d.ts +0 -6
  146. package/src/core/math/spline/spline_hermite3_bounds.d.ts.map +0 -1
  147. package/src/core/math/spline/spline_hermite3_bounds.js +0 -6
  148. package/src/core/math/spline/spline_hermite3_to_bezier.d.ts +0 -2
  149. package/src/core/math/spline/spline_hermite3_to_bezier.d.ts.map +0 -1
  150. package/src/core/math/spline/spline_hermite3_to_bezier.js +0 -6
  151. package/src/engine/intelligence/behavior/decorator/RepeatUntilFailureBehavior.d.ts +0 -37
  152. package/src/engine/intelligence/behavior/decorator/RepeatUntilFailureBehavior.d.ts.map +0 -1
  153. package/src/engine/intelligence/behavior/decorator/RepeatUntilFailureBehavior.js +0 -70
  154. /package/src/core/{graph → math/linalg}/eigen/eigen_values_find_spectral_gap.d.ts +0 -0
  155. /package/src/core/{graph → math/linalg}/eigen/eigen_values_find_spectral_gap.js +0 -0
  156. /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
+