volleyballsimtypes 0.0.493 → 0.0.495
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/dist/cjs/src/data/models/competition.d.ts +7 -1
- package/dist/cjs/src/service/player/improvement.d.ts +30 -2
- package/dist/cjs/src/service/player/improvement.js +113 -48
- package/dist/cjs/src/service/player/improvement.test.js +118 -41
- package/dist/cjs/src/service/team/schemas/designated-sub.z.js +5 -1
- package/dist/cjs/src/service/team/schemas/designated-sub.z.test.js +3 -1
- package/dist/cjs/src/stat-config/improvement-floors.json +394 -0
- package/dist/cjs/src/stat-config/improvement-floors.test.d.ts +1 -0
- package/dist/cjs/src/stat-config/improvement-floors.test.js +78 -0
- package/dist/cjs/src/stat-config/index.d.ts +2 -1
- package/dist/cjs/src/stat-config/index.js +5 -1
- package/dist/esm/src/data/models/competition.d.ts +7 -1
- package/dist/esm/src/service/player/improvement.d.ts +30 -2
- package/dist/esm/src/service/player/improvement.js +111 -48
- package/dist/esm/src/service/player/improvement.test.js +120 -43
- package/dist/esm/src/service/team/schemas/designated-sub.z.js +5 -1
- package/dist/esm/src/service/team/schemas/designated-sub.z.test.js +3 -1
- package/dist/esm/src/stat-config/improvement-floors.json +394 -0
- package/dist/esm/src/stat-config/improvement-floors.test.d.ts +1 -0
- package/dist/esm/src/stat-config/improvement-floors.test.js +76 -0
- package/dist/esm/src/stat-config/index.d.ts +2 -1
- package/dist/esm/src/stat-config/index.js +4 -1
- package/package.json +7 -2
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "GENERATED FILE, do not hand-edit. Regenerate with: yarn build && yarn floors",
|
|
3
|
+
"samplesPerRarity": 400000,
|
|
4
|
+
"inputs": {
|
|
5
|
+
"wishlistAdoption": 0.3733,
|
|
6
|
+
"wishlistProcRate": 0.4,
|
|
7
|
+
"rolePickWeights": {
|
|
8
|
+
"OUTSIDE_HITTER": 0.269,
|
|
9
|
+
"MIDDLE_BLOCKER": 0.235,
|
|
10
|
+
"OPPOSITE_HITTER": 0.188,
|
|
11
|
+
"LIBERO": 0.171,
|
|
12
|
+
"SETTER": 0.137
|
|
13
|
+
},
|
|
14
|
+
"measuredFrom": "prod read-only 2026-07-24: 434 Wishlist rows, 162 with roles; role listing shares"
|
|
15
|
+
},
|
|
16
|
+
"sourceHash": "ec4620ca048d05741b9c42ce6f24a4e7bd04f047c5a420bb93f5a5e711d7adeb",
|
|
17
|
+
"floors": [
|
|
18
|
+
{
|
|
19
|
+
"rarity": "COMMON",
|
|
20
|
+
"role": "SETTER",
|
|
21
|
+
"samples": 104658,
|
|
22
|
+
"roleBranchShare": 0,
|
|
23
|
+
"all": 540.2,
|
|
24
|
+
"focus": {
|
|
25
|
+
"ATTACK": 172.3,
|
|
26
|
+
"SET": 136.7,
|
|
27
|
+
"RECEIVE": 172.6,
|
|
28
|
+
"SERVE": 93.9,
|
|
29
|
+
"BLOCK": 175.8,
|
|
30
|
+
"STAMINA": 28.6
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"rarity": "COMMON",
|
|
35
|
+
"role": "OUTSIDE_HITTER",
|
|
36
|
+
"samples": 1886,
|
|
37
|
+
"roleBranchShare": 0,
|
|
38
|
+
"all": 545.7,
|
|
39
|
+
"focus": {
|
|
40
|
+
"ATTACK": 183.8,
|
|
41
|
+
"SET": 100,
|
|
42
|
+
"RECEIVE": 183.7,
|
|
43
|
+
"SERVE": 102.4,
|
|
44
|
+
"BLOCK": 181.6,
|
|
45
|
+
"STAMINA": 32.7
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"rarity": "COMMON",
|
|
50
|
+
"role": "MIDDLE_BLOCKER",
|
|
51
|
+
"samples": 99825,
|
|
52
|
+
"roleBranchShare": 0,
|
|
53
|
+
"all": 541,
|
|
54
|
+
"focus": {
|
|
55
|
+
"ATTACK": 177.9,
|
|
56
|
+
"SET": 113,
|
|
57
|
+
"RECEIVE": 174.5,
|
|
58
|
+
"SERVE": 86.9,
|
|
59
|
+
"BLOCK": 198.3,
|
|
60
|
+
"STAMINA": 30.4
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"rarity": "COMMON",
|
|
65
|
+
"role": "OPPOSITE_HITTER",
|
|
66
|
+
"samples": 85837,
|
|
67
|
+
"roleBranchShare": 0,
|
|
68
|
+
"all": 537,
|
|
69
|
+
"focus": {
|
|
70
|
+
"ATTACK": 200.9,
|
|
71
|
+
"SET": 112,
|
|
72
|
+
"RECEIVE": 172.9,
|
|
73
|
+
"SERVE": 92.6,
|
|
74
|
+
"BLOCK": 170.4,
|
|
75
|
+
"STAMINA": 28.5
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"rarity": "COMMON",
|
|
80
|
+
"role": "LIBERO",
|
|
81
|
+
"samples": 107794,
|
|
82
|
+
"roleBranchShare": 0,
|
|
83
|
+
"all": 541.4,
|
|
84
|
+
"focus": {
|
|
85
|
+
"ATTACK": 173.1,
|
|
86
|
+
"SET": 117.3,
|
|
87
|
+
"RECEIVE": 197.8,
|
|
88
|
+
"SERVE": 86.9,
|
|
89
|
+
"BLOCK": 174.4,
|
|
90
|
+
"STAMINA": 32.1
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"rarity": "RARE",
|
|
95
|
+
"role": "SETTER",
|
|
96
|
+
"samples": 92252,
|
|
97
|
+
"roleBranchShare": 0.0888,
|
|
98
|
+
"all": 865.3,
|
|
99
|
+
"focus": {
|
|
100
|
+
"ATTACK": 269,
|
|
101
|
+
"SET": 239.4,
|
|
102
|
+
"RECEIVE": 299.7,
|
|
103
|
+
"SERVE": 176.2,
|
|
104
|
+
"BLOCK": 299.9,
|
|
105
|
+
"STAMINA": 40.2
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"rarity": "RARE",
|
|
110
|
+
"role": "OUTSIDE_HITTER",
|
|
111
|
+
"samples": 18916,
|
|
112
|
+
"roleBranchShare": 0.8573,
|
|
113
|
+
"all": 691.4,
|
|
114
|
+
"focus": {
|
|
115
|
+
"ATTACK": 237.8,
|
|
116
|
+
"SET": 114.5,
|
|
117
|
+
"RECEIVE": 210.9,
|
|
118
|
+
"SERVE": 119.5,
|
|
119
|
+
"BLOCK": 207.7,
|
|
120
|
+
"STAMINA": 42.7
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"rarity": "RARE",
|
|
125
|
+
"role": "MIDDLE_BLOCKER",
|
|
126
|
+
"samples": 92272,
|
|
127
|
+
"roleBranchShare": 0.1525,
|
|
128
|
+
"all": 899.1,
|
|
129
|
+
"focus": {
|
|
130
|
+
"ATTACK": 306.8,
|
|
131
|
+
"SET": 201.7,
|
|
132
|
+
"RECEIVE": 303.5,
|
|
133
|
+
"SERVE": 149.3,
|
|
134
|
+
"BLOCK": 339.8,
|
|
135
|
+
"STAMINA": 42.6
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"rarity": "RARE",
|
|
140
|
+
"role": "OPPOSITE_HITTER",
|
|
141
|
+
"samples": 100116,
|
|
142
|
+
"roleBranchShare": 0.1141,
|
|
143
|
+
"all": 895.9,
|
|
144
|
+
"focus": {
|
|
145
|
+
"ATTACK": 352.3,
|
|
146
|
+
"SET": 208.1,
|
|
147
|
+
"RECEIVE": 293.3,
|
|
148
|
+
"SERVE": 173.1,
|
|
149
|
+
"BLOCK": 273.9,
|
|
150
|
+
"STAMINA": 40.3
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"rarity": "RARE",
|
|
155
|
+
"role": "LIBERO",
|
|
156
|
+
"samples": 96444,
|
|
157
|
+
"roleBranchShare": 0.1067,
|
|
158
|
+
"all": 886.5,
|
|
159
|
+
"focus": {
|
|
160
|
+
"ATTACK": 272,
|
|
161
|
+
"SET": 225.8,
|
|
162
|
+
"RECEIVE": 349.9,
|
|
163
|
+
"SERVE": 154.5,
|
|
164
|
+
"BLOCK": 301.7,
|
|
165
|
+
"STAMINA": 44.8
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"rarity": "LEGENDARY",
|
|
170
|
+
"role": "SETTER",
|
|
171
|
+
"samples": 68986,
|
|
172
|
+
"roleBranchShare": 0.119,
|
|
173
|
+
"all": 1077.5,
|
|
174
|
+
"focus": {
|
|
175
|
+
"ATTACK": 368.2,
|
|
176
|
+
"SET": 285.7,
|
|
177
|
+
"RECEIVE": 362.8,
|
|
178
|
+
"SERVE": 186.9,
|
|
179
|
+
"BLOCK": 355.3,
|
|
180
|
+
"STAMINA": 48.7
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"rarity": "LEGENDARY",
|
|
185
|
+
"role": "OUTSIDE_HITTER",
|
|
186
|
+
"samples": 16108,
|
|
187
|
+
"roleBranchShare": 0.9988,
|
|
188
|
+
"all": 845.7,
|
|
189
|
+
"focus": {
|
|
190
|
+
"ATTACK": 290.2,
|
|
191
|
+
"SET": 140.7,
|
|
192
|
+
"RECEIVE": 250.7,
|
|
193
|
+
"SERVE": 145.4,
|
|
194
|
+
"BLOCK": 250.5,
|
|
195
|
+
"STAMINA": 49.5
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"rarity": "LEGENDARY",
|
|
200
|
+
"role": "MIDDLE_BLOCKER",
|
|
201
|
+
"samples": 116110,
|
|
202
|
+
"roleBranchShare": 0.1202,
|
|
203
|
+
"all": 1090,
|
|
204
|
+
"focus": {
|
|
205
|
+
"ATTACK": 361.5,
|
|
206
|
+
"SET": 242.2,
|
|
207
|
+
"RECEIVE": 379.6,
|
|
208
|
+
"SERVE": 155.6,
|
|
209
|
+
"BLOCK": 414.7,
|
|
210
|
+
"STAMINA": 49.7
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"rarity": "LEGENDARY",
|
|
215
|
+
"role": "OPPOSITE_HITTER",
|
|
216
|
+
"samples": 83884,
|
|
217
|
+
"roleBranchShare": 0.1336,
|
|
218
|
+
"all": 1098.1,
|
|
219
|
+
"focus": {
|
|
220
|
+
"ATTACK": 428.8,
|
|
221
|
+
"SET": 255.3,
|
|
222
|
+
"RECEIVE": 350.8,
|
|
223
|
+
"SERVE": 182.8,
|
|
224
|
+
"BLOCK": 347.6,
|
|
225
|
+
"STAMINA": 49
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"rarity": "LEGENDARY",
|
|
230
|
+
"role": "LIBERO",
|
|
231
|
+
"samples": 114912,
|
|
232
|
+
"roleBranchShare": 0.089,
|
|
233
|
+
"all": 1098.7,
|
|
234
|
+
"focus": {
|
|
235
|
+
"ATTACK": 358.7,
|
|
236
|
+
"SET": 260.7,
|
|
237
|
+
"RECEIVE": 421.5,
|
|
238
|
+
"SERVE": 157.8,
|
|
239
|
+
"BLOCK": 381.3,
|
|
240
|
+
"STAMINA": 50.2
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"rarity": "MYTHIC",
|
|
245
|
+
"role": "SETTER",
|
|
246
|
+
"samples": 99645,
|
|
247
|
+
"roleBranchShare": 0.0824,
|
|
248
|
+
"all": 1246.4,
|
|
249
|
+
"focus": {
|
|
250
|
+
"ATTACK": 410.6,
|
|
251
|
+
"SET": 324.7,
|
|
252
|
+
"RECEIVE": 413.3,
|
|
253
|
+
"SERVE": 236.4,
|
|
254
|
+
"BLOCK": 404,
|
|
255
|
+
"STAMINA": 66.7
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"rarity": "MYTHIC",
|
|
260
|
+
"role": "OUTSIDE_HITTER",
|
|
261
|
+
"samples": 15991,
|
|
262
|
+
"roleBranchShare": 1,
|
|
263
|
+
"all": 962.4,
|
|
264
|
+
"focus": {
|
|
265
|
+
"ATTACK": 326.1,
|
|
266
|
+
"SET": 157,
|
|
267
|
+
"RECEIVE": 281.9,
|
|
268
|
+
"SERVE": 162.7,
|
|
269
|
+
"BLOCK": 282.2,
|
|
270
|
+
"STAMINA": 66.9
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"rarity": "MYTHIC",
|
|
275
|
+
"role": "MIDDLE_BLOCKER",
|
|
276
|
+
"samples": 92022,
|
|
277
|
+
"roleBranchShare": 0.1535,
|
|
278
|
+
"all": 1251.8,
|
|
279
|
+
"focus": {
|
|
280
|
+
"ATTACK": 393.6,
|
|
281
|
+
"SET": 266.9,
|
|
282
|
+
"RECEIVE": 421.4,
|
|
283
|
+
"SERVE": 210.1,
|
|
284
|
+
"BLOCK": 472.7,
|
|
285
|
+
"STAMINA": 67.2
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"rarity": "MYTHIC",
|
|
290
|
+
"role": "OPPOSITE_HITTER",
|
|
291
|
+
"samples": 99024,
|
|
292
|
+
"roleBranchShare": 0.1126,
|
|
293
|
+
"all": 1272.8,
|
|
294
|
+
"focus": {
|
|
295
|
+
"ATTACK": 485.3,
|
|
296
|
+
"SET": 288.2,
|
|
297
|
+
"RECEIVE": 396.8,
|
|
298
|
+
"SERVE": 234.5,
|
|
299
|
+
"BLOCK": 389.7,
|
|
300
|
+
"STAMINA": 66.7
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
"rarity": "MYTHIC",
|
|
305
|
+
"role": "LIBERO",
|
|
306
|
+
"samples": 93318,
|
|
307
|
+
"roleBranchShare": 0.1088,
|
|
308
|
+
"all": 1267.3,
|
|
309
|
+
"focus": {
|
|
310
|
+
"ATTACK": 391.6,
|
|
311
|
+
"SET": 290.9,
|
|
312
|
+
"RECEIVE": 482.1,
|
|
313
|
+
"SERVE": 215.1,
|
|
314
|
+
"BLOCK": 426.2,
|
|
315
|
+
"STAMINA": 67.5
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"rarity": "SPECIAL",
|
|
320
|
+
"role": "SETTER",
|
|
321
|
+
"samples": 102997,
|
|
322
|
+
"roleBranchShare": 0.0804,
|
|
323
|
+
"all": 1457.3,
|
|
324
|
+
"focus": {
|
|
325
|
+
"ATTACK": 485.2,
|
|
326
|
+
"SET": 360.5,
|
|
327
|
+
"RECEIVE": 481.4,
|
|
328
|
+
"SERVE": 265.7,
|
|
329
|
+
"BLOCK": 475.5,
|
|
330
|
+
"STAMINA": 79
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"rarity": "SPECIAL",
|
|
335
|
+
"role": "OUTSIDE_HITTER",
|
|
336
|
+
"samples": 16546,
|
|
337
|
+
"roleBranchShare": 0.9652,
|
|
338
|
+
"all": 1100.6,
|
|
339
|
+
"focus": {
|
|
340
|
+
"ATTACK": 371.9,
|
|
341
|
+
"SET": 183.9,
|
|
342
|
+
"RECEIVE": 323.6,
|
|
343
|
+
"SERVE": 187.1,
|
|
344
|
+
"BLOCK": 323.7,
|
|
345
|
+
"STAMINA": 79.6
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
"rarity": "SPECIAL",
|
|
350
|
+
"role": "MIDDLE_BLOCKER",
|
|
351
|
+
"samples": 87715,
|
|
352
|
+
"roleBranchShare": 0.1592,
|
|
353
|
+
"all": 1440.2,
|
|
354
|
+
"focus": {
|
|
355
|
+
"ATTACK": 463.7,
|
|
356
|
+
"SET": 309.9,
|
|
357
|
+
"RECEIVE": 482.8,
|
|
358
|
+
"SERVE": 234.3,
|
|
359
|
+
"BLOCK": 522.8,
|
|
360
|
+
"STAMINA": 79.5
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
"rarity": "SPECIAL",
|
|
365
|
+
"role": "OPPOSITE_HITTER",
|
|
366
|
+
"samples": 102830,
|
|
367
|
+
"roleBranchShare": 0.1096,
|
|
368
|
+
"all": 1469.1,
|
|
369
|
+
"focus": {
|
|
370
|
+
"ATTACK": 536.1,
|
|
371
|
+
"SET": 331.8,
|
|
372
|
+
"RECEIVE": 467.1,
|
|
373
|
+
"SERVE": 262.7,
|
|
374
|
+
"BLOCK": 463.9,
|
|
375
|
+
"STAMINA": 79.2
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
"rarity": "SPECIAL",
|
|
380
|
+
"role": "LIBERO",
|
|
381
|
+
"samples": 89912,
|
|
382
|
+
"roleBranchShare": 0.1147,
|
|
383
|
+
"all": 1462.7,
|
|
384
|
+
"focus": {
|
|
385
|
+
"ATTACK": 464.9,
|
|
386
|
+
"SET": 332.7,
|
|
387
|
+
"RECEIVE": 531.3,
|
|
388
|
+
"SERVE": 240.9,
|
|
389
|
+
"BLOCK": 490.4,
|
|
390
|
+
"STAMINA": 80.3
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
]
|
|
394
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, it, expect } from '@jest/globals';
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
import { improvementFloorsJSON, rolesJSON, statCapsJSON } from './index';
|
|
4
|
+
import { RarityEnum, RarityRanges, StaminaRanges } from '../service/player/rarity';
|
|
5
|
+
import { RoleEnum } from '../service/player/role';
|
|
6
|
+
import { StatsEnum, getMultipliers } from '../service/player/stats';
|
|
7
|
+
import { getStatCap } from '../service/player/improvement';
|
|
8
|
+
// improvement-floors.json is GENERATED (scripts/generate-improvement-floors.mjs) by sampling PlayerGenerator.
|
|
9
|
+
// Nothing fails loudly if the generator's inputs change and the file is not regenerated, so the floors would
|
|
10
|
+
// silently misprice every improvement in the game. These are the guards.
|
|
11
|
+
//
|
|
12
|
+
// The drift guard is a HASH OF THE PARSED VALUES the floors depend on, not of any source text: hashing source
|
|
13
|
+
// would fail on a comment edit and force a regeneration that rewrites every number with fresh sampling noise.
|
|
14
|
+
// It therefore catches a retuned role weight, stat cap or rarity range, and does NOT catch a pure logic change
|
|
15
|
+
// inside player-generator.ts. That case is covered by re-running the generator deliberately.
|
|
16
|
+
const RARITIES = [RarityEnum.COMMON, RarityEnum.RARE, RarityEnum.LEGENDARY, RarityEnum.MYTHIC, RarityEnum.SPECIAL];
|
|
17
|
+
const ROLES = [RoleEnum.SETTER, RoleEnum.OUTSIDE_HITTER, RoleEnum.MIDDLE_BLOCKER, RoleEnum.OPPOSITE_HITTER, RoleEnum.LIBERO];
|
|
18
|
+
const FOCI = [StatsEnum.ATTACK, StatsEnum.SET, StatsEnum.RECEIVE, StatsEnum.SERVE, StatsEnum.BLOCK, StatsEnum.STAMINA];
|
|
19
|
+
const REGENERATE = 'improvement-floors.json is stale. Run: yarn build && yarn floors';
|
|
20
|
+
describe('improvement-floors.json', () => {
|
|
21
|
+
it('was generated from the current roles, caps and rarity ranges', () => {
|
|
22
|
+
const canonical = JSON.stringify({
|
|
23
|
+
roles: rolesJSON,
|
|
24
|
+
statCaps: statCapsJSON,
|
|
25
|
+
rarityRanges: RarityRanges,
|
|
26
|
+
staminaRanges: StaminaRanges
|
|
27
|
+
});
|
|
28
|
+
const expected = createHash('sha256').update(canonical).digest('hex');
|
|
29
|
+
expect(`${improvementFloorsJSON.sourceHash} ${REGENERATE}`).toBe(`${expected} ${REGENERATE}`);
|
|
30
|
+
});
|
|
31
|
+
it('has exactly one entry per rarity and role', () => {
|
|
32
|
+
expect(improvementFloorsJSON.floors).toHaveLength(RARITIES.length * ROLES.length);
|
|
33
|
+
for (const rarity of RARITIES) {
|
|
34
|
+
for (const role of ROLES) {
|
|
35
|
+
const cell = improvementFloorsJSON.floors.filter(f => f.rarity === rarity && f.role === role);
|
|
36
|
+
expect(`${rarity}/${role}: ${cell.length}`).toBe(`${rarity}/${role}: 1`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
it('carries a floor for every training focus, matching the focus pools exactly', () => {
|
|
41
|
+
for (const entry of improvementFloorsJSON.floors) {
|
|
42
|
+
const keys = Object.keys(entry.focus).sort();
|
|
43
|
+
expect(`${entry.rarity}/${entry.role}: ${keys.join(',')}`).toBe(`${entry.rarity}/${entry.role}: ${[...FOCI].sort().join(',')}`);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
it('keeps every floor strictly below its ceiling, so no band is degenerate', () => {
|
|
47
|
+
for (const entry of improvementFloorsJSON.floors) {
|
|
48
|
+
const cap = getStatCap(entry.rarity);
|
|
49
|
+
const label = `${entry.rarity}/${entry.role}`;
|
|
50
|
+
expect(`${label} all: ${entry.all < 18 * cap ? 'below' : 'AT OR ABOVE'} ceiling`).toBe(`${label} all: below ceiling`);
|
|
51
|
+
expect(entry.all).toBeGreaterThan(0);
|
|
52
|
+
for (const focus of FOCI) {
|
|
53
|
+
const poolSize = Object.keys(getMultipliers(focus)).length;
|
|
54
|
+
const floor = entry.focus[focus];
|
|
55
|
+
expect(`${label} ${focus}: ${floor < poolSize * cap ? 'below' : 'AT OR ABOVE'} ceiling`).toBe(`${label} ${focus}: below ceiling`);
|
|
56
|
+
expect(floor).toBeGreaterThan(0);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
it('was sampled hard enough for every cell to be trustworthy', () => {
|
|
61
|
+
for (const entry of improvementFloorsJSON.floors) {
|
|
62
|
+
// 500 keeps the standard error of a cell mean well under a stat point; the generator warns below this.
|
|
63
|
+
expect(`${entry.rarity}/${entry.role}: ${entry.samples >= 500 ? 'enough' : `only ${entry.samples}`} samples`)
|
|
64
|
+
.toBe(`${entry.rarity}/${entry.role}: enough samples`);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
it('records the production inputs the mixture was built from', () => {
|
|
68
|
+
expect(improvementFloorsJSON.inputs.wishlistProcRate).toBeGreaterThan(0);
|
|
69
|
+
expect(improvementFloorsJSON.inputs.wishlistAdoption).toBeGreaterThan(0);
|
|
70
|
+
expect(improvementFloorsJSON.inputs.wishlistAdoption).toBeLessThanOrEqual(1);
|
|
71
|
+
// The role branch only exists for RARE and above, so COMMON must be pure generic branch.
|
|
72
|
+
for (const entry of improvementFloorsJSON.floors.filter(f => f.rarity === RarityEnum.COMMON)) {
|
|
73
|
+
expect(`COMMON/${entry.role} roleBranchShare: ${entry.roleBranchShare}`).toBe(`COMMON/${entry.role} roleBranchShare: 0`);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -2,4 +2,5 @@ import rolesJSON from './roles.json';
|
|
|
2
2
|
import traitsJSON from './traits.json';
|
|
3
3
|
import statCapsJSON from './stat-caps.json';
|
|
4
4
|
import declineProfilesJSON from './decline-profiles.json';
|
|
5
|
-
|
|
5
|
+
import improvementFloorsJSON from './improvement-floors.json';
|
|
6
|
+
export { rolesJSON, traitsJSON, statCapsJSON, declineProfilesJSON, improvementFloorsJSON };
|
|
@@ -2,4 +2,7 @@ import rolesJSON from './roles.json';
|
|
|
2
2
|
import traitsJSON from './traits.json';
|
|
3
3
|
import statCapsJSON from './stat-caps.json';
|
|
4
4
|
import declineProfilesJSON from './decline-profiles.json';
|
|
5
|
-
|
|
5
|
+
// GENERATED, never hand-edited. Regenerate with `yarn build && yarn floors`; a stale file is caught by the
|
|
6
|
+
// sourceHash assertion in improvement-floors.test.ts.
|
|
7
|
+
import improvementFloorsJSON from './improvement-floors.json';
|
|
8
|
+
export { rolesJSON, traitsJSON, statCapsJSON, declineProfilesJSON, improvementFloorsJSON };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "volleyballsimtypes",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.495",
|
|
4
4
|
"description": "vbsim types",
|
|
5
5
|
"main": "./dist/cjs/src/index.js",
|
|
6
6
|
"module": "./dist/esm/src/index.js",
|
|
@@ -60,6 +60,10 @@
|
|
|
60
60
|
"import": "./dist/esm/src/stat-config/traits.json",
|
|
61
61
|
"require": "./dist/cjs/src/stat-config/traits.json"
|
|
62
62
|
},
|
|
63
|
+
"./stat-config/improvement-floors.json": {
|
|
64
|
+
"import": "./dist/esm/src/stat-config/improvement-floors.json",
|
|
65
|
+
"require": "./dist/cjs/src/stat-config/improvement-floors.json"
|
|
66
|
+
},
|
|
63
67
|
"./api": {
|
|
64
68
|
"types": "./dist/esm/src/api/index.d.ts",
|
|
65
69
|
"import": "./dist/esm/src/api/index.js",
|
|
@@ -78,7 +82,8 @@
|
|
|
78
82
|
"build": "npm run test-types && yarn clean && tsc -p tsconfig.json && tsc -p tsconfig-cjs.json",
|
|
79
83
|
"sync:consumers": "powershell -NoProfile -ExecutionPolicy Bypass -File ./scripts/sync-consumers.ps1",
|
|
80
84
|
"release": "powershell -NoProfile -ExecutionPolicy Bypass -File ./scripts/release.ps1",
|
|
81
|
-
"export:schema": "powershell -NoProfile -ExecutionPolicy Bypass -File ./scripts/export-schema.ps1"
|
|
85
|
+
"export:schema": "powershell -NoProfile -ExecutionPolicy Bypass -File ./scripts/export-schema.ps1",
|
|
86
|
+
"floors": "node scripts/generate-improvement-floors.mjs"
|
|
82
87
|
},
|
|
83
88
|
"author": "Francisco Farias <fariasfranciscoe@gmail.com>",
|
|
84
89
|
"license": "ISC",
|