Desktop Dungeons Shops

Calculate probabilities for item spawns in desktop dungeons shops.

HTML

<label for="shops">Shops:</label>
<input id="shops" type="text" value="8">
<label for="shops">Exponent:</label>
<input id="exponent" type="text" value="2">
<br>
<label for="order">Order:</label>
<select id="order" value="bqe">
  <option value="bqe">Basic Quest Elite</option>
  <option value="qbe">Quest Basic Elite</option>
  <option value="ebq">Elite Basic Quest</option>
  <option value="qeb">Quest Elite Basic</option>
  <option value="eqb">Elite Quest Basic</option>
</select>
<br>
Vanilla:
Basic<input id="vanillaBasic" type="checkbox" checked="1"/>
Quest<input id="vanillaQuest" type="checkbox" checked="1"/>
Elite<input id="vanillaElite" type="checkbox" checked="1"/>
<br>
EE:
Basic<input id="eeBasic" type="checkbox" checked="0"/>
Quest<input id="eeQuest" type="checkbox" checked="0"/>
Elite<input id="eeElite" type="checkbox" checked="0"/>
<br>
AQE:
Basic<input id="aqeBasic" type="checkbox" checked="0"/>
Quest<input id="aqeQuest" type="checkbox" checked="0"/>
Elite<input id="aqeElite" type="checkbox" checked="0"/>
<br>
<button id="update">Update</button>
<div id="odds-of-basic">Odds your first shop is a Basic:</div>
<div id="odds-of-quest">Odds your first shop is a Quest:</div>
<div id="odds-of-elite">Odds your first shop is an Elite:</div>
<table id="itemlist"></table>

JavaScript

function probabilityShopIsItem(index, numItemsInPool) {
	const flippedIndex = numItemsInPool - index - 1;
	const itemChosenRange = Math.pow(flippedIndex + 1, power) - Math.pow(flippedIndex, power);
  const totalRange = Math.pow(numItemsInPool, power);
  const probabilityChosen = itemChosenRange / totalRange;
	//console.log(index, flippedIndex, numItemsInPool, probabilityChosen);
  return probabilityChosen;
}

function probabilityShopIsNotItem(index, numItemsInPool) {
	return 1 - probabilityShopIsItem(index, numItemsInPool);
}

function probabilityOneOfShopsIsItem(startIndex, endIndex, numItemsInPool) {
	let totalProbabilityChosen = 0;
	for (let i = startIndex; i < endIndex; i++) {
  	totalProbabilityChosen += probabilityShopIsItem(i, numItemsInPool);
  }
  return totalProbabilityChosen;
}

function p_multi(i, n, s) {
  let totalProbabilityChosen = probabilityShopIsItem(i, n);
  if (s > 1) {
    if (i > 0) {
    	const probabilityItemBeforeThisWasChosen = probabilityOneOfShopsIsItem(0, i, n);
      totalProbabilityChosen += probabilityItemBeforeThisWasChosen * p_multi(i - 1, n - 1, s - 1);
    }
    if (i < n - 1) {
    	const probabilityItemBeforeThisWasChosen = probabilityOneOfShopsIsItem(i + 1, n, n);
      totalProbabilityChosen += probabilityItemBeforeThisWasChosen * p_multi(i, n - 1, s - 1)
    }
  }
  return totalProbabilityChosen;
}

function refreshDisplayedItems() {
	items = [];
  itemlist = [];
  let numBasics = 0;
  let numQuests = 0;
  let numElites = 0;
  if (document.getElementById('vanillaBasic').checked) {
  	numBasics += vanillaBasics.length;
		items = items.concat(vanillaBasics);
  }
  if (document.getElementById('eeBasic').checked) {
  	numBasics += eeBasics.length;
		items = items.concat(eeBasics);
  }
  if (document.getElementById('aqeBasic').checked) {
  	numBasics += aqeBasics.length;
		items = items.concat(aqeBasics);
  }
  if (document.getElementById('vanillaQuest').checked) {
  	numQuests += vanillaQuests.length;
		items =...