Skip to content

Instantly share code, notes, and snippets.

@jessejputnam
Last active January 8, 2023 12:08
Show Gist options
  • Save jessejputnam/2e81fbd676085618c28cd07458ad5d19 to your computer and use it in GitHub Desktop.
Save jessejputnam/2e81fbd676085618c28cd07458ad5d19 to your computer and use it in GitHub Desktop.
Snippet that simulates DnD 5e tavern marketing budget scenarios to determine optimal marketing budget from players
/**
* DnD 5e Waterdeep campaign includes a Tavern controlled by players, that for
* every 10-day cycle, must have upkeep given but also can earn income from
* the business. That net income is determined by dice roll. However, players
* may opt to add a --marketing budget-- which adds on to the dice roll...
* but at what (literal) cost?
*
* To answer the question of which amount of gold would grant best return on
* investment, I wrote this to run scenarios to determine, over many iterations,
* what the best choice of marketing budget would be.
*/
"use strict";
// Helper Functions
/**
* Gets dice roll value based on variably sided die
* @param {number} sides - Number of sides on the die
* @returns {number} - Roll value
*/
function roll(sides) {
return Math.floor(Math.random() * sides) + 1;
}
/**
* Determines gross income based on dice roll
* @param {number} value -
* @returns {number}
*/
function getGrossIncome(value) {
const scenario = Math.trunc((value - 1) * 0.1);
if (scenario < 2) return -90;
else if (scenario === 2) return -60;
else if (scenario === 3) return -30;
else if (scenario === 4 || scenario === 5) return 0;
else if (scenario === 6 || scenario === 7) return 5 * roll(6);
else if (scenario === 8) return 5 * (roll(8) + roll(8));
else return 5 * (roll(10) + roll(10) + roll(10));
}
/**
* Calculates net income for the week based on marketing budget
* @param {number} budget - Total gold given by players as marketing budget
* @returns {number} - Net income from 10-day cycle
*/
function getNetIncome(budget) {
const value = roll(100) + 10 + budget;
return getGrossIncome(value) - budget;
}
// MAIN FUNCTION
/**
* Calculates the optimal marketing budget by running sample_size
* trials. Marketing budgets checked from 0gp to 90gp as beyond
* guarantees money lost.
* @returns {{max_roi: number, budget: number, all_roi: number[]}}
* max_roi: optimal return of interest
* budget: optimal budget
* all_roi: avg roi per budget
*/
function findOptimalMarketingBudget() {
const sample_size = 1_000_000;
// Indices correspond to marketing budget (arr[0] = 0gp; arr[90] = 90gp)
const trial_sums = new Array(91).fill(0);
// Run experiment sample_size times
for (let i = 0; i < sample_size; i++) {
// For each trial, add a cycle to arr[index] where the index is the marketing budget
trial_sums.forEach(
(_, budget) => (trial_sums[budget] += getNetIncome(budget))
);
}
// Initialize variables for storing return of investment max and its corresponding budget
let max_roi = -Infinity;
let max_roi_budget;
// Get mean averages and find max_roi and budget
const all_roi = trial_sums.map((trial_sum, budget) => {
const avg_roi = Number((trial_sum / sample_size).toFixed(2));
if (avg_roi > max_roi) {
max_roi = avg_roi;
max_roi_budget = budget;
}
return avg_roi;
});
return {
max_roi,
budget: max_roi_budget,
all_roi
};
}
console.log(findOptimalMarketingBudget());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment