pick item from list at random, each has different

2020-03-07 04:28发布

I wanted to write a simple function that allows to roll random item from list, i did it with this code:

this.resources = [false, 'nitrogen', 'silicon', 'cobalt', 'magnesium'];

this.assign_resource = function() {
    var index = tools.rnd(0, this.resources.length - 1);
    return this.resources[index];
};

But it doesn't play well, so i wanted to change it to different system that allows a list of items (including empty one) and it picks one at random but each one has different chance (for example this one has 10%, this one has 20%). Maybe someone could help me with this kind of function.

Edited -----

for example this could be new list:

this.resources = [
    { type: 'empty', chance: 30 },
    { type: 'nitrogen', chance: 10 },
    { type: 'silicon', chance: 20 },
    { type: 'cobalt', chance: 30 },
    { type: 'magnesium', chance: 10 }
];

How to use it now to make it happen properly?

Edited 2 -----

I am trying to figure out well done programming solution using math rather then simply duplicating items in array, answers presented in this topic are just work arounds to a problem.

5条回答
Luminary・发光体
2楼-- · 2020-03-07 04:48

This is how I'd implement the solution. Step 1: accumulate all the possible chances Step 2: pick a random value in proportion to total chance Step 3: loop through the resources to see in which part it random value falls under.

var resources = [
    { type: 'empty', chance: 30 },
    { type: 'nitrogen', chance: 10 },
    { type: 'silicon', chance: 20 },
    { type: 'cobalt', chance: 30 },
    { type: 'magnesium', chance: 10 }
];

function solution(resources) {
  let chanceTotal = resources.reduce((acc, val) => { acc += val.chance ; return acc;}, 0);
  let randomVal = parseInt(Math.random() * chanceTotal);
  let chanceAcc = 0;
  let ans;
  resources.forEach(r => {
    chanceAcc += r.chance;
    if (chanceAcc > randomVal && !ans) {
      ans = r;
    }
  });
  return ans;
}

console.log(solution(resources));

查看更多
冷血范
3楼-- · 2020-03-07 04:49

I'd solve it by having an array of objects with a chance to be the result, totalling 1.0, then picking a random number between 0 and 1, and then iterating over the resources and check if adding it to a cumulative total includes your random number.

var resources = [
  { resource: false, chance: 0.2 },
  { resource: 'nitrogen', chance: 0.1 },
  { resource: 'silicon', chance: 0.2 },
  { resource: 'cobalt', chance: 0.45 },
  { resource: 'mangesium', chance: 0.05 }    
];

function get_result(resouceList) {
  //get our random from 0 to 1
  var rnd = Math.random();
  
  //initialise our cumulative percentage
  var cumulativeChance = 0;
  //iterate over our resources
  for (var i = 0; i < resouceList.length; i++) {
    
    //include current resource
    cumulativeChance += resouceList[i].chance;
    
    if (rnd < cumulativeChance)
      return resouceList[i].resource;
  }
  
  return false;  
}

//test
console.log(get_result(resources));
console.log(get_result(resources));
console.log(get_result(resources));
console.log(get_result(resources));
console.log(get_result(resources));

查看更多
时光不老,我们不散
4楼-- · 2020-03-07 04:51

You can do something like this.

Creating an array with the same value multiple times gives it a higher chance of being selected.

var resources = [{
    type: 'empty',
    chance: 30
  },
  {
    type: 'nitrogen',
    chance: 10
  },
  {
    type: 'silicon',
    chance: 20
  },
  {
    type: 'cobalt',
    chance: 30
  },
  {
    type: 'magnesium',
    chance: 10
  }
];


function GetRandom(list) {
  var array = [];
  for (var i = 0; i < list.length; i++) {
    var item = list[i];
    var chance = item.chance / 10;
    for (var j = 0; j < chance; j++) {
      array.push(item.type);
    }
  }
  var idx = Math.floor(Math.random() * array.length);
  return array[idx];

}

console.log(GetRandom(resources))
.as-console-wrapper { max-height: 100% !important; top: 0; }

查看更多
爱情/是我丢掉的垃圾
5楼-- · 2020-03-07 04:51

Here is another implementation.

var res = [
    ["empty", 3],
    ["nitrogen", 1],
    ["silicon", 2],
    ["cobalt", 3],
    ["magnesium", 1]
];
var flat = [];
var item;
for(var i = 0, l = res.length; i < l; i++) {
    item = Array(res[i][1]+1).join(i+",");
    item = item.substr(0, item.length-1);
    flat.push(item);
}
flat = flat.join(",").split(",");

function get_random_item() {
    var ridx = Math.floor(Math.random() * (flat.length));
    return res[flat[ridx]][0];
}

var pick;
for(var p = 0; p < 50; p++) {
    pick = get_random_item();
    console.log(p, pick);
}

查看更多
别忘想泡老子
6楼-- · 2020-03-07 05:06

I would set it up so that only the actual resources are in your array and "empty" happens if the random roll falls outside of those.

this.resources = [
    { type: 'nitrogen', chance: 10 },
    { type: 'silicon', chance: 20 },
    { type: 'cobalt', chance: 30 },
    { type: 'magnesium', chance: 10 }
];

this.assign_resource = function() {
  var rnd = Math.random();
  var acc = 0;
  for (var i=0, r; r = this.resources[i]; i++) {
    acc += r.chance / 100;
    if (rnd < acc) return r.type;
  }
  // rnd wasn't less than acc, so no resource was found
  return 'empty';
}
查看更多
登录 后发表回答