4

As a fun project, I'm developing a cricket simulator game and one of the main aspects of it is to have a random outcome on each delivery bowled.

In general, in Test Cricket, the possibility is as follows:

"0", "1" happen very frequently (60% of the time)

"2", "3" happen moderately (25% of the the time)

"FOUR", "SIX", "OUT" happen rarely (10% of the time)

"WIDE BALL", "NO BALL" happen very rarely (2% of the time)

If I have an array such as:

var possible_outcomes = ["0","1","2","3","FOUR","SIX","OUT","WIDE BALL","NO BALL"];

What could be the best way to get the above mentioned probability when pulling a random item from possible_outcomes over a fixed number of iterations, say 60.

PS: I'm sorry if some of you are unaware of the sport. I've used a couple of terms related to Cricket since I didn't know how to explain any better.

Community
  • 1
  • 1
asprin
  • 9,579
  • 12
  • 66
  • 119
  • To actually answer your question, what you're looking for is a weighted random number. See here: http://stackoverflow.com/questions/8435183/generate-a-weighted-random-number – Rory McCrossan Dec 13 '16 at 11:07
  • @RoryMcCrossan Exceptions :) In sporting terms - Overthrows, 5 runs, Penalties etc – asprin Dec 13 '16 at 11:07
  • You could make an array with 100 entries. Make 30 of them 0, 30 of 1, 25 of 3 etc.. ? – Steyn Dec 13 '16 at 11:10

6 Answers6

1

You can generate a random number between 0-100. If 0 < number < 10 then 0 run, if 20-25 then 2 run. Similarly if we get 100 then wicket.

Amit
  • 3,662
  • 2
  • 26
  • 34
  • What if I get 100 3-times in a row?. It can happen. That wouldn't be very real, right? – asprin Dec 13 '16 at 11:08
  • 1
    That's the nature of randomness though – Rory McCrossan Dec 13 '16 at 11:09
  • Thats how probability works. Chances of two 100's is very less than chanhed of getting a number between 1-20 twice. And aditionally 3 wickets do happen in cricket(Hatrick) :) – Amit Dec 13 '16 at 11:10
  • Thanks. I've got some very good solutions. It's gonna be difficult to pick just 1 answer as a solution. I've got some tough decision to make here :) – asprin Dec 13 '16 at 11:34
1

IMO the easiest, however not so fancy solution is to create an array of hundred elements with repeated items in it:

var possible_outcomes = ["0","0","0","0","0","0","0","0","0","0",
"0","0","0","0","0","0","0","0","0","0",
"0","0","0","0","0","0","0","0","0","0",
"1","1","1","1","1","1","1","1","1","1",
"1","1","1","1","1","1","1","1","1","1",
"1","1","1","1","1","1","1","1","1","1",
,and so on...,
,"WIDE BALL","WIDE BALL","NO BALL","NO BALL"];

Then you just get the random item from that array. No if's or switch-cases needed.

PS. I don't know the cricket, however if repeating 100 times "0" is not allowed, you can always remove selected option from the array, so it won't happen again.

piotrwest
  • 2,098
  • 23
  • 35
  • Thanks. I've got some very good solutions. It's gonna be difficult to pick just 1 answer as a solution. I've got some tough decision to make here :) – asprin Dec 13 '16 at 11:34
  • No worries, the most important thing is that you find the answer which suits your needs. – piotrwest Dec 13 '16 at 11:36
1

I suggest to use a continuous check of the probability and the rest of the random number.

This function sets first the return value to the last possible index and iterates until the rest of the random value is smaller than the actual probability.

The probabilities have to sum to one.

function getRandomIndexByProbability(probabilities) {
    var r = Math.random(),
        index = probabilities.length - 1;

    probabilities.some(function (probability, i) {
        if (r < probability) {
            index = i;
            return true;
        }
        r -= probability;
    });
    return index;
}

var i,
    action = ["0", "1", "2", "3", "FOUR", "SIX", "OUT", "WIDE BALL", "NO BALL", "other"],
    probabilities = [0.3, 0.3, 1 / 8, 1 / 8, 1 / 30, 1 / 30, 1 / 30, 0.01, 0.01, 0.03],
    count = {},
    index;

