Rollin’ the dice

Today's mission was randomizing dice rolls, for a given number of players at a table. The concept obviously comes in handy in a number of board games, or at a gambling table, and several simple dice-based games as well.

For the purposes of this demo, I decided on using a six-sided die, and we'll have any number of players, say 2-10. When designing this, I need methods to handle the actual action of rolling the dice, and then another method to handle the randomization, and any algorithm-related activity of the dice roll.

Let's start with the first one, we'll call it rollDice(). To keep it simple, I'm going to just pass it a number of dice to roll. We'll store the results in an array, and return the array of values.

var rollDice = function(numDice) {
    var roll=[];
    //do something numDice times ...
    console.log('roll', roll);
    return roll;
  };

Great, we're off to a rolling start. That's returning an empty array, which is what we want. Once we push values in there, it will be populated.

Next up is our second function, the randomizer, and I'm going to call that randomizeDiceRoll(). So for that one, to keep it simple, I'll define the dice configuration within the method, but this would be a perfect situation for an external config setting of some kind - a global object, a call() implementation, or a Die class which holds config for multiple types and shapes of dies. This one will simply randomize the outcome based on the number of sides in the die.

var randomizeDiceRoll = function() {
    var diceSides = [1,2,3,4,5,6];
      return Math.floor((Math.random() * diceSides[diceSides.length-1]) + 1);
 };

This we can test a few times in console and see it execute, and it appears to be handling the outcome fairly randomly, so we're good here. Let's move on to the last step, which is plugging this into our previous method.

var rollDice = function(numDice) {
    var roll=[];
    for(var i=0;i<numDice;i++) {
      roll.push( randomizeDiceRoll() );
    }

    console.log('roll', roll);
    return roll;
  };

Testing that in console, I can see I'm getting 3 rolls back for 3 players, 2 for 2 players, etc. So this is working as well.

rollDice(3)
•>  roll [3, 5, 2]
[3, 5, 2]
rollDice(2)
•>  roll [1, 2]
[1, 2]
rollDice(8)
•>  roll [1,2,2,5,4,6,1,1]
[1, 2, 2, 5, 4, 6, 1, 1]

So we hit a snag. What happens when we have a tie? Well, we can just force everyone to re-roll, adding a new boolean variable - tie, along with some recursion to our rollDice() method. Something like this:

var tie = false;
 
roll.forEach(function(n, idx) {
  if(roll[idx] == roll[idx+1]) {
    tie = true;
  }
});
if(tie == true) {
  rollDice(numDice);
}

So this works - I rolled for 3, got a tie, and it re-rolled, eliminating the tie:

rollDice(3);
roll [3, 3, 5]
roll [2, 3, 1]

Tie problem solved but now we have an additional problem. If you look at those results, the last item in the first array originally had the highest score, placing them in first position to start a game, or possibly win a battle. After re-rolling though, they are actually in last, unless we're using the numbers in reverse order, either way, their fortune has been flipped on its head. So let's fix that

var rollDice = function(numDice) {
    var roll=[],
        diceSides = [1,2,3,4,5,6];
    for(var i=0;i<numDice;i++) {
      roll.push( randomizeDiceRoll(diceSides) );
    }

    console.log('roll', roll);
  
    checkForTies(roll, diceSides);
};
  var randomizeDiceRoll = function(diceSides) {
    //var diceSides = [1,2,3,4,5,6];
    return Math.floor((Math.random() * diceSides[diceSides.length-1]) + 1);
  };



var checkForTies = function(roll, diceSides) {
  if(roll.length > diceSides.length) {
    return roll;
  }
  else {
    roll.forEach(
    function(n, idx) {
      if(roll.indexOf(n) != roll.lastIndexOf(n)) {
        tie = true;
        roll[roll.indexOf(n)] = randomizeDiceRoll(diceSides);
        roll[roll.lastIndexOf(n)] = randomizeDiceRoll(diceSides);
        checkForTies(roll, diceSides);
      }
  });
  return roll;
  }
  
}

rollDice(6); //should end with unique values for each array element
rollDice(7); //there will be duplicate elements

OK, so you can see we've restructured things a bit. We're now passing the diceSides to each of the child functions (not ideal, but it works for now), and we check for a tie. If the number of sides of the dice is greater than or equal to the number of players rolling, we can sort out the tie - if not, we just return the original array of values, as it's mathematically impossible for there NOT to be a tie (ex: 7 players with a 6-sided die).

For now, we have a solution for our randomized dice roll. Some enhancements would be a config flag to say whether or not to trigger the tie-resolving method (you may want it in certain cases, but not in others), and a cleaner implementation architecturally. So I'll work on those soon.

Thanks for reading - feedback welcome.

Leave a Reply

Your email address will not be published. Required fields are marked *