toosoon-prng 1.0.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.
- package/.prettierrc +7 -0
- package/LICENSE +21 -0
- package/lib/controllers.d.ts +160 -0
- package/lib/controllers.js +382 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +31 -0
- package/lib/prng.d.ts +28 -0
- package/lib/prng.js +106 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/lib/utils.d.ts +38 -0
- package/lib/utils.js +108 -0
- package/package.json +31 -0
- package/src/controllers.ts +420 -0
- package/src/index.ts +24 -0
- package/src/prng.ts +112 -0
- package/src/utils.ts +106 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import type { Gui, GuiController } from 'toosoon-gui';
|
|
2
|
+
|
|
3
|
+
import prng from './prng';
|
|
4
|
+
|
|
5
|
+
export enum PRNGControllerTypes {
|
|
6
|
+
Boolean = 'Boolean',
|
|
7
|
+
Sign = 'Sign',
|
|
8
|
+
Int = 'Int',
|
|
9
|
+
Float = 'Float',
|
|
10
|
+
HexColor = 'HexColor',
|
|
11
|
+
Item = 'Item',
|
|
12
|
+
ObjectProperty = 'ObjectProperty',
|
|
13
|
+
Weights = 'Weights'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PRNGController<T = unknown> {
|
|
17
|
+
seed: string;
|
|
18
|
+
value: T;
|
|
19
|
+
addGUI(gui: Gui, min?: number, max?: number, step?: number): GuiController;
|
|
20
|
+
getValue(): T;
|
|
21
|
+
dispose(): void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface PRNGGroupController<T = unknown> {
|
|
25
|
+
seed: string;
|
|
26
|
+
addGUI(gui: Gui, min?: number, max?: number, step?: number): Gui;
|
|
27
|
+
createController(index: number): PRNGController<T>;
|
|
28
|
+
getValueAt(index: number): T;
|
|
29
|
+
dispose(): void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// *********************
|
|
33
|
+
// Abstract controllers
|
|
34
|
+
// *********************
|
|
35
|
+
abstract class BasePRNGController<T> implements PRNGController<T> {
|
|
36
|
+
seed: string;
|
|
37
|
+
abstract value: T;
|
|
38
|
+
gui!: GuiController;
|
|
39
|
+
|
|
40
|
+
constructor(seed: string) {
|
|
41
|
+
this.seed = seed;
|
|
42
|
+
prng.addController(this);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
abstract addGUI(gui: Gui, min?: number, max?: number, step?: number): GuiController;
|
|
46
|
+
|
|
47
|
+
abstract getValue(): T;
|
|
48
|
+
|
|
49
|
+
dispose() {
|
|
50
|
+
prng.removeController(this);
|
|
51
|
+
this.gui?.destroy();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
abstract class BasePRNGGroupController<T> implements PRNGGroupController<T> {
|
|
56
|
+
seed: string;
|
|
57
|
+
controllers: PRNGController<T>[] = [];
|
|
58
|
+
gui!: Gui;
|
|
59
|
+
guiArgs!: {
|
|
60
|
+
min?: number;
|
|
61
|
+
max?: number;
|
|
62
|
+
step?: number;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
constructor(seed: string) {
|
|
66
|
+
this.seed = seed;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
addGUI(gui: Gui, min?: number, max?: number, step?: number) {
|
|
70
|
+
this.gui = gui.addFolder(this.seed).close();
|
|
71
|
+
this.guiArgs = { min, max, step };
|
|
72
|
+
return this.gui;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
abstract createController(index: number): PRNGController<T>;
|
|
76
|
+
|
|
77
|
+
getValueAt(index: number): T {
|
|
78
|
+
let controller = this.controllers[index];
|
|
79
|
+
if (!controller) {
|
|
80
|
+
controller = this.createController(index);
|
|
81
|
+
if (this.gui) {
|
|
82
|
+
controller
|
|
83
|
+
.addGUI(this.gui, this.guiArgs.min, this.guiArgs.max, this.guiArgs.step)
|
|
84
|
+
.name(`${this.seed}-${index}`);
|
|
85
|
+
}
|
|
86
|
+
this.controllers[index] = controller;
|
|
87
|
+
}
|
|
88
|
+
return controller.value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
dispose() {
|
|
92
|
+
this.controllers.forEach((controller) => controller.dispose());
|
|
93
|
+
this.controllers = [];
|
|
94
|
+
this.gui?.destroy();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// *********************
|
|
99
|
+
// Controllers
|
|
100
|
+
// *********************
|
|
101
|
+
export class BooleanController extends BasePRNGController<boolean> {
|
|
102
|
+
value: boolean;
|
|
103
|
+
probability: number;
|
|
104
|
+
|
|
105
|
+
constructor(seed: string, probability = 0.5) {
|
|
106
|
+
super(seed);
|
|
107
|
+
|
|
108
|
+
this.probability = probability;
|
|
109
|
+
this.value = this.getValue();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
addGUI(gui: Gui) {
|
|
113
|
+
this.gui = gui.add(this, 'value').name(this.seed);
|
|
114
|
+
return this.gui;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
getValue() {
|
|
118
|
+
this.value = prng.randomBoolean(this.seed, this.probability);
|
|
119
|
+
this.gui?.updateDisplay();
|
|
120
|
+
return this.value;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export class SignController extends BasePRNGController<number> {
|
|
125
|
+
value: number;
|
|
126
|
+
probability: number;
|
|
127
|
+
|
|
128
|
+
constructor(seed: string, probability = 0.5) {
|
|
129
|
+
super(seed);
|
|
130
|
+
|
|
131
|
+
this.probability = probability;
|
|
132
|
+
this.value = this.getValue();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
addGUI(gui: Gui) {
|
|
136
|
+
this.gui = gui.add(this, 'value', [-1, 1]).name(this.seed);
|
|
137
|
+
return this.gui;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
getValue() {
|
|
141
|
+
this.value = prng.randomSign(this.seed, this.probability);
|
|
142
|
+
this.gui?.updateDisplay();
|
|
143
|
+
return this.value;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export class IntController extends BasePRNGController<number> {
|
|
148
|
+
value: number;
|
|
149
|
+
min: number;
|
|
150
|
+
max: number;
|
|
151
|
+
|
|
152
|
+
constructor(seed: string, min = 0, max = 1) {
|
|
153
|
+
super(seed);
|
|
154
|
+
|
|
155
|
+
this.min = min;
|
|
156
|
+
this.max = max;
|
|
157
|
+
this.value = prng.randomInt(this.seed, min, max);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
addGUI(gui: Gui, min: number, max: number, step = 1) {
|
|
161
|
+
this.gui = gui.add(this, 'value', min, max, step).name(this.seed);
|
|
162
|
+
return this.gui;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
getValue() {
|
|
166
|
+
this.value = prng.randomInt(this.seed, this.min, this.max);
|
|
167
|
+
this.gui?.updateDisplay();
|
|
168
|
+
return this.value;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export class FloatController extends BasePRNGController<number> {
|
|
173
|
+
value: number;
|
|
174
|
+
min: number;
|
|
175
|
+
max: number;
|
|
176
|
+
|
|
177
|
+
constructor(seed: string, min = 0, max = 1) {
|
|
178
|
+
super(seed);
|
|
179
|
+
|
|
180
|
+
this.min = min;
|
|
181
|
+
this.max = max;
|
|
182
|
+
this.value = this.getValue();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
addGUI(gui: Gui, min: number, max: number, step: number = 0.01) {
|
|
186
|
+
this.gui = gui.add(this, 'value', min, max, step).name(this.seed);
|
|
187
|
+
return this.gui;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
getValue() {
|
|
191
|
+
this.value = prng.randomFloat(this.seed, this.min, this.max);
|
|
192
|
+
this.gui?.updateDisplay();
|
|
193
|
+
return this.value;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export class HexColorController extends BasePRNGController<string> {
|
|
198
|
+
value: string;
|
|
199
|
+
|
|
200
|
+
constructor(seed: string) {
|
|
201
|
+
super(seed);
|
|
202
|
+
|
|
203
|
+
this.value = this.getValue();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
addGUI(gui: Gui) {
|
|
207
|
+
this.gui = gui.addColor(this, 'value').name(this.seed);
|
|
208
|
+
return this.gui;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
getValue() {
|
|
212
|
+
this.value = prng.randomHexColor(this.seed);
|
|
213
|
+
this.gui?.updateDisplay();
|
|
214
|
+
return this.value;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export class ItemController<T = unknown> extends BasePRNGController<T> {
|
|
219
|
+
value: T;
|
|
220
|
+
items: T[];
|
|
221
|
+
|
|
222
|
+
constructor(seed: string, items: T[]) {
|
|
223
|
+
super(seed);
|
|
224
|
+
|
|
225
|
+
this.items = items;
|
|
226
|
+
this.value = this.getValue();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
addGUI(gui: Gui) {
|
|
230
|
+
this.gui = gui.add(this, 'value', this.items).name(this.seed);
|
|
231
|
+
return this.gui;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
getValue() {
|
|
235
|
+
this.value = prng.randomItem(this.seed, this.items) as T;
|
|
236
|
+
this.gui?.updateDisplay();
|
|
237
|
+
return this.value;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export class ObjectPropertyController<T = unknown> extends BasePRNGController<T> {
|
|
242
|
+
value: T;
|
|
243
|
+
object: Object;
|
|
244
|
+
|
|
245
|
+
constructor(seed: string, object: Object) {
|
|
246
|
+
super(seed);
|
|
247
|
+
|
|
248
|
+
this.object = object;
|
|
249
|
+
this.value = this.getValue();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
addGUI(gui: Gui) {
|
|
253
|
+
this.gui = gui.add(this, 'value', this.object).name(this.seed);
|
|
254
|
+
return this.gui;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
getValue() {
|
|
258
|
+
this.value = prng.randomObjectProperty(this.seed, this.object) as T;
|
|
259
|
+
this.gui?.updateDisplay();
|
|
260
|
+
return this.value;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
type WeightsItems<T> = Array<{ weight: number; value: T }>;
|
|
265
|
+
|
|
266
|
+
export class WeightsController<T = unknown> extends BasePRNGController<T> {
|
|
267
|
+
value: T;
|
|
268
|
+
items: WeightsItems<T>;
|
|
269
|
+
weights: number[];
|
|
270
|
+
|
|
271
|
+
constructor(seed: string, items: WeightsItems<T>) {
|
|
272
|
+
super(seed);
|
|
273
|
+
|
|
274
|
+
this.items = items;
|
|
275
|
+
this.weights = this.items.map((item) => item.weight);
|
|
276
|
+
this.value = this.getValue();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
addGUI(gui: Gui) {
|
|
280
|
+
this.gui = gui
|
|
281
|
+
.add(
|
|
282
|
+
this,
|
|
283
|
+
'value',
|
|
284
|
+
this.items.map((item) => item.value)
|
|
285
|
+
)
|
|
286
|
+
.name(this.seed);
|
|
287
|
+
return this.gui;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
getValue() {
|
|
291
|
+
const index = prng.randomIndex(this.seed, this.weights);
|
|
292
|
+
this.value = this.items[index].value;
|
|
293
|
+
this.gui?.updateDisplay();
|
|
294
|
+
return this.value;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// *********************
|
|
299
|
+
// Group Controllers
|
|
300
|
+
// *********************
|
|
301
|
+
export class BooleanGroupController extends BasePRNGGroupController<boolean> {
|
|
302
|
+
probability: number;
|
|
303
|
+
controllers: BooleanController[] = [];
|
|
304
|
+
|
|
305
|
+
constructor(seed: string, probability: number) {
|
|
306
|
+
super(seed);
|
|
307
|
+
|
|
308
|
+
this.probability = probability;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
createController(index: number) {
|
|
312
|
+
return new BooleanController(`${this.seed}-${index}`, this.probability);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export class SignGroupController extends BasePRNGGroupController<number> {
|
|
317
|
+
probability: number;
|
|
318
|
+
controllers: SignController[] = [];
|
|
319
|
+
|
|
320
|
+
constructor(seed: string, probability: number) {
|
|
321
|
+
super(seed);
|
|
322
|
+
|
|
323
|
+
this.probability = probability;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
createController(index: number) {
|
|
327
|
+
return new SignController(`${this.seed}-${index}`, this.probability);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export class IntGroupController extends BasePRNGGroupController<number> {
|
|
332
|
+
min: number;
|
|
333
|
+
max: number;
|
|
334
|
+
controllers: IntController[] = [];
|
|
335
|
+
|
|
336
|
+
constructor(seed: string, min: number, max: number) {
|
|
337
|
+
super(seed);
|
|
338
|
+
|
|
339
|
+
this.min = min;
|
|
340
|
+
this.max = max;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
createController(index: number) {
|
|
344
|
+
return new IntController(`${this.seed}-${index}`, this.min, this.max);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export class FloatGroupController extends BasePRNGGroupController<number> {
|
|
349
|
+
min: number;
|
|
350
|
+
max: number;
|
|
351
|
+
controllers: FloatController[] = [];
|
|
352
|
+
|
|
353
|
+
constructor(seed: string, min: number, max: number) {
|
|
354
|
+
super(seed);
|
|
355
|
+
|
|
356
|
+
this.min = min;
|
|
357
|
+
this.max = max;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
createController(index: number) {
|
|
361
|
+
return new FloatController(`${this.seed}-${index}`, this.min, this.max);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export class HexColorGroupController extends BasePRNGGroupController<string> {
|
|
366
|
+
controllers: HexColorController[] = [];
|
|
367
|
+
|
|
368
|
+
// constructor(seed: string) {
|
|
369
|
+
// super(seed);
|
|
370
|
+
// }
|
|
371
|
+
|
|
372
|
+
createController(index: number) {
|
|
373
|
+
return new HexColorController(`${this.seed}-${index}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export class ItemGroupController<T = unknown> extends BasePRNGGroupController<T> {
|
|
378
|
+
items: T[];
|
|
379
|
+
controllers: ItemController<T>[] = [];
|
|
380
|
+
|
|
381
|
+
constructor(seed: string, items: T[]) {
|
|
382
|
+
super(seed);
|
|
383
|
+
|
|
384
|
+
this.items = items;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
createController(index: number) {
|
|
388
|
+
return new ItemController<T>(`${this.seed}-${index}`, this.items);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export class ObjectPropertyGroupController<T = unknown> extends BasePRNGGroupController<T> {
|
|
393
|
+
object: Object;
|
|
394
|
+
controllers: ObjectPropertyController<T>[] = [];
|
|
395
|
+
|
|
396
|
+
constructor(seed: string, object: Object) {
|
|
397
|
+
super(seed);
|
|
398
|
+
|
|
399
|
+
this.object = object;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
createController(index: number) {
|
|
403
|
+
return new ObjectPropertyController<T>(`${this.seed}-${index}`, this.object);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export class WeightsGroupController<T = unknown> extends BasePRNGGroupController<T> {
|
|
408
|
+
items: WeightsItems<T>;
|
|
409
|
+
controllers: WeightsController<T>[] = [];
|
|
410
|
+
|
|
411
|
+
constructor(seed: string, items: WeightsItems<T>) {
|
|
412
|
+
super(seed);
|
|
413
|
+
|
|
414
|
+
this.items = items;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
createController(index: number) {
|
|
418
|
+
return new WeightsController<T>(`${this.seed}-${index}`, this.items);
|
|
419
|
+
}
|
|
420
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export {
|
|
2
|
+
BooleanController,
|
|
3
|
+
SignController,
|
|
4
|
+
IntController,
|
|
5
|
+
FloatController,
|
|
6
|
+
HexColorController,
|
|
7
|
+
ItemController,
|
|
8
|
+
ObjectPropertyController,
|
|
9
|
+
WeightsController,
|
|
10
|
+
BooleanGroupController,
|
|
11
|
+
SignGroupController,
|
|
12
|
+
IntGroupController,
|
|
13
|
+
FloatGroupController,
|
|
14
|
+
HexColorGroupController,
|
|
15
|
+
ItemGroupController,
|
|
16
|
+
ObjectPropertyGroupController,
|
|
17
|
+
WeightsGroupController
|
|
18
|
+
} from './controllers';
|
|
19
|
+
|
|
20
|
+
export { cyrb128, sfc32, mulberry32, jsf32, xoshiro128ss } from './utils';
|
|
21
|
+
|
|
22
|
+
export type { PRNGControllerTypes, PRNGController, PRNGGroupController } from './controllers';
|
|
23
|
+
|
|
24
|
+
export { default } from './prng';
|
package/src/prng.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { PRNGController } from './controllers';
|
|
2
|
+
import { cyrb128, jsf32, mulberry32, sfc32, xoshiro128ss } from './utils';
|
|
3
|
+
|
|
4
|
+
export enum PRNGMethod {
|
|
5
|
+
jsf32 = 'jsf32',
|
|
6
|
+
mulberry32 = 'mulberry32',
|
|
7
|
+
sfc32 = 'sfc32',
|
|
8
|
+
xoshiro128ss = 'xoshiro128**'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class PRNG {
|
|
12
|
+
public seed: string = '';
|
|
13
|
+
public method: PRNGMethod = PRNGMethod.sfc32;
|
|
14
|
+
public controllers: PRNGController[] = [];
|
|
15
|
+
|
|
16
|
+
constructor(seed: string = '') {
|
|
17
|
+
this.setSeed(seed);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public addController(controller: PRNGController) {
|
|
21
|
+
this.controllers.push(controller);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public removeController(controller: PRNGController) {
|
|
25
|
+
const index = this.controllers.indexOf(controller);
|
|
26
|
+
this.controllers.splice(index, 1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public setSeed(seed: string) {
|
|
30
|
+
if (this.seed === seed) return;
|
|
31
|
+
|
|
32
|
+
this.seed = seed;
|
|
33
|
+
|
|
34
|
+
this.controllers.forEach((controller) => {
|
|
35
|
+
controller.getValue();
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public setMethod(method: PRNGMethod) {
|
|
40
|
+
this.method = method;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public random(seed: string): number {
|
|
44
|
+
const hashes = cyrb128(this.seed + seed);
|
|
45
|
+
switch (this.method) {
|
|
46
|
+
case PRNGMethod.jsf32:
|
|
47
|
+
return jsf32(hashes[0], hashes[1], hashes[2], hashes[3]);
|
|
48
|
+
case PRNGMethod.mulberry32:
|
|
49
|
+
return mulberry32(hashes[0]);
|
|
50
|
+
case PRNGMethod.sfc32:
|
|
51
|
+
return sfc32(hashes[0], hashes[1], hashes[2], hashes[3]);
|
|
52
|
+
case PRNGMethod.xoshiro128ss:
|
|
53
|
+
return xoshiro128ss(hashes[0], hashes[1], hashes[2], hashes[3]);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public randomBoolean(seed: string, probability: number = 0.5): boolean {
|
|
58
|
+
return this.random(seed) < probability;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public randomSign(seed: string, probability: number = 0.5): number {
|
|
62
|
+
return this.randomBoolean(seed, probability) ? 1 : -1;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public randomInt(seed: string, min: number, max: number) {
|
|
66
|
+
return Math.floor(this.random(seed) * (max - min + 1) + min);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public randomFloat(seed: string, min: number = 0, max: number = 1, precision: number = 2): number {
|
|
70
|
+
return parseFloat(Math.min(min + this.random(seed) * (max - min), max).toFixed(precision));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public randomHexColor(seed: string): string {
|
|
74
|
+
return '#' + ('00000' + ((this.random(seed) * (1 << 24)) | 0).toString(16)).slice(-6);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public randomItem<T = unknown>(seed: string, array: T[] = []): T | undefined {
|
|
78
|
+
if (array.length === 0) return undefined;
|
|
79
|
+
return array[this.randomInt(seed, 0, array.length - 1)];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public randomObjectProperty(seed: string, object: object): unknown | undefined {
|
|
83
|
+
const keys = Object.keys(object);
|
|
84
|
+
const key = this.randomItem(seed, keys);
|
|
85
|
+
if (key && object.hasOwnProperty(key)) {
|
|
86
|
+
return object[key as keyof object];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public randomIndex(seed: string, weights: number[] = []): number {
|
|
91
|
+
if (weights.length === 0) return -1;
|
|
92
|
+
|
|
93
|
+
let totalWeight = 0;
|
|
94
|
+
for (let weight of weights) {
|
|
95
|
+
totalWeight += weight;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (totalWeight <= 0) console.warn('PRNG.randomIndex()', 'Weights must sum to > 0', totalWeight);
|
|
99
|
+
|
|
100
|
+
let random = this.random(seed) * totalWeight;
|
|
101
|
+
for (let i = 0; i < weights.length; i++) {
|
|
102
|
+
if (random < weights[i]) return i;
|
|
103
|
+
random -= weights[i];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const prng = new PRNG();
|
|
111
|
+
|
|
112
|
+
export default prng;
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Seeding random number generator
|
|
2
|
+
// https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Produce a 128-bit hash value from a string
|
|
6
|
+
*
|
|
7
|
+
* @param {string} seed
|
|
8
|
+
* @return {number}
|
|
9
|
+
*/
|
|
10
|
+
export function cyrb128(seed: string): number[] {
|
|
11
|
+
let h1 = 1779033703;
|
|
12
|
+
let h2 = 3144134277;
|
|
13
|
+
let h3 = 1013904242;
|
|
14
|
+
let h4 = 2773480762;
|
|
15
|
+
for (let i = 0, k; i < seed.length; i++) {
|
|
16
|
+
k = seed.charCodeAt(i);
|
|
17
|
+
h1 = h2 ^ Math.imul(h1 ^ k, 597399067);
|
|
18
|
+
h2 = h3 ^ Math.imul(h2 ^ k, 2869860233);
|
|
19
|
+
h3 = h4 ^ Math.imul(h3 ^ k, 951274213);
|
|
20
|
+
h4 = h1 ^ Math.imul(h4 ^ k, 2716044179);
|
|
21
|
+
}
|
|
22
|
+
h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067);
|
|
23
|
+
h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233);
|
|
24
|
+
h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213);
|
|
25
|
+
h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179);
|
|
26
|
+
return [(h1 ^ h2 ^ h3 ^ h4) >>> 0, (h2 ^ h1) >>> 0, (h3 ^ h1) >>> 0, (h4 ^ h1) >>> 0];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ******************************************
|
|
30
|
+
// Pseudo-random Number Generator
|
|
31
|
+
// ******************************************
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Simple Fast Counter, Generator with a 128-bit state
|
|
35
|
+
*
|
|
36
|
+
* @param {number} a
|
|
37
|
+
* @param {number} b
|
|
38
|
+
* @param {number} c
|
|
39
|
+
* @param {number} d
|
|
40
|
+
* @return {Function}
|
|
41
|
+
*/
|
|
42
|
+
export function sfc32(a: number, b: number, c: number, d: number): number {
|
|
43
|
+
a >>>= 0;
|
|
44
|
+
b >>>= 0;
|
|
45
|
+
c >>>= 0;
|
|
46
|
+
d >>>= 0;
|
|
47
|
+
let t = (a + b) | 0;
|
|
48
|
+
a = b ^ (b >>> 9);
|
|
49
|
+
b = (c + (c << 3)) | 0;
|
|
50
|
+
c = (c << 21) | (c >>> 11);
|
|
51
|
+
d = (d + 1) | 0;
|
|
52
|
+
t = (t + d) | 0;
|
|
53
|
+
c = (c + t) | 0;
|
|
54
|
+
return (t >>> 0) / 4294967296;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Mulberry32, Generator with a 32-bit state
|
|
59
|
+
*
|
|
60
|
+
* @param {number} a
|
|
61
|
+
* @return {Function}
|
|
62
|
+
*/
|
|
63
|
+
export function mulberry32(a: number): number {
|
|
64
|
+
let t = (a += 0x6d2b79f5);
|
|
65
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
66
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
67
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Jenkins' Small Fast, Generator with a 32-bit state
|
|
72
|
+
*
|
|
73
|
+
* @param {number} a
|
|
74
|
+
* @return {Function}
|
|
75
|
+
*/
|
|
76
|
+
export function jsf32(a: number, b: number, c: number, d: number): number {
|
|
77
|
+
a |= 0;
|
|
78
|
+
b |= 0;
|
|
79
|
+
c |= 0;
|
|
80
|
+
d |= 0;
|
|
81
|
+
let t = (a - ((b << 27) | (b >>> 5))) | 0;
|
|
82
|
+
a = b ^ ((c << 17) | (c >>> 15));
|
|
83
|
+
b = (c + d) | 0;
|
|
84
|
+
c = (d + t) | 0;
|
|
85
|
+
d = (a + t) | 0;
|
|
86
|
+
return (d >>> 0) / 4294967296;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* xoshiro128**, Generator with a 128-bit state
|
|
91
|
+
*
|
|
92
|
+
* @param {number} a
|
|
93
|
+
* @return {Function}
|
|
94
|
+
*/
|
|
95
|
+
export function xoshiro128ss(a: number, b: number, c: number, d: number): number {
|
|
96
|
+
let t = b << 9;
|
|
97
|
+
let r = a * 5;
|
|
98
|
+
r = ((r << 7) | (r >>> 25)) * 9;
|
|
99
|
+
c ^= a;
|
|
100
|
+
d ^= b;
|
|
101
|
+
b ^= c;
|
|
102
|
+
a ^= d;
|
|
103
|
+
c ^= t;
|
|
104
|
+
d = (d << 11) | (d >>> 21);
|
|
105
|
+
return (r >>> 0) / 4294967296;
|
|
106
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"include": ["src"],
|
|
3
|
+
"exclude": ["node_modules"],
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"baseUrl": ".",
|
|
6
|
+
"outDir": "lib",
|
|
7
|
+
"target": "es5",
|
|
8
|
+
"module": "commonjs",
|
|
9
|
+
"lib": ["DOM", "ESNext"],
|
|
10
|
+
"allowJs": true,
|
|
11
|
+
"allowSyntheticDefaultImports": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"experimentalDecorators": true,
|
|
15
|
+
"forceConsistentCasingInFileNames": true,
|
|
16
|
+
"isolatedModules": true,
|
|
17
|
+
"incremental": true,
|
|
18
|
+
"noUnusedParameters": true,
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"resolveJsonModule": true,
|
|
21
|
+
"skipLibCheck": true,
|
|
22
|
+
"strict": true
|
|
23
|
+
}
|
|
24
|
+
}
|