action.forEach(function (a) { count[a] = 0; });

for (i = 0; i < 1e6; i++) {
    index = getRandomIndexByProbability(probabilities);
    count[action[index]]++;
}

console.log(count);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Thanks. I've got some very good solutions. It's gonna be difficult to pick just 1 answer as a solution. I've got some tough decision to make here :) – asprin Dec 13 '16 at 11:34
1

The technique you want to utilise is called 'weighted randomness'. To achieve this in Javascript you can build an array which is populated with the possible outcomes in amounts which match the given probability of their occurrence. For details on how to do this and why it works, see this question.

In the logic of your cricket game you can then use a loop to pull out random outcomes until the batter is out, something like this:

function weightedRand(spec) {
  var i, j, table = [];
  for (i in spec) {
    for (j = 0; j < spec[i] * 10; j++) {
      table.push(i);
    }
  }
  return function() {
    return table[Math.floor(Math.random() * table.length)];
  }
}

var outcomes = weightedRand({
  '0': 0.3,
  '1': 0.3,
  '2': 0.125,
  '3': 0.125,
  'FOUR': 0.033,
  'SIX': 0.033,
  'OUT': 0.033,
  'WIDE BALL': 0.01,
  'NO BALL': 0.01
});

$('button').click(function() {
  clearInterval(interval);
  $('.innings').empty();
  $(this).prop('disabled', true);

  var interval = setInterval(function() {
    var item = outcomes();
    $('.innings').append('<div>' + item + '<div>');

    if (item == 'OUT') {
      clearInterval(interval);
      $('button').prop('disabled', false);
    }
  }, 1000);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Start innings</button>
<div class="innings"></div>

Note that the probabilities won't quite match what you've specified in the above code as they don't cumulatively add up to 1.00, but the values can easily be amended.

Community
  • 1
  • 1
Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
  • Thanks. I've got some very good solutions. It's gonna be difficult to pick just 1 answer as a solution. I've got some tough decision to make here :) – asprin Dec 13 '16 at 11:34
1

An easiely changeable solution would be the following:

var cricket = [
    {
        name: 0,
        weight: 30
    },
    {
        name: 1,
        weight: 30
    },
    {
        name: 2,
        weight: 13
    }
    // ...
];

// Create the chances array by putting as many instances of a value in it, as it's weight

var chances = [];
for (var i = 0; i < cricket.length; i++) {
    for (var j = 0; j < cricket[i].weight; j++) {
        chances.push(cricket[i].name);
    }
}

// getting a value

var value = chances[Math.floor(Math.random() * chances.length)];
Bálint
  • 4,009
  • 2
  • 16
  • 27
  • Thanks. I've got some very good solutions. It's gonna be difficult to pick just 1 answer as a solution. I've got some tough decision to make here :) – asprin Dec 13 '16 at 11:34
1

Forgive me for my awkward code but I hope you can consider this as a solution? :D

function repeatArray(value, len) {
  if (len == 0) return [];
  var a = [value];
  while (a.length * 2 <= len) a = a.concat(a);
  if (a.length < len) a = a.concat(a.slice(0, len - a.length));
  return a;
}

var zeroes = repeatArray("0", 30);
var ones = repeatArray("1", 25);
var twos = repeatArray("2", 15);
var threes = repeatArray("3", 10);
var fours = repeatArray("FOUR", 5);
var sixes = repeatArray("SIX", 5);
var wickets = repeatArray("OUT", 5);
var extras = repeatArray("Extra", 5);
var finalArr = [];

finalArr = finalArr.concat(zeroes, ones, twos, threes, fours, sixes, wickets, extras);
for (var i = 0; i < 20; i++) {
  var idx = Math.floor(Math.random() * finalArr.length);
  $("#out").append(finalArr[idx]+", ");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="out"></div>
philantrovert
  • 9,904
  • 3
  • 37
  • 61
  • Thanks. I've got some very good solutions. It's gonna be difficult to pick just 1 answer as a solution. I've got some tough decision to make here :) – asprin Dec 13 '16 at 11:34