1 | import { factory } from '../../utils/factory'
|
2 | import { isNumber } from '../../utils/is'
|
3 | import { arraySize } from '../../utils/array'
|
4 | import { createRng } from './util/seededRNG'
|
5 |
|
6 | const name = 'pickRandom'
|
7 | const dependencies = ['typed', 'config', '?on']
|
8 |
|
9 | export const createPickRandom = factory(name, dependencies, ({ typed, config, on }) => {
|
10 |
|
11 | let rng = createRng(config.randomSeed)
|
12 |
|
13 | if (on) {
|
14 | on('config', function (curr, prev) {
|
15 | if (curr.randomSeed !== prev.randomSeed) {
|
16 | rng = createRng(curr.randomSeed)
|
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 | return typed({
|
52 | 'Array | Matrix': function (possibles) {
|
53 | return _pickRandom(possibles)
|
54 | },
|
55 |
|
56 | 'Array | Matrix, number': function (possibles, number) {
|
57 | return _pickRandom(possibles, number, undefined)
|
58 | },
|
59 |
|
60 | 'Array | Matrix, Array': function (possibles, weights) {
|
61 | return _pickRandom(possibles, undefined, weights)
|
62 | },
|
63 |
|
64 | 'Array | Matrix, Array | Matrix, number': function (possibles, weights, number) {
|
65 | return _pickRandom(possibles, number, weights)
|
66 | },
|
67 |
|
68 | 'Array | Matrix, number, Array | Matrix': function (possibles, number, weights) {
|
69 | return _pickRandom(possibles, number, weights)
|
70 | }
|
71 | })
|
72 |
|
73 | function _pickRandom (possibles, number, weights) {
|
74 | const single = (typeof number === 'undefined')
|
75 | if (single) {
|
76 | number = 1
|
77 | }
|
78 |
|
79 | possibles = possibles.valueOf()
|
80 | if (weights) {
|
81 | weights = weights.valueOf()
|
82 | }
|
83 |
|
84 | if (arraySize(possibles).length > 1) {
|
85 | throw new Error('Only one dimensional vectors supported')
|
86 | }
|
87 |
|
88 | let totalWeights = 0
|
89 |
|
90 | if (typeof weights !== 'undefined') {
|
91 | if (weights.length !== possibles.length) {
|
92 | throw new Error('Weights must have the same length as possibles')
|
93 | }
|
94 |
|
95 | for (let i = 0, len = weights.length; i < len; i++) {
|
96 | if (!isNumber(weights[i]) || weights[i] < 0) {
|
97 | throw new Error('Weights must be an array of positive numbers')
|
98 | }
|
99 |
|
100 | totalWeights += weights[i]
|
101 | }
|
102 | }
|
103 |
|
104 | const length = possibles.length
|
105 |
|
106 | if (length === 0) {
|
107 | return []
|
108 | } else if (number >= length) {
|
109 | return number > 1 ? possibles : possibles[0]
|
110 | }
|
111 |
|
112 | const result = []
|
113 | let pick
|
114 |
|
115 | while (result.length < number) {
|
116 | if (typeof weights === 'undefined') {
|
117 | pick = possibles[Math.floor(rng() * length)]
|
118 | } else {
|
119 | let randKey = rng() * totalWeights
|
120 |
|
121 | for (let i = 0, len = possibles.length; i < len; i++) {
|
122 | randKey -= weights[i]
|
123 |
|
124 | if (randKey < 0) {
|
125 | pick = possibles[i]
|
126 | break
|
127 | }
|
128 | }
|
129 | }
|
130 |
|
131 | if (result.indexOf(pick) === -1) {
|
132 | result.push(pick)
|
133 | }
|
134 | }
|
135 |
|
136 | return single ? result[0] : result
|
137 |
|
138 |
|
139 |
|
140 | }
|
141 | })
|