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.