3
\$\begingroup\$

After reviewing this approach: Rock-Paper-Scissors with the revealing module pattern

I decided to write an alternative where the Revealing Module Pattern is used for the UI, model, and controller.

I am open to all feedback from style to design.

// noprotect
var roshambo = (function() {
  
  var UI = ( function containedUI(){
  
    var output = document.getElementById( 'output' );
    
    function log( message ){
      output.innerHTML = message + '<br>' + output.innerHTML;
    }
    
    return { 
      log : log 
    }; 
    
  })();
  
  var model = ( function containedModel(){
  
      //'Rock: 1, Paper: 2, Scissors: 3'
      var gamesCount = 0,
          winsCount = 0,
          robotMove,
          DRAW = 0,
          LOSE = 1,
          WIN  = 2,
          ROCK = 1,
          PAPER = 2,
          SCISSORS = 3,
          moves = [ '', 'Rock', 'Paper', 'Scissors' ],
          results = [ 'Draw !', 'You lose !', 'You win !' ];
    
    function registerResult( result ){
      if ( result == WIN ){ 
        winsCount++;
      }
      gamesCount++;
    }
    
    function getStats(){
      return {
        gamesCount : gamesCount , winsCount : winsCount
      };
    }
    
    function generateRobotMove(){
      return Math.floor(Math.random() * 3) + 1;
    }
    
    function getMoveDescription( move ){
      return moves[move];
    }
    
    function getResultDescription( result ){
      return results[result];
    }
    
    function compareMoves(human, robot) {
      
      // Tie
      if (human == robot) {
        return DRAW;
      }

      // Robot wins
      if (robot == ROCK && human == SCISSORS || 
          robot == PAPER && human == ROCK || 
          robot == SCISSORS && human == PAPER) {
        return LOSE;
      }

      //In all other cases, the player wins
      return WIN;
    }
    
    return { 
      registerResult : registerResult,
      generateRobotMove : generateRobotMove,
      getMoveDescription : getMoveDescription,
      getResultDescription : getResultDescription,
      getStats : getStats,
      compareMoves : compareMoves,
      ROCK: ROCK,
      PAPER: PAPER,
      SCISSORS: SCISSORS
    }; 
    
  })();
  
  
  var controller = ( function containedController(doc){
  
    function play(){
      UI.log( 'Click on of the move buttons to play' );
    }
    
    function newGame( humanMove ){
      
      var robotMove = model.generateRobotMove();
      
      UI.log( 'You chose ' + model.getMoveDescription( humanMove ) );
      UI.log( 'The robot played ' + model.getMoveDescription( robotMove ) );

      var result = model.compareMoves(humanMove, robotMove);
      
      UI.log( model.getResultDescription( result ) );
      model.registerResult( result );
      
      var stats = model.getStats();
      var winStats = Math.round( stats.winsCount / stats.gamesCount * 100 , 2);
      UI.log( 'You won ' + winStats + '% of total games !' );
    }
    
    doc.getElementById('Rock').addEventListener("click" , function clickRock(){
      newGame( model.ROCK );
    });
    
    doc.getElementById('Paper').addEventListener("click" , function clickRock(){
      newGame( model.PAPER );
    });

    doc.getElementById('Scissors').addEventListener("click" , function clickRock(){
      newGame( model.SCISSORS );
    });

    return { 
      play : play 
    }; 
    
  })(document);

  // Display a message with total winrate from 0 to 100% (rounded)
  function privateAlertTotalResults() {
    var results = Math.floor((privateGameDB / privateGameCount) * 100);
    UI.log('You won ' + results + '% of total games !');
  }

  // The only function available to the caller is the play function
  return {
    play: controller.play
  };

})();

// Init a game
roshambo.play();
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>

<button id="Rock">Rock</button>
<button id="Paper">Paper</button>
<button id="Scissors">Scissors</button>
  
<div id="output">
  
</div>
  


  
  
  
</body>
</html>

\$\endgroup\$
2
\$\begingroup\$
output.innerHTML = message + '<br>' + output.innerHTML;

// to

output.innerHTML = `${message}<br>${output.innerHTML}`;

If on ES6, template strings for easy string construction.

return { 
  log : log 
}; 

// to

return { log };

If on ES6, assigning variables to keys with the same name, the assignment can be omitted.

      DRAW = 0,
      LOSE = 1,
      WIN  = 2,
      ROCK = 1,
      PAPER = 2,
      SCISSORS = 3,

// to

const MOVE = { ROCK: 1, PAPER: 2, SCISSORS: 3 };
const RESULT = { DRAW: 0, LOSE: 1, WIN: 2 };

Move collections to something that resembles an enum. In this case, we use an object whose keys act like constants.

  return {
    gamesCount : gamesCount , winsCount : winsCount
  };

Might need a newline here

doc.getElementById('Rock').addEventListener("click" , function clickRock(){
  newGame( model.ROCK );
});

doc.getElementById('Rock').addEventListener("click" , () => newGame( model.ROCK ));

Arrow functions could be of use here for shorter syntax.

\$\endgroup\$
  • \$\begingroup\$ Hmmm, I dont do ES6, but you make a mighty good case ;) \$\endgroup\$ – konijn Jul 14 '16 at 1:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.