Skip to main content
added 138 characters in body
Source Link
 function ValidityCheck(){
  this.result = true;
  this.reason = '';
  
  this.set = function(status,reason){
    this.result = status;
    this.reason = reason || '';
  }
}

var ruleEngine = (function ruleEngine(){
  
  var game;
  
  function cardsToDraw(card){
    
    if(!game.validityCheck.result) return game.validityCheck;

    var result = new ValidityCheck();
            
    if(game.cardsToDraw() > 0 && card.value !== '7'){
      result.set(false, 'cards to draw');
    }
    
    return result;
    
  }
  
  function cardMatch(card){

    if(!game.validityCheck.result) return game.validityCheck;
    
    var result = new ValidityCheck();
    
    var requirements = game.getRequirements();
    
    if (card.color !== requirements.color && card.value !== requirements.value && card.value !== 'J') {
      result.set(false, 'card mismatch')
    }
    
    return result;
  }
  
  var rules = {
    'cards to draw': cardsToDraw,
    'card match': cardMatch
  };
  
  function check(rule,move){
    
    if(!game) throw new Error('No game was set');
    
    game.validityCheck = rules[rule](move.card);
    
    return this;
  }
  
  function setGame(gameData){
    game = gameData;
  }
  
  return{
    check: check,
    setGame: setGame
  }
  
  })();
  
  module.exports = ruleEngine;
 function ValidityCheck(){
  this.result = true;
  this.reason = '';
  
  this.set = function(status,reason){
    this.result = status;
    this.reason = reason || '';
  }
}

var ruleEngine = (function ruleEngine(){
  
  var game;
  
  function cardsToDraw(card){
    
    var result = new ValidityCheck();
            
    if(game.cardsToDraw() > 0 && card.value !== '7'){
      result.set(false, 'cards to draw');
    }
    
    return result;
    
  }
  
  function cardMatch(card){
    
    var result = new ValidityCheck();
    
    var requirements = game.getRequirements();
    
    if (card.color !== requirements.color && card.value !== requirements.value && card.value !== 'J') {
      result.set(false, 'card mismatch')
    }
    
    return result;
  }
  
  var rules = {
    'cards to draw': cardsToDraw,
    'card match': cardMatch
  };
  
  function check(rule,move){
    
    if(!game) throw new Error('No game was set');
    
    game.validityCheck = rules[rule](move.card);
    
    return this;
  }
  
  function setGame(gameData){
    game = gameData;
  }
  
  return{
    check: check,
    setGame: setGame
  }
  
  })();
  
  module.exports = ruleEngine;
 function ValidityCheck(){
  this.result = true;
  this.reason = '';
  
  this.set = function(status,reason){
    this.result = status;
    this.reason = reason || '';
  }
}

var ruleEngine = (function ruleEngine(){
  
  var game;
  
  function cardsToDraw(card){
    
    if(!game.validityCheck.result) return game.validityCheck;

    var result = new ValidityCheck();
            
    if(game.cardsToDraw() > 0 && card.value !== '7'){
      result.set(false, 'cards to draw');
    }
    
    return result;
    
  }
  
  function cardMatch(card){

    if(!game.validityCheck.result) return game.validityCheck;
    
    var result = new ValidityCheck();
    
    var requirements = game.getRequirements();
    
    if (card.color !== requirements.color && card.value !== requirements.value && card.value !== 'J') {
      result.set(false, 'card mismatch')
    }
    
    return result;
  }
  
  var rules = {
    'cards to draw': cardsToDraw,
    'card match': cardMatch
  };
  
  function check(rule,move){
    
    if(!game) throw new Error('No game was set');
    
    game.validityCheck = rules[rule](move.card);
    
    return this;
  }
  
  function setGame(gameData){
    game = gameData;
  }
  
  return{
    check: check,
    setGame: setGame
  }
  
  })();
  
  module.exports = ruleEngine;
Source Link

I think it is worth another answer to my own question to show my implementation of a 'ruleEngine' as @mjgpy3 has suggested.

I really liked the idea of being able to pipe all the rules in correct order, however it bothered me that I would have to set up a call to invalidOn after each "rule".

Here is what I did:

 function ValidityCheck(){
  this.result = true;
  this.reason = '';
  
  this.set = function(status,reason){
    this.result = status;
    this.reason = reason || '';
  }
}

var ruleEngine = (function ruleEngine(){
  
  var game;
  
  function cardsToDraw(card){
    
    var result = new ValidityCheck();
            
    if(game.cardsToDraw() > 0 && card.value !== '7'){
      result.set(false, 'cards to draw');
    }
    
    return result;
    
  }
  
  function cardMatch(card){
    
    var result = new ValidityCheck();
    
    var requirements = game.getRequirements();
    
    if (card.color !== requirements.color && card.value !== requirements.value && card.value !== 'J') {
      result.set(false, 'card mismatch')
    }
    
    return result;
  }
  
  var rules = {
    'cards to draw': cardsToDraw,
    'card match': cardMatch
  };
  
  function check(rule,move){
    
    if(!game) throw new Error('No game was set');
    
    game.validityCheck = rules[rule](move.card);
    
    return this;
  }
  
  function setGame(gameData){
    game = gameData;
  }
  
  return{
    check: check,
    setGame: setGame
  }
  
  })();
  
  module.exports = ruleEngine;

This allows me to do this:

ruleEngine.setGame(game);

function checkCard(move){
  
  ruleEngine
  .check('cards to draw',move)
  .check('card match',move);
  
  if(game.validityCheck.result){
    // make move
  }
  else{
    // return reason
  }
  
}

Which feels pretty nice, compared to my first solution. Still plenty of room to improve, though.

I wonder if it would be better to go the other way round and set up a rule engine for a game. Then I could implement a setRule method and would get a strongly encapsulated game engine that can be passed to other games as well.

Also, currently a failed check will set up a property on the game itself. Is this a good idea?