[rules-users] Logic Problems in DROOLS

Mark Armitage mail at cluttered.co.uk
Mon Oct 20 11:44:43 EDT 2008


I've been playing with DROOLS again and had another look at  
LogicProblems. I am quite pleased with the program I have now created.  
It's pretty simple and should be easily modifiable for any of the  
magazine type logic problems. Anyway, just in case it's of interest to  
anyone else:

The java is:

package com.mda.logic;

import java.io.InputStreamReader;

import org.drools.RuleBase;
import org.drools.RuleBaseFactory;
import org.drools.StatefulSession;
import org.drools.compiler.PackageBuilder;

public class MonkeyDROOLS {

     /**
      * @param args
      */
     public static void main(final String[] args) throws Exception {

     	//Standard stuff to load in the DROOLS rules and set it all up
         final PackageBuilder builder = new PackageBuilder();
         builder.addPackageFromDrl( new  
InputStreamReader( MonkeyDROOLS.class.getResourceAsStream( "/ 
MonkeyDROOLS.drl" ) ) );

         final RuleBase ruleBase = RuleBaseFactory.newRuleBase();
         ruleBase.addPackage( builder.getPackage() );

         final StatefulSession session = ruleBase.newStatefulSession();

         //Define the various names being used for he problem
         String[] names     = new String[] { "anna", "harriet",  
"mike", "sam" };
         String[] fruits    = new String[] { "apple", "banana",  
"orange", "pear" };
         String[] positions = new String[] { "branch", "grass",  
"rock", "stream" };

         //Insert all the possible combinations
         for ( int n = 0; n < names.length; n++ ) {
             for ( int c = 0; c < fruits.length; c++ ) {
                 for ( int p = 0; p < positions.length; p++ ) {
                     session.insert( new GRID( names[n], fruits[c],  
positions[p]) );
                     //System.out.printf("Insert:%7s %7s %7s 
\n",names[n], fruits[c], positions[p]);
                 }
             }
         }
         //System.out.println( "----------------------------------");

         //Fire the rules and then clean-up
         session.fireAllRules();
         session.dispose();
     }


//Storage for the logic problem items plus accessors to get each item
     public static class GRID {
         private String name;
         private String fruit;
         private String position;

         public GRID(String name,
                 String fruit,
                 String position) {
       super();
       this.name = name;
       this.fruit = fruit;
       this.position = position;
         }


         /**
          * @return the fruit
          */
         public String getFruit() {
             return this.fruit;
         }
         /**
          * @return the name
          */
         public String getName() {
             return this.name;
         }

         /**
          * @return the position
          */
         public String getPosition() {
             return this.position;
         }

     }
}

The rules file is:

//Monkey Puzzle Logic Problem
//
//Mark Armitage 2008
//
//The basic idea is, in the JAVA we assert every possible combination.
//Then we have rules that retract anything given by the problems data.
//If it's a negative, i.e. 'sam doesn't like bananas' then that will
//equate to one rule; 'GRID( name == "sam", fruit == "banana")'
//in other words; if the entry is for 'sam' and the fruit is 'banana'  
then
//we know it can't be true so retract it.
//Positive rules are a bit more complicated, they require 2 rules ;-)
//So e.g. 'sam likes sitting on grass'. We are retracting remember, so  
we
//want to remove entries where sam isn't sitting on grass, or where  
someone
//else IS sitting on grass, we know both of those cases are false. So  
the
//rules would be; 'GRID( name == "sam", position  != "grass" )' &
//                'GRID( name != "sam", position  == "grass" )'
//I've tried to make it all as simple as possible, basically so that it
//should be easy to modify for ANY of the Logic Problems in the  
magazines.
//There is one final rule that has to be added; that's to remove  
duplicates.
//Sam can't be sat in two places, the apple can't be liked by two  
monkeys etc.

package com.mda.logic;

//So we can access the java GRID with the data
import com.mda.logic.MonkeyDROOLS.GRID;


//Salience 150 so it is printed first
//Just an introduction to the problem.
rule "Intro"
     salience 150
     when
     then
        	System.out.println("Monkey Logic Problem from Puzzler's  
Paradise");
        	 
System.out.println("============================================");
        	System.out.println("1) Sam doesn't like bananas, likes  
sitting on grass.");
        	System.out.println("2) Monkey sat on the rock ate the apple.");
        	System.out.println("   Monkey who ate the pear didn't sit on  
the tree branch.");
        	System.out.println("3) Anna sat by the stream but she didn't  
eat the pear.");
        	System.out.println("4) Harriet didn't sit on the tree  
branch.");
        	System.out.println("   Mike doesn't like oranges.");
        	System.out.println("");
end


//1) Sam doesn't like bananas, likes sitting on grass.
//[We're retracting, so if sam likes bananas then that can't be right,
// also if sam is not on the grass that has to come out,
// but so does anything on the grass that isn't sam.]
rule "#1"
     salience 100
     when
     	$fact: (GRID( name == "sam", fruit     == "banana") or
     	        GRID( name == "sam", position  != "grass" ) or
     	        GRID( name != "sam", position  == "grass" ));
	then
	    retract ($fact);
end


//2) Monkey sat on the rock ate the apple.
//   Monkey who ate the pear didn't sit on the tree branch.
//[We're retracting, so if the position is rock but not an apple then  
bin it,
// again you also have to do the other; if it is an apple but not on  
the rock.
// positive facts such as pos=rock, fruit=apple will equate to two  
retraction criteria,
// firstly the pos=rock but fruit<>apple; but also the other way  
around, fruit=apple but pos<>rock.]
rule "#2"
     salience 100
     when
     	$fact: (GRID( position == "rock",  fruit    != "apple" ) or
     	        GRID( fruit    == "apple", position != "rock"  ) or
     	        GRID( fruit    == "pear",  position == "branch"));
	then
	    retract ($fact);
end


//3) Anna sat by the stream but she didn't eat the pear.
rule "#3"
     salience 100
     when
     	$fact: (GRID( name     == "anna",   position != "stream") or
     	        GRID( position == "stream", name     != "anna"  ) or
     	        GRID( name     == "anna",   fruit    == "pear"  ));
	then
	    retract ($fact);
end


//4) Harriet didn't sit on the tree branch.
//   Mike doesn't like oranges.
rule "#4"
     salience 100
     when
     	$fact: (GRID( name == "harriet", position == "branch") or
     	        GRID( name == "mike",    fruit    == "orange"));
	then
	    retract ($fact);
end


//Ensure everything is used just once
//Find a solution but ensure that we don't have any duplicates.
rule "No duplicates"
     salience 50
     when
      	$sol1:  GRID( name == "anna",    $aP : position,
      									 $aF : fruit);
     	$sol2:  GRID( name == "harriet", $hP : position != $aP,
     									 $hF : fruit    != $aF);
     	$sol3:  GRID( name == "mike",    $mP : position != $aP,  
position != $hP,
     									 $mF : fruit    != $aF, fruit    != $hF);
     	$sol4:  GRID( name == "sam",     $sP : position != $aP,  
position != $hP, position != $mP,
     									 $sF : fruit    != $aF, fruit    != $hF, fruit    != $mF);
     then
		printGrid( "	  ",$sol1,0);
		printGrid( "	1:",$sol1,1);
		printGrid( "	2:",$sol2,1);
		printGrid( "	3:",$sol3,1);
		printGrid( "	4:",$sol4,1);
end


//Salience -99 so it is printed last
rule "Summary"
     salience -99
     when
     then
        	System.out.println("");
        	System.out.println("The solution should be:");
	    System.out.printf("	  %-7.7s %-7.7s %-8.8s 
\n","Name","Fruit","Position");
	    System.out.printf("	  %-7.7s %-7.7s %-8.8s 
\n","====","=====","========");
	    System.out.printf("	  %-7.7s %-7.7s %-8.8s 
\n","anna","orange","stream");
	    System.out.printf("	  %-7.7s %-7.7s %-8.8s 
\n","harriet","apple","rock");
	    System.out.printf("	  %-7.7s %-7.7s %-8.8s 
\n","mike","banana","branch");
	    System.out.printf("	  %-7.7s %-7.7s %-8.8s 
\n","sam","pear","grass");
end


function void printGrid( String tmp, GRID summary, int mode)
{
     if (mode == 0)
     {
        	System.out.println("");
        	System.out.println("The solution from DROOLS:");
	    System.out.printf(tmp+"%-7.7s %-7.7s %-8.8s 
\n","Name","Fruit","Position");
	    System.out.printf(tmp+"%-7.7s %-7.7s %-8.8s 
\n","====","=====","========");
     }
     else
     {
	    System.out.printf(tmp+"%-7.7s %-7.7s %-8.8s 
\n",summary.getName(),summary.getFruit(),summary.getPosition());
	}
}


A sample run is:

Monkey Logic Problem from Puzzler's Paradise
============================================
1) Sam doesn't like bananas, likes sitting on grass.
2) Monkey sat on the rock ate the apple.
    Monkey who ate the pear didn't sit on the tree branch.
3) Anna sat by the stream but she didn't eat the pear.
4) Harriet didn't sit on the tree branch.
    Mike doesn't like oranges.


The solution from DROOLS:
	  Name    Fruit   Position
	  ====    =====   ========
	1:anna    orange  stream
	2:harriet apple   rock
	3:mike    banana  branch
	4:sam     pear    grass

The solution should be:
	  Name    Fruit   Position
	  ====    =====   ========
	  anna    orange  stream
	  harriet apple   rock
	  mike    banana  branch
	  sam     pear    grass





More information about the rules-users mailing list