An Easy Rules example (a Java “rules engine”)

I’m considering using Easy Rules as a simple “rules engine” in my Android Football Game application, primarily because (a) there are a ton of “rules” involved in having a computer call offensive and defensive plays, and (b) I’m trying to find a way to simplify that code and make it more maintainable.

The Easy Rules website has a Hello, world demo you can look at to get started, but after you take a look at that, here’s my first example.

The main “driver” class

My main class — the class that includes my main method — looks like this:

package test2;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.LogManager;
import org.easyrules.api.RulesEngine;
import static org.easyrules.core.RulesEngineBuilder.aNewRulesEngine;

public class FootballRulesTest1 {

    public static List<BasicFootballRule> defensiveRules = new ArrayList<BasicFootballRule>();
    
    public static void main(String[] ignore) {

        // turn off logging
        LogManager.getLogManager().reset();
    
        // i want a different set of rules for each down
        DefenseFirstDownRule firstDownRule   = new DefenseFirstDownRule();
        DefenseSecondDownRule secondDownRule = new DefenseSecondDownRule();
        DefenseThirdDownRule thirdDownRule   = new DefenseThirdDownRule();
        DefenseFourthDownRule fourthDownRule = new DefenseFourthDownRule();
    
        // add the rules to my own list so i can update the "game state" before each play.
        // this is a kludge because i can't (or don't know how to) get the list of rules back out of the RulesEngine.
        defensiveRules.add(firstDownRule);
        defensiveRules.add(secondDownRule);
        defensiveRules.add(thirdDownRule);
        defensiveRules.add(fourthDownRule);

        // create a RuleEngine instance
        RulesEngine rulesEngine = aNewRulesEngine().build();

        // register the rules
        rulesEngine.registerRule(firstDownRule);
        rulesEngine.registerRule(secondDownRule);
        rulesEngine.registerRule(thirdDownRule);
        rulesEngine.registerRule(fourthDownRule);

        // set the current state on all rules
        FootballGameState gameState = new FootballGameState(1, 10, 20, 1, 15, 0, 0); // beginning of game
        for (BasicFootballRule rule: defensiveRules) {
            rule.setGameState(gameState);
        }

        // fire the rules
        rulesEngine.fireRules();
    
    }

}

BasicFootballRule class

My BasicFootballRule class is an abstract class. Here’s the code for it:

package test2;

import org.easyrules.core.BasicRule;

//TODO the "football game state" stuff should be a separate interface
public abstract class BasicFootballRule extends BasicRule {

    protected FootballGameState gameState;

    public void setGameState(FootballGameState gameState) {
        this.gameState = gameState;
    }
        
}

The football game state

The way my game works is that a human always plays against a computer, and as a result, I need to update the game state between each play. For the purposes of this example I create this “state” class:

package test2;

public class FootballGameState {
    
    public FootballGameState(
        int down, 
        int distance, 
        int fieldPosition,
        int quarter, 
        int timeRemaining, 
        int humanScore, 
        int computerScore) {
    this.down = down;
    this.distance = distance;
    this.fieldPosition = fieldPosition;
    this.quarter = quarter;
    this.timeRemaining = timeRemaining;
    this.humanScore = humanScore;
    this.computerScore = computerScore;
    }

    public int down;
    public int distance;
    public int fieldPosition;
    public int quarter;
    public int timeRemaining; // seconds
    public int humanScore;
    public int computerScore;

}

So this line of code in the main class:

FootballGameState gameState = new FootballGameState(1, 10, 20, 1, 15, 0, 0);

can be interpreted as “First down, ten yards to go, ball is on the 20 yard line, 1st quarter, 15 minutes left in the quarter, and the score is 0-0.” (There

The 1st through 4th down classes

The classes for 1st, 2nd, 3rd, and 4th down look like this:

public class DefenseFirstDownRule extends BasicFootballRule {
    
    // return `true` if the rule should be applied, `false` else
    @Override public boolean evaluate() {
        if (gameState == null) return false;
        if (gameState.down != 1) return false;

        // we know it's 1st down ...
        return true;
    }
    
    @Override public void execute() {
        System.out.println("1st Down rule fired");
    }
    
}

public class DefenseSecondDownRule extends BasicFootballRule {
    
    // return `true` if the rule should be applied, `false` else
    @Override public boolean evaluate() {
        if (gameState == null) return false;
        if (gameState.down != 2) return false;

        // we know it's 2nd down ...
        return true;
    }
    
    @Override public void execute() {
        System.out.println("2nd Down rule fired");
    }
    
}

public class DefenseThirdDownRule extends BasicFootballRule {
    
    // return `true` if the rule should be applied, `false` else
    @Override public boolean evaluate() {
        if (gameState == null) return false;
        if (gameState.down != 3) return false;

        // we know it's 3rd down ...
        return true;
    }
    
    @Override public void execute() {
        System.out.println("3rd Down rule fired");
    }
    
}

public class DefenseFourthDownRule extends BasicFootballRule {
    
    // return `true` if the rule should be applied, `false` else
    @Override public boolean evaluate() {
        if (gameState == null) return false;
        if (gameState.down != 4) return false;

        // we know it's 4th down ...
        return true;
    }
    
    @Override public void execute() {
        System.out.println("4th Down rule fired");
    }
    
}

Running the code

When I run the main method, the code prints the following output:

1st Down rule fired

getName() is required(!)

It’s stated pretty clearly in the documentation but I still didn’t notice this: you need to implement the getName() method in your Rule classes, like this:

public class DefenseFirstDownRule extends BasicFootballRule {
    
    // return `true` if the rule should be applied, `false` else
    @Override public boolean evaluate() {
        System.out.println("evalute 1st down");
        if (gameState == null) return false;
        if (gameState.down != 1) return false;

        // we know it's 1st down ...
        return true;
    }
    
    // MUST IMPLEMENT THIS METHOD
    @Override public String getName() {
        return "1st Down Rule";
    }
    
    @Override public void execute() {
        System.out.println("1st Down rule fired");
    }
    
}

If you don’t do this, your rules will silently fail (as I learned). At the moment I don’t know why that’s necessary, but it is.

Discussion

I actually don’t have too much to say about this code right now, other than yes, the code needs to be cleaned up a little, and also, I can put my rules in a static ArrayList before the main method, which will help me avoid the current “kludge” in the code. (And I hope there are better improvements for that kludge.)

I have a lot to learn about Easy Rules, and I want to do some things to try to implement priorities, and possibly composite rules and listeners (both shown at the bottom of this page).

The current rules required for the computer to call a play on defense would take at least four printed pages, and they are all if/then/else statements.

One thing I may do as a result of learning more about Easy Rules is to “reverse” my rules. Currently I have rules for downs one through four, but it may be that I would rather have rules for the defenses the computer can call, like this:

  • Defense43
  • DefenseRunBlitz
  • DefensePassBlitz
  • DefenseNickel
  • DefenseDime

Again, I’ll know more about that as I work with Easy Rules.

I’ll add more to this discussion as I learn more about Easy Rules, but until then, I hope it helps to see another example of how to use the “Easy Rules” Java rules engine.