-2

I'm trying to get an efficient way of getting an item from a loot table (which can have hundreds of items), because it has to run on my node server and most online users will run it once every minute. So having an inefficient one could mean a lot of server costs. And I'm, of course, trying to avoid wasting resources.

One way of doing it would be by filling an array by, if the chance is 10, then pushing 10 of that item.id into it and just pick one item from array with Math.random().

const lootTable = [{
    id: 'stone_sword',
    chance: 500 // in 1000, which is then 50%
  },
  {
    id: 'gold_sword',
    chance: 100 // in 1000, which is then 10%
  },
  {
    id: 'iron_sword',
    chance: 100 // in 1000, which is then 10%
  },
  {
    id: 'diamond_sword',
    chance: 10 // in 1000, which is then 1%
  }
]

let filledLootBox = []
lootTable.forEach(item => {
  // inserts item.chance amount of times item.id into lootItem
  const lootItem = new Array(item.chance).fill(item.id)

  filledLootBox.push(...lootItem)
})

const pickedItem = filledLootBox[Math.floor(Math.random() * filledLootBox.length)]

console.log('picked item:', pickedItem)
console.log(filledLootBox)

But, I just don't know how efficient this is in a server, where there's thousands of these requests a minute, because .fill seems to be sluggish/slow.

Holiday
  • 871
  • 1
  • 8
  • 14

1 Answers1

3

Even if lootTable has hundreds of entries, looping through it for each request isn't at all a big deal. Hundreds of thousands of them might be.

Calculate the random number (let's call it roll) on a field of 0 <= roll < 1000, then pick the item that covers that part of the field. For instance:

const roll = Math.floor(Math.random() * 1000);
let picked = null;
for (let i = 0, len = lootTable.length; i < len; ++i) {
    const loot = lootTable[i];
    const {chance} = loot;
    if (roll < chance) {
        picked = loot;
        break;
    }
    roll -= chance;
}
// Here, `picked' will definitely be non-`null` if the table's values
// exhaust the field (the ones in your question don't, but I figured it's
// an excerpt).

That's not asking much at all relative to other costs of server requests.

Note: You could express the chance in percentage terms (.5, .1, .1, .01 for the list in your question) then just use Math.random() rather than Math.floor(Math.random() * 1000).

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875