Search code examples
javascriptarraysfunctionsimulationdice

Is it possible to remove a part of a function? (i.e. a specific variable when a condition is met)


I'm trying to create a simple dice game using JavaScript. I want to simulate a Yahtzee game with 5 dice, and after each dice roll, every dice showing 6 should be put aside and the code should roll the remaining dice until all dice are put aside.

Here's what I've done so far:

<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<head>

<style>
div.dice{
float:left;
width:32px;
background:#F5F5F5;
border:#999 1px solid;
padding:10px;
font-size:24px;
text-align:center;
margin:5px;
}
</style>

<script>

function rollDice1(){
 var die1 = document.getElementById("die1");
 var d1 = Math.floor(Math.random() * 6) + 1;
 die1.innerHTML = d1;
}

function rollDice2(){
 var die2 = document.getElementById("die2");
 var d2 = Math.floor(Math.random() * 6) + 1;
 die2.innerHTML = d2;
}

function rollDice3(){
 var die3 = document.getElementById("die3");
 var d3 = Math.floor(Math.random() * 6) + 1;
 die3.innerHTML = d3;
 var status3 = d3;
}

function rollDice4(){
 var die4 = document.getElementById("die4");
 var d4 = Math.floor(Math.random() * 6) + 1;
 die4.innerHTML = d4;
}

function rollDice5(){
 var die5 = document.getElementById("die5");
 var d5 = Math.floor(Math.random() * 6) + 1;
 die5.innerHTML = d5;
}

function allDice() {
 rollDice1(); rollDice2(); rollDice3(); rollDice4(); rollDice5();
}

</script>

</head>

<body>

<div id="die1" class="dice">0</div>
<div id="die2" class="dice">0</div>
<div id="die3" class="dice">0</div>
<div id="die4" class="dice">0</div>
<div id="die5" class="dice">0</div>
<button onclick="allDice()">Roll Dice</button>

</body>

</html>

I've managed to create my 5 dice, and they also get assigned a new value with each roll. However, I've yet to manage to find a way to stop rolling the specific dice that show the value of six.

Ideally I would just be able to keep pressing the button labelled "Roll Dice" until all five dice show the value 6.

I've tried many methods, including putting all 5 roll function into an array and then removing the functions responsible for the specific dice that end up showing 6 after each roll. However, creating arrays seem to somehow affect my previous code. I've gotten error messages such as 'die1' is 'null' or that the function 'allDice' is undefined.

If needed I can add in some of the other versions of the program I've tried making work; I chose not to add them initially as this post felt way too long.

So, I've reached a point where I'm trying to create a function that rolls all 5 dice with each press of the button and I'm then looking for a way to remove each of these functions whenever their respective dice shows 6, or remove the variable that overrides the number shown on the dice from each of the rolling functions. If this is not possible or another solution is more suitable, then please write them below.


Solution

  • If we look at your current die rolling algorithm we see that they are all the same except the element ID

    function rollDice5() {
      var die5 = document.getElementById("die5");
      var d5 = Math.floor(Math.random() * 6) + 1;
      die5.innerHTML = d5;
    }
    

    When can dry up your code by passing in the one thing that is changing (the element id) as an argument

    function rollDie(dieElementID){
        var die = document.getElementById(dieElementID);
        var newDieValue = Math.floor(Math.random() * 6) + 1;
        die.innerHTML = newDieValue;     
    }
    

    Element.innerHTML works for both setting and reading the element's HTML value. We can read what the current die is showing and determine if we need to roll it again.

    function rollDie(dieElementID){
        var die = document.getElementById(dieElementID);
        if(die.innerHTML != 6){
            var newDieValue = Math.floor(Math.random() * 6) + 1;
            die.innerHTML = newDieValue;
        }
    }
    

    To roll all the dice we simply call our new generic rollDie and pass in the id.

    function allDice() {
      rollDie("die1");
      rollDie("die2");
      rollDie("die3");
      rollDie("die4");
      rollDie("die5");
    }
    

    Every time we see a repetition of code that only has one thing changing we can try to dry it up. We can do this with allDice by storing a list of our die element ID's

    function allDice() {
      var diceToRoll = ["die1","die2","die3","die4","die5"];
      for(var i = 0; i < diceToRoll.length; i++){
         rollDie(diceToRoll[i]);
      }
    }
    

    This is the minimum we need to get it to just work. We should also re-examine our rollDie function. Notice that we have introduced the side effect of not rolling when the die value is 6. We should break up rolling and checking if it is six to create flexibility in our code. To do this we can create two seperate functions and pass in the die element instead of the die id.

    function rollDie(dieElement) {
      var newDieValue = Math.floor(Math.random() * 6) + 1;
      dieElement.innerHTML = newDieValue;   
    }
    
    function dieHasValue(dieElement, value) {
      return dieElement.innerHTML == value;
    }
    

    Then our allDice will look like this

    function allDice() {
      var diceToRoll = ["die1", "die2", "die3", "die4", "die5"];
      for (var i = 0; i < diceToRoll.length; i++) {
        var dieElement = document.getElementById(diceToRoll[i]);
        if (!dieHasValue(dieElement, 6)) {
          rollDie(dieElement);
        }
      }
    }
    

    function rollDie(dieElement) {
      var newDieValue = Math.floor(Math.random() * 6) + 1;
      dieElement.innerHTML = newDieValue;
    }
    
    function dieHasValue(dieElement, value) {
      return dieElement.innerHTML == value;
    }
    
    function allDice() {
      var diceToRoll = ["die1", "die2", "die3", "die4", "die5"];
      for (var i = 0; i < diceToRoll.length; i++) {
        var dieElement = document.getElementById(diceToRoll[i]);
        if (!dieHasValue(dieElement, 6)) {
          rollDie(dieElement);
        }
      }
    }
    div.dice {
      float: left;
      width: 32px;
      background: #F5F5F5;
      border: #999 1px solid;
      padding: 10px;
      font-size: 24px;
      text-align: center;
      margin: 5px;
    }
    <html>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    
    <head>
    </head>
    
    <body>
      <div id="die1" class="dice">0</div>
      <div id="die2" class="dice">0</div>
      <div id="die3" class="dice">0</div>
      <div id="die4" class="dice">0</div>
      <div id="die5" class="dice">0</div>
      <button onclick="allDice()">Roll Dice</button>
    
    </body>
    
    </html>

    If you want to you can use the Array manipulation functions to make allDice look cleaner.

    function getDie(elementId){
       return document.getElementById(elementId);
    }
    function dieNotShowingSix(dieElement){
       return !dieHasValue(dieElement,6);
    }
    function allDice() {
      ["die1", "die2", "die3", "die4", "die5"].
      map(getDie).
      filter(dieNotShowingSix).
      forEach(rollDie);
    }