Chapter 29. Number Guess Example
29.1. Number Guess Example: Loading the Example
final KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(); kbuilder.add( ResourceFactory.newClassPathResource( "NumberGuess.drl", ShoppingExample.class ), ResourceType.DRL ); kbuilder.add( ResourceFactory.newClassPathResource( "NumberGuess.rf", ShoppingExample.class ), ResourceType.DRF ); final KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(); kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );
- The Number Guess example located in
NumberGuess.drl
shows the use of Rule Flow, a way of controlling the order in which rules are fired. It is loaded as shown above.
29.2. Number Guess Example: Starting the RuleFlow
final StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession(); KnowledgeRuntimeLogger logger = KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "log/numberguess"); ksession.insert( new GameRules( 100, 5 ) ); ksession.insert( new RandomNumber() ); ksession.insert( new Game() ); ksession.startProcess( "Number Guess" ); ksession.fireAllRules(); logger.close(); ksession.dispose();
- The above code demonstrates the creation of the package and the loading of the rules (using the
add()
method). - There is an additional line to add the Rule Flow (
NumberGuess.rf
), which provides the option of specifying different rule flows for the same Knowledge Base. - Once the Knowledge Base is created it can be used to obtain a Stateful Session. The facts are then inserted.
29.3. Number Guess Example: Classes and Methods
Note
The Number Guess Example classes are all contained within the
NumberGuessExample.java
file.
- Class
GameRules
provides the maximum range and the number of guesses allowed. - Class
RandomNumber
automatically generates a number between 0 and 100 and makes it available to the rules. It does so by insertion via thegetValue()
method. - Class
Game
keeps track of the number of guesses made. - To start the process, the
startProcess()
method is called. - To execute the rules, the
fireAllRules()
method is called. - To clear the Working Memory session, the
dispose()
method is called.
29.4. Number Guess Example: Observing the RuleFlow
Procedure 29.1. Task
- Open the
NumberGuess.rf
file in the Drools IDE. A diagram will appear that works much like a standard flowchart. - To edit the diagram, use the menu of available components to the left of the diagram in the IDE. This is called a palette.
- Save the diagram in XML. (If installed, you can utilise XStream to do this.)
- If it is not already open, ensure that the Properties View is visible in the IDE. It can be opened by clicking→ → where you can select the
Properties
view. If you do this before you select an item on the rule flow (or click on the blank space in the rule flow) you will see the properties. You can use these properties to identify processes and observe changes.
29.5. Number Guess Example: RuleFlow Nodes
In the Number Guess RuleFlow there are several node types:
- The Start node (white arrow in a green circle) and the End node (red box) mark beginning and end of the rule flow.
- A Rule Flow Group box (yellow, without an icon) represents a Rule Flow Groups defined in the rules (DRL) file. For example, when the flow reaches the Rule Flow Group "Too High", only those rules marked with an attribute of
ruleflow-group
"Too High"
can potentially fire. - Action nodes (yellow cog-shaped icon) perform standard Java method calls. Most action nodes in this example call
System.out.println()
, indicating the program's progress to the user. - Split and Join Nodes (blue ovals, no icon) such as "Guess Correct?" and "More guesses Join" mark places where the flow of control can split and rejoin.
- Arrows indicate the flow between the various nodes.
29.6. Number Guess Example: Firing Rules at a Specific Point in NumberGuess.drl
rule "Get user Guess" ruleflow-group "Guess" no-loop when $r : RandomNumber() rules : GameRules( allowed : allowedGuesses ) game : Game( guessCount < allowed ) not ( Guess() ) then System.out.println( "You have " + ( rules.allowedGuesses - game.guessCount ) + " out of " + rules.allowedGuesses + " guesses left.\nPlease enter your guess from 0 to " + rules.maxRange ); br = new BufferedReader( new InputStreamReader( System.in ) ); i = br.readLine(); modify ( game ) { guessCount = game.guessCount + 1 } insert( new Guess( i ) ); end
- The various nodes in combination with the rules make the Number Guess game work. For example, the "Guess" Rule Flow Group allows only the rule "Get user Guess" to fire, because only that rule has a matching attribute of
ruleflow-group
"Guess"
. - The LHS section (after
when
) of the rule states that it will be activated for eachRandomNumber
object inserted into the Working Memory whereguessCount
is less thanallowedGuesses
from theGameRules
object and where the user has not guessed the correct number. - The RHS section (or consequence, after
then
) prints a message to the user and then awaits user input fromSystem.in
. After obtaining this input (thereadLine()
method call blocks until the return key is pressed) it modifies the guess count and inserts the new guess, making both available to the Working Memory. - The package declares the dialect as MVEL and various Java classes are imported.
In total, there are five rules in this file:
- Get User Guess, the Rule examined above.
- A Rule to record the highest guess.
- A Rule to record the lowest guess.
- A Rule to inspect the guess and retract it from memory if incorrect.
- A Rule that notifies the user that all guesses have been used up.
29.7. Number Guess Example: Viewing RuleFlow Constraints
Procedure 29.2. Task
- In the IDE, go to the
Properties
view and open the Constraints Editor by clicking on the "Constraints" property line. - Click on the
Edit
button besideTo node Too High
to open the dialogue which will present you with various options. The values in theTextual Editor
window follow the standard rule format for the LHS and can refer to objects in Working Memory. The consequence (RHS) is that the flow of control follows this node (that is,To node Too High
) if the LHS expression evaluates to true.
29.8. Number Guess Example: Console Output
You have 5 out of 5 guesses left. Please enter your guess from 0 to 100 50 Your guess was too high You have 4 out of 5 guesses left. Please enter your guess from 0 to 100 25 Your guess was too low You have 3 out of 5 guesses left. Please enter your guess from 0 to 100 37 Your guess was too low You have 2 out of 5 guesses left. Please enter your guess from 0 to 100 44 Your guess was too low You have 1 out of 5 guesses left. Please enter your guess from 0 to 100 47 Your guess was too low You have no more guesses The correct guess was 48
- Since the file
NumberGuess.java
contains amain()
method, it can be run as a standard Java application, either from the command line or via the IDE. A typical game might result in the interaction above. The numbers in bold were typed in by the user. - The
main()
method ofNumberGuessExample.java
loads a Rule Base, creates a Stateful Session and insertsGame
,GameRules
andRandomNumber
(containing the target number) objects into it. The method also sets the process flow to be used and fires all rules. Control passes to the RuleFlow. - The RuleFlow file
NumberGuess.rf
begins at the "Start" node. - At the Guess node, the appropriate Rule Flow Group ("Get user Guess") is enabled. In this case the Rule "Guess" (in the
NumberGuess.drl
file) is triggered. This rule displays a message to the user, takes the response, and puts it into Working Memory. Flow passes to the next Rule Flow Node. - At the next node, "Guess Correct", constraints inspect the current session and decide which path to take.If the guess in step 4 was too high or too low, flow proceeds along a path which has an action node with normal Java code printing a suitable message and a Rule Flow Group causing a highest guess or lowest guess rule to be triggered. Flow passes from these nodes to step 6.If the guess in step 4 was right, we proceed along the path towards the end of the RuleFlow. Before this, an action node with normal Java code prints a statement "you guessed correctly". There is a join node here (just before the Rule Flow end) so the no-more-guesses path (step 7) can also terminate the RuleFlow.
- Control passes as per the RuleFlow via a join node to a "guess incorrect" RuleFlow Group (triggering a rule to retract a guess from Working Memory) and onto the "More guesses" decision node.
- The "More guesses" decision node (on the right hand side of the rule flow) uses constraints, again looking at values that the rules have put into the working memory, to decide if the user has more guesses and. If so, it moves to step 3. If not, the user proceeds to the end of the RuleFlow via a RuleFlow Group that triggers a rule stating "you have no more guesses".
- The loop over steps 3 to 7 continues until the number is guessed correctly or the user runs out of guesses.