@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.
Files changed (104) hide show
  1. package/README.md +7 -7
  2. package/dungeons/technical/ad-spend-schema.json +128 -0
  3. package/dungeons/technical/ad-spend.js +116 -0
  4. package/dungeons/technical/anonymous-users-schema.json +92 -0
  5. package/dungeons/technical/anonymous-users.js +102 -0
  6. package/dungeons/technical/array-of-object-lookup-schema.json +191 -0
  7. package/dungeons/{array-of-object-lookup.js → technical/array-of-object-lookup.js} +12 -5
  8. package/dungeons/technical/experiments-schema.json +203 -0
  9. package/dungeons/technical/experiments.js +168 -0
  10. package/dungeons/technical/foobar-schema.json +362 -0
  11. package/dungeons/{foobar.js → technical/foobar.js} +3 -3
  12. package/dungeons/technical/group-analytics-schema.json +241 -0
  13. package/dungeons/technical/group-analytics.js +151 -0
  14. package/dungeons/technical/mirror-strategies-schema.json +84 -0
  15. package/dungeons/technical/mirror-strategies.js +126 -0
  16. package/dungeons/technical/nested-objects-schema.json +145 -0
  17. package/dungeons/technical/nested-objects.js +207 -0
  18. package/dungeons/technical/retention-cadence-schema.json +37 -0
  19. package/dungeons/{retention-cadence.js → technical/retention-cadence.js} +2 -3
  20. package/dungeons/technical/sanity-schema.json +185 -0
  21. package/dungeons/{sanity.js → technical/sanity.js} +4 -2
  22. package/dungeons/technical/scale-test-schema.json +70 -0
  23. package/dungeons/technical/scale-test.js +90 -0
  24. package/dungeons/technical/scd-schema.json +467 -0
  25. package/dungeons/{scd.js → technical/scd.js} +20 -10
  26. package/dungeons/technical/simple-schema.json +362 -0
  27. package/dungeons/{simple.js → technical/simple.js} +7 -7
  28. package/dungeons/technical/simplest-schema.json +724 -0
  29. package/dungeons/{simplest.js → technical/simplest.js} +7 -7
  30. package/dungeons/technical/text-generation-schema.json +1062 -0
  31. package/dungeons/{text-generation.js → technical/text-generation.js} +24 -11
  32. package/dungeons/vertical/community-schema.json +579 -0
  33. package/dungeons/vertical/community.js +794 -0
  34. package/dungeons/vertical/devtools-schema.json +601 -0
  35. package/dungeons/vertical/devtools.js +843 -0
  36. package/dungeons/vertical/ecommerce-schema.json +604 -0
  37. package/dungeons/{ecommerce.js → vertical/ecommerce.js} +43 -5
  38. package/dungeons/vertical/education-schema.json +5686 -0
  39. package/dungeons/{education.js → vertical/education.js} +70 -39
  40. package/dungeons/vertical/fintech-schema.json +630 -0
  41. package/dungeons/{fintech.js → vertical/fintech.js} +107 -56
  42. package/dungeons/vertical/fitness-schema.json +530 -0
  43. package/dungeons/vertical/fitness.js +808 -0
  44. package/dungeons/vertical/food-delivery-schema.json +36728 -0
  45. package/dungeons/{food.js → vertical/food-delivery.js} +67 -26
  46. package/dungeons/vertical/gaming-schema.json +438 -0
  47. package/dungeons/{gaming.js → vertical/gaming.js} +29 -16
  48. package/dungeons/vertical/healthcare-schema.json +549 -0
  49. package/dungeons/vertical/healthcare.js +775 -0
  50. package/dungeons/vertical/insurance-application-schema.json +485 -0
  51. package/dungeons/{insurance-application.js → vertical/insurance-application.js} +41 -31
  52. package/dungeons/vertical/logistics-schema.json +574 -0
  53. package/dungeons/vertical/logistics.js +799 -0
  54. package/dungeons/vertical/marketplace-schema.json +533 -0
  55. package/dungeons/vertical/marketplace.js +850 -0
  56. package/dungeons/vertical/media-schema.json +4749 -0
  57. package/dungeons/{media.js → vertical/media.js} +103 -19
  58. package/dungeons/vertical/rpg-schema.json +2491 -0
  59. package/dungeons/{rpg.js → vertical/rpg.js} +87 -60
  60. package/dungeons/vertical/sass-schema.json +3128 -0
  61. package/dungeons/{sass.js → vertical/sass.js} +65 -26
  62. package/dungeons/vertical/social-schema.json +620 -0
  63. package/dungeons/{social.js → vertical/social.js} +76 -17
  64. package/dungeons/vertical/travel-schema.json +580 -0
  65. package/dungeons/vertical/travel.js +742 -0
  66. package/index.js +20 -16
  67. package/lib/core/config-validator.js +286 -1
  68. package/lib/core/context.js +16 -5
  69. package/lib/core/storage.js +2 -0
  70. package/lib/generators/adspend.js +3 -3
  71. package/lib/generators/events.js +141 -14
  72. package/lib/generators/funnels.js +67 -7
  73. package/lib/generators/text.js +2 -2
  74. package/lib/orchestrators/user-loop.js +444 -22
  75. package/lib/templates/soup-presets.js +2 -2
  76. package/lib/utils/utils.js +4 -5
  77. package/package.json +4 -3
  78. package/scripts/dungeon-to-json.mjs +163 -0
  79. package/scripts/extract-dungeon-schema.mjs +343 -0
  80. package/scripts/json-to-dungeon.mjs +200 -0
  81. package/scripts/run-dungeon.mjs +76 -0
  82. package/scripts/run-many.mjs +118 -0
  83. package/scripts/verify-runner.mjs +42 -0
  84. package/types.d.ts +316 -1
  85. package/dungeons/array-of-object-lookup-schema.json +0 -327
  86. package/dungeons/ecommerce-schema.json +0 -462
  87. package/dungeons/education-schema.json +0 -2409
  88. package/dungeons/fintech-schema.json +0 -14034
  89. package/dungeons/foobar-schema.json +0 -403
  90. package/dungeons/food-delivery-schema.json +0 -192
  91. package/dungeons/food-delivery.js +0 -602
  92. package/dungeons/food-schema.json +0 -1152
  93. package/dungeons/gaming-schema.json +0 -1270
  94. package/dungeons/insurance-application-schema.json +0 -204
  95. package/dungeons/media-schema.json +0 -906
  96. package/dungeons/retention-cadence-schema.json +0 -78
  97. package/dungeons/rpg-schema.json +0 -4526
  98. package/dungeons/sanity-schema.json +0 -255
  99. package/dungeons/sass-schema.json +0 -1291
  100. package/dungeons/scd-schema.json +0 -919
  101. package/dungeons/simple-schema.json +0 -608
  102. package/dungeons/simplest-schema.json +0 -1418
  103. package/dungeons/social-schema.json +0 -1118
  104. 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 "../lib/utils/utils.js";
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("../types.d.ts").Dungeon} Config */
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": u.pickAWinner(dungeonIds),
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": u.pickAWinner(questIds) },
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": u.pickAWinner([true, false], 0.15),
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": u.pickAWinner(questIds),
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": u.pickAWinner(questIds),
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": u.pickAWinner(questIds),
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": u.pickAWinner(dungeonIds),
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": u.pickAWinner(dungeonIds),
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": u.pickAWinner([true, false], 0.25),
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": u.pickAWinner(itemIds),
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": u.pickAWinner(itemIds),
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": u.pickAWinner([4.99, 9.99, 19.99, 49.99, 99.99]),
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": u.pickAWinner([true, false], 0.6),
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": u.pickAWinner(itemIds),
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": u.pickAWinner([true, false], 0.7),
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: u.pickAWinner(["Free", "Free", "Free", "Premium", "Elite"]),
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
- const deathEvent = {
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 deathEvent;
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: "quest turned in",
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 extraPurchase = {
746
- event: "real money purchase",
747
- time: eventTime.add(chance.integer({ min: 1, max: 3 }), 'days').toISOString(),
748
- user_id: event.user_id,
749
- product: chance.pickone([
750
- "Premium Currency (5000)",
751
- "Legendary Weapon Chest",
752
- "Season Pass"
753
- ]),
754
- price_usd: chance.pickone([19.99, 49.99, 99.99]),
755
- payment_method: chance.pickone(["Credit Card", "PayPal"]),
756
- lucky_charm_effect: true,
757
- };
758
- userEvents.splice(idx + 1, 0, extraPurchase);
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 treasureTypes = ["Rare Artifact", "Gold", "Weapon", "Armor"];
867
- const bonusEvent = {
868
- event: "find treasure",
869
- time: eventTime.add(Math.floor(Math.random() * 26) + 5, 'minutes').toISOString(),
870
- user_id: event.user_id,
871
- treasure_type: treasureTypes[Math.floor(Math.random() * treasureTypes.length)],
872
- treasure_value: Math.floor(Math.random() * 601) + 200,
873
- subscriber_advantage: "Elite",
874
- elite_bonus: true,
875
- };
876
- userEvents.splice(idx + 1, 0, bonusEvent);
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
- if (lastEvent && chance.bool({ likelihood: 60 })) {
927
+ const combatTemplate = userEvents.find(e => e.event === "combat completed");
928
+ if (lastEvent && combatTemplate && chance.bool({ likelihood: 60 })) {
902
929
  const retentionEvent = {
903
- event: "combat completed",
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",