@ak--47/dungeon-master 1.0.0 → 1.2.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/README.md +7 -7
- package/dungeons/technical/ad-spend-schema.json +128 -0
- package/dungeons/technical/ad-spend.js +116 -0
- package/dungeons/technical/anonymous-users-schema.json +92 -0
- package/dungeons/technical/anonymous-users.js +102 -0
- package/dungeons/technical/array-of-object-lookup-schema.json +191 -0
- package/dungeons/{array-of-object-lookup.js → technical/array-of-object-lookup.js} +12 -5
- package/dungeons/technical/experiments-schema.json +203 -0
- package/dungeons/technical/experiments.js +168 -0
- package/dungeons/technical/foobar-schema.json +362 -0
- package/dungeons/{foobar.js → technical/foobar.js} +3 -3
- package/dungeons/technical/group-analytics-schema.json +241 -0
- package/dungeons/technical/group-analytics.js +151 -0
- package/dungeons/technical/mirror-strategies-schema.json +84 -0
- package/dungeons/technical/mirror-strategies.js +126 -0
- package/dungeons/technical/nested-objects-schema.json +145 -0
- package/dungeons/technical/nested-objects.js +207 -0
- package/dungeons/technical/retention-cadence-schema.json +37 -0
- package/dungeons/{retention-cadence.js → technical/retention-cadence.js} +2 -3
- package/dungeons/technical/sanity-schema.json +185 -0
- package/dungeons/{sanity.js → technical/sanity.js} +4 -2
- package/dungeons/technical/scale-test-schema.json +70 -0
- package/dungeons/technical/scale-test.js +90 -0
- package/dungeons/technical/scd-schema.json +467 -0
- package/dungeons/{scd.js → technical/scd.js} +20 -10
- package/dungeons/technical/simple-schema.json +362 -0
- package/dungeons/{simple.js → technical/simple.js} +7 -7
- package/dungeons/technical/simplest-schema.json +724 -0
- package/dungeons/{simplest.js → technical/simplest.js} +7 -7
- package/dungeons/technical/text-generation-schema.json +1062 -0
- package/dungeons/{text-generation.js → technical/text-generation.js} +24 -11
- package/dungeons/vertical/community-schema.json +579 -0
- package/dungeons/vertical/community.js +794 -0
- package/dungeons/vertical/devtools-schema.json +601 -0
- package/dungeons/vertical/devtools.js +843 -0
- package/dungeons/vertical/ecommerce-schema.json +604 -0
- package/dungeons/{ecommerce.js → vertical/ecommerce.js} +43 -5
- package/dungeons/vertical/education-schema.json +5686 -0
- package/dungeons/{education.js → vertical/education.js} +70 -39
- package/dungeons/vertical/fintech-schema.json +630 -0
- package/dungeons/{fintech.js → vertical/fintech.js} +107 -56
- package/dungeons/vertical/fitness-schema.json +530 -0
- package/dungeons/vertical/fitness.js +808 -0
- package/dungeons/vertical/food-delivery-schema.json +36728 -0
- package/dungeons/{food.js → vertical/food-delivery.js} +67 -26
- package/dungeons/vertical/gaming-schema.json +438 -0
- package/dungeons/{gaming.js → vertical/gaming.js} +29 -16
- package/dungeons/vertical/healthcare-schema.json +549 -0
- package/dungeons/vertical/healthcare.js +775 -0
- package/dungeons/vertical/insurance-application-schema.json +485 -0
- package/dungeons/{insurance-application.js → vertical/insurance-application.js} +41 -31
- package/dungeons/vertical/logistics-schema.json +574 -0
- package/dungeons/vertical/logistics.js +799 -0
- package/dungeons/vertical/marketplace-schema.json +533 -0
- package/dungeons/vertical/marketplace.js +850 -0
- package/dungeons/vertical/media-schema.json +4749 -0
- package/dungeons/{media.js → vertical/media.js} +103 -19
- package/dungeons/vertical/rpg-schema.json +2491 -0
- package/dungeons/{rpg.js → vertical/rpg.js} +87 -60
- package/dungeons/vertical/sass-schema.json +3128 -0
- package/dungeons/{sass.js → vertical/sass.js} +65 -26
- package/dungeons/vertical/social-schema.json +620 -0
- package/dungeons/{social.js → vertical/social.js} +76 -17
- package/dungeons/vertical/travel-schema.json +580 -0
- package/dungeons/vertical/travel.js +742 -0
- package/index.js +20 -16
- package/lib/core/config-validator.js +286 -1
- package/lib/core/context.js +16 -5
- package/lib/core/storage.js +2 -0
- package/lib/generators/adspend.js +3 -3
- package/lib/generators/events.js +141 -14
- package/lib/generators/funnels.js +67 -7
- package/lib/generators/text.js +2 -2
- package/lib/orchestrators/user-loop.js +444 -22
- package/lib/templates/soup-presets.js +2 -2
- package/lib/utils/utils.js +4 -5
- package/package.json +4 -3
- package/scripts/dungeon-to-json.mjs +163 -0
- package/scripts/extract-dungeon-schema.mjs +343 -0
- package/scripts/json-to-dungeon.mjs +200 -0
- package/scripts/run-dungeon.mjs +76 -0
- package/scripts/run-many.mjs +118 -0
- package/scripts/verify-runner.mjs +42 -0
- package/types.d.ts +316 -1
- package/dungeons/array-of-object-lookup-schema.json +0 -327
- package/dungeons/ecommerce-schema.json +0 -462
- package/dungeons/education-schema.json +0 -2409
- package/dungeons/fintech-schema.json +0 -14034
- package/dungeons/foobar-schema.json +0 -403
- package/dungeons/food-delivery-schema.json +0 -192
- package/dungeons/food-delivery.js +0 -602
- package/dungeons/food-schema.json +0 -1152
- package/dungeons/gaming-schema.json +0 -1270
- package/dungeons/insurance-application-schema.json +0 -204
- package/dungeons/media-schema.json +0 -906
- package/dungeons/retention-cadence-schema.json +0 -78
- package/dungeons/rpg-schema.json +0 -4526
- package/dungeons/sanity-schema.json +0 -255
- package/dungeons/sass-schema.json +0 -1291
- package/dungeons/scd-schema.json +0 -919
- package/dungeons/simple-schema.json +0 -608
- package/dungeons/simplest-schema.json +0 -1418
- package/dungeons/social-schema.json +0 -1118
- package/dungeons/text-generation-schema.json +0 -3096
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import dayjs from "dayjs";
|
|
2
2
|
import utc from "dayjs/plugin/utc.js";
|
|
3
3
|
import "dotenv/config";
|
|
4
|
-
import * as u from "
|
|
4
|
+
import * as u from "../../lib/utils/utils.js";
|
|
5
5
|
import * as v from "ak-tools";
|
|
6
6
|
|
|
7
7
|
const SEED = "harness-gaming";
|
|
@@ -10,7 +10,7 @@ const chance = u.initChance(SEED);
|
|
|
10
10
|
const num_users = 5_000;
|
|
11
11
|
const days = 100;
|
|
12
12
|
|
|
13
|
-
/** @typedef {import("
|
|
13
|
+
/** @typedef {import("../../types").Dungeon} Config */
|
|
14
14
|
|
|
15
15
|
/*
|
|
16
16
|
* ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -186,7 +186,6 @@ const config = {
|
|
|
186
186
|
|
|
187
187
|
hasAvatar: true,
|
|
188
188
|
|
|
189
|
-
batchSize: 2_500_000,
|
|
190
189
|
concurrency: 1,
|
|
191
190
|
writeToDisk: false,
|
|
192
191
|
|
|
@@ -211,7 +210,7 @@ const config = {
|
|
|
211
210
|
timeToConvert: 2,
|
|
212
211
|
weight: 4,
|
|
213
212
|
props: {
|
|
214
|
-
"dungeon_id":
|
|
213
|
+
"dungeon_id": dungeonIds,
|
|
215
214
|
"difficulty": ["Easy", "Medium", "Hard", "Deadly"],
|
|
216
215
|
}
|
|
217
216
|
},
|
|
@@ -221,7 +220,7 @@ const config = {
|
|
|
221
220
|
conversionRate: 55,
|
|
222
221
|
timeToConvert: 3,
|
|
223
222
|
weight: 3,
|
|
224
|
-
props: { "quest_id":
|
|
223
|
+
props: { "quest_id": questIds },
|
|
225
224
|
},
|
|
226
225
|
{
|
|
227
226
|
// Preparation before dungeon: inspect + search for strategic explorer hook
|
|
@@ -274,14 +273,14 @@ const config = {
|
|
|
274
273
|
weight: 2,
|
|
275
274
|
properties: {
|
|
276
275
|
"completion_time_mins": u.weighNumRange(3, 25, 0.8, 10),
|
|
277
|
-
"skipped":
|
|
276
|
+
"skipped": [false, false, false, false, false, false, true],
|
|
278
277
|
}
|
|
279
278
|
},
|
|
280
279
|
{
|
|
281
280
|
event: "quest accepted",
|
|
282
281
|
weight: 15,
|
|
283
282
|
properties: {
|
|
284
|
-
"quest_id":
|
|
283
|
+
"quest_id": questIds,
|
|
285
284
|
"quest_type": [
|
|
286
285
|
"Main Story",
|
|
287
286
|
"Side Quest",
|
|
@@ -296,7 +295,7 @@ const config = {
|
|
|
296
295
|
event: "quest objective completed",
|
|
297
296
|
weight: 12,
|
|
298
297
|
properties: {
|
|
299
|
-
"quest_id":
|
|
298
|
+
"quest_id": questIds,
|
|
300
299
|
"objective_number": u.weighNumRange(1, 5),
|
|
301
300
|
}
|
|
302
301
|
},
|
|
@@ -304,16 +303,18 @@ const config = {
|
|
|
304
303
|
event: "quest turned in",
|
|
305
304
|
weight: 10,
|
|
306
305
|
properties: {
|
|
307
|
-
"quest_id":
|
|
306
|
+
"quest_id": questIds,
|
|
308
307
|
"reward_gold": u.weighNumRange(10, 500, 0.5, 100),
|
|
309
308
|
"reward_xp": u.weighNumRange(50, 2000, 0.5, 500),
|
|
309
|
+
"compass_user": [false],
|
|
310
|
+
"subscriber_advantage": ["Free"],
|
|
310
311
|
}
|
|
311
312
|
},
|
|
312
313
|
{
|
|
313
314
|
event: "enter dungeon",
|
|
314
315
|
weight: 18,
|
|
315
316
|
properties: {
|
|
316
|
-
"dungeon_id":
|
|
317
|
+
"dungeon_id": dungeonIds,
|
|
317
318
|
"difficulty": ["Easy", "Medium", "Hard", "Deadly"],
|
|
318
319
|
"party_size": u.weighNumRange(1, 5),
|
|
319
320
|
}
|
|
@@ -322,9 +323,12 @@ const config = {
|
|
|
322
323
|
event: "exit dungeon",
|
|
323
324
|
weight: 14,
|
|
324
325
|
properties: {
|
|
325
|
-
"dungeon_id":
|
|
326
|
+
"dungeon_id": dungeonIds,
|
|
326
327
|
"time_spent_mins": u.weighNumRange(5, 120, 0.6, 30),
|
|
327
328
|
"completion_status": ["completed", "abandoned", "died"],
|
|
329
|
+
"strategic_explorer": [false],
|
|
330
|
+
"legendary_weapon_equipped": [false],
|
|
331
|
+
"subscriber_advantage": ["Free"],
|
|
328
332
|
}
|
|
329
333
|
},
|
|
330
334
|
{
|
|
@@ -340,6 +344,10 @@ const config = {
|
|
|
340
344
|
"Rare Artifact"
|
|
341
345
|
],
|
|
342
346
|
"treasure_value": u.weighNumRange(5, 1000, 1.2, 50),
|
|
347
|
+
"legendary_drop": [false],
|
|
348
|
+
"strategic_explorer": [false],
|
|
349
|
+
"subscriber_advantage": ["Free"],
|
|
350
|
+
"elite_bonus": [false],
|
|
343
351
|
}
|
|
344
352
|
},
|
|
345
353
|
{
|
|
@@ -354,7 +362,10 @@ const config = {
|
|
|
354
362
|
"Friendly Fire"
|
|
355
363
|
],
|
|
356
364
|
"player_level": u.weighNumRange(1, 50),
|
|
357
|
-
"resurrection_used":
|
|
365
|
+
"resurrection_used": [false, false, false, true],
|
|
366
|
+
"cursed_week": [false],
|
|
367
|
+
"near_death_survival": [false],
|
|
368
|
+
"subscriber_advantage": ["Free"],
|
|
358
369
|
}
|
|
359
370
|
},
|
|
360
371
|
{
|
|
@@ -369,7 +380,7 @@ const config = {
|
|
|
369
380
|
event: "item purchased",
|
|
370
381
|
weight: 11,
|
|
371
382
|
properties: {
|
|
372
|
-
"item_id":
|
|
383
|
+
"item_id": itemIds,
|
|
373
384
|
"item_type": [
|
|
374
385
|
"Weapon",
|
|
375
386
|
"Armor",
|
|
@@ -386,7 +397,7 @@ const config = {
|
|
|
386
397
|
event: "item sold",
|
|
387
398
|
weight: 7,
|
|
388
399
|
properties: {
|
|
389
|
-
"item_id":
|
|
400
|
+
"item_id": itemIds,
|
|
390
401
|
"item_type": [
|
|
391
402
|
"Weapon",
|
|
392
403
|
"Armor",
|
|
@@ -409,8 +420,9 @@ const config = {
|
|
|
409
420
|
"Cosmetic Bundle",
|
|
410
421
|
"Season Pass"
|
|
411
422
|
],
|
|
412
|
-
"price_usd":
|
|
423
|
+
"price_usd": [4.99, 9.99, 19.99, 49.99, 99.99],
|
|
413
424
|
"payment_method": ["Credit Card", "PayPal", "Apple Pay", "Google Pay"],
|
|
425
|
+
"lucky_charm_effect": [false],
|
|
414
426
|
}
|
|
415
427
|
},
|
|
416
428
|
{
|
|
@@ -457,14 +469,14 @@ const config = {
|
|
|
457
469
|
"Quest Location",
|
|
458
470
|
"Town Square"
|
|
459
471
|
],
|
|
460
|
-
"clue_found":
|
|
472
|
+
"clue_found": [false, false, true, true, true],
|
|
461
473
|
}
|
|
462
474
|
},
|
|
463
475
|
{
|
|
464
476
|
event: "use item",
|
|
465
477
|
weight: 14,
|
|
466
478
|
properties: {
|
|
467
|
-
"item_id":
|
|
479
|
+
"item_id": itemIds,
|
|
468
480
|
"item_type": [
|
|
469
481
|
"Health Potion",
|
|
470
482
|
"Mana Potion",
|
|
@@ -497,7 +509,11 @@ const config = {
|
|
|
497
509
|
weight: 18,
|
|
498
510
|
properties: {
|
|
499
511
|
"outcome": ["Victory", "Defeat", "Fled"],
|
|
500
|
-
"loot_gained":
|
|
512
|
+
"loot_gained": [false, false, false, true, true, true, true, true, true, true],
|
|
513
|
+
"legendary_weapon_equipped": [false],
|
|
514
|
+
"subscriber_advantage": ["Free"],
|
|
515
|
+
"near_death_survival": [false],
|
|
516
|
+
"guild_member_retained": [false],
|
|
501
517
|
}
|
|
502
518
|
}
|
|
503
519
|
],
|
|
@@ -516,10 +532,17 @@ const config = {
|
|
|
516
532
|
"High",
|
|
517
533
|
"Ultra"
|
|
518
534
|
],
|
|
519
|
-
subscription_tier:
|
|
535
|
+
subscription_tier: ["Free", "Free", "Free", "Premium", "Elite"],
|
|
520
536
|
},
|
|
521
537
|
|
|
522
|
-
scdProps: {
|
|
538
|
+
scdProps: {
|
|
539
|
+
player_rank: {
|
|
540
|
+
values: ["recruit", "veteran", "elite", "legend"],
|
|
541
|
+
frequency: "week",
|
|
542
|
+
timing: "fuzzy",
|
|
543
|
+
max: 20
|
|
544
|
+
}
|
|
545
|
+
},
|
|
523
546
|
|
|
524
547
|
userProps: {
|
|
525
548
|
"preferred_playstyle": [
|
|
@@ -585,18 +608,16 @@ const config = {
|
|
|
585
608
|
if (EVENT_TIME.isAfter(CURSED_WEEK_START) && EVENT_TIME.isBefore(CURSED_WEEK_END)) {
|
|
586
609
|
// 50% chance to inject a death event
|
|
587
610
|
if (chance.bool({ likelihood: 30 })) {
|
|
588
|
-
|
|
589
|
-
event: "player death",
|
|
590
|
-
time: record.time,
|
|
591
|
-
user_id: record.user_id,
|
|
592
|
-
cause_of_death: "Curse",
|
|
593
|
-
player_level: chance.integer({ min: 1, max: 50 }),
|
|
594
|
-
resurrection_used: chance.bool({ likelihood: 80 }), // More res usage during curse
|
|
595
|
-
cursed_week: true,
|
|
596
|
-
};
|
|
597
|
-
// Return death event instead sometimes
|
|
611
|
+
// Return death event instead sometimes (clone from current record for schema)
|
|
598
612
|
if (chance.bool({ likelihood: 50 })) {
|
|
599
|
-
return
|
|
613
|
+
return {
|
|
614
|
+
...record,
|
|
615
|
+
event: "player death",
|
|
616
|
+
cause_of_death: "Curse",
|
|
617
|
+
player_level: chance.integer({ min: 1, max: 50 }),
|
|
618
|
+
resurrection_used: chance.bool({ likelihood: 80 }), // More res usage during curse
|
|
619
|
+
cursed_week: true,
|
|
620
|
+
};
|
|
600
621
|
}
|
|
601
622
|
}
|
|
602
623
|
}
|
|
@@ -715,9 +736,8 @@ const config = {
|
|
|
715
736
|
// Add extra quest completions for compass users
|
|
716
737
|
if (chance.bool({ likelihood: 40 })) {
|
|
717
738
|
const extraQuest = {
|
|
718
|
-
event
|
|
739
|
+
...event,
|
|
719
740
|
time: eventTime.add(chance.integer({ min: 10, max: 120 }), 'minutes').toISOString(),
|
|
720
|
-
user_id: event.user_id,
|
|
721
741
|
quest_id: chance.pickone(questIds),
|
|
722
742
|
reward_gold: chance.integer({ min: 100, max: 500 }),
|
|
723
743
|
reward_xp: chance.integer({ min: 500, max: 2000 }),
|
|
@@ -742,20 +762,23 @@ const config = {
|
|
|
742
762
|
|
|
743
763
|
// Add additional purchase events (higher LTV)
|
|
744
764
|
if (event.event === "item purchased" && chance.bool({ likelihood: 35 })) {
|
|
745
|
-
const
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
765
|
+
const purchaseTemplate = userEvents.find(e => e.event === "real money purchase");
|
|
766
|
+
if (purchaseTemplate) {
|
|
767
|
+
const extraPurchase = {
|
|
768
|
+
...purchaseTemplate,
|
|
769
|
+
time: eventTime.add(chance.integer({ min: 1, max: 3 }), 'days').toISOString(),
|
|
770
|
+
user_id: event.user_id,
|
|
771
|
+
product: chance.pickone([
|
|
772
|
+
"Premium Currency (5000)",
|
|
773
|
+
"Legendary Weapon Chest",
|
|
774
|
+
"Season Pass"
|
|
775
|
+
]),
|
|
776
|
+
price_usd: chance.pickone([19.99, 49.99, 99.99]),
|
|
777
|
+
payment_method: chance.pickone(["Credit Card", "PayPal"]),
|
|
778
|
+
lucky_charm_effect: true,
|
|
779
|
+
};
|
|
780
|
+
userEvents.splice(idx + 1, 0, extraPurchase);
|
|
781
|
+
}
|
|
759
782
|
}
|
|
760
783
|
}
|
|
761
784
|
|
|
@@ -863,17 +886,20 @@ const config = {
|
|
|
863
886
|
// Elite users get bonus engagement events
|
|
864
887
|
if (isElite && Math.random() * 100 < 15) {
|
|
865
888
|
if (event.event === "quest turned in" || event.event === "exit dungeon") {
|
|
866
|
-
const
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
889
|
+
const treasureTemplate = userEvents.find(e => e.event === "find treasure");
|
|
890
|
+
if (treasureTemplate) {
|
|
891
|
+
const treasureTypes = ["Rare Artifact", "Gold", "Weapon", "Armor"];
|
|
892
|
+
const bonusEvent = {
|
|
893
|
+
...treasureTemplate,
|
|
894
|
+
time: eventTime.add(Math.floor(Math.random() * 26) + 5, 'minutes').toISOString(),
|
|
895
|
+
user_id: event.user_id,
|
|
896
|
+
treasure_type: treasureTypes[Math.floor(Math.random() * treasureTypes.length)],
|
|
897
|
+
treasure_value: Math.floor(Math.random() * 601) + 200,
|
|
898
|
+
subscriber_advantage: "Elite",
|
|
899
|
+
elite_bonus: true,
|
|
900
|
+
};
|
|
901
|
+
userEvents.splice(idx + 1, 0, bonusEvent);
|
|
902
|
+
}
|
|
877
903
|
}
|
|
878
904
|
}
|
|
879
905
|
}
|
|
@@ -898,9 +924,10 @@ const config = {
|
|
|
898
924
|
} else if (joinedGuildEarly) {
|
|
899
925
|
// Add extra engagement events for retained users
|
|
900
926
|
const lastEvent = userEvents[userEvents.length - 1];
|
|
901
|
-
|
|
927
|
+
const combatTemplate = userEvents.find(e => e.event === "combat completed");
|
|
928
|
+
if (lastEvent && combatTemplate && chance.bool({ likelihood: 60 })) {
|
|
902
929
|
const retentionEvent = {
|
|
903
|
-
|
|
930
|
+
...combatTemplate,
|
|
904
931
|
time: dayjs(lastEvent.time).add(chance.integer({ min: 1, max: 5 }), 'days').toISOString(),
|
|
905
932
|
user_id: lastEvent.user_id,
|
|
906
933
|
outcome: "Victory",
|