Left Hand Side (LHS)
Inside a package it is possible to define a rule (only one per package!). Each rule consists of a Left Hand Side (LHS) and a Right Hand Side (RHS). The LHS contains all the conditions that lead to the rule execution when they are met; the RHS contains all the consequences of the rule's execution. In this section it is shown how it is possible to leverage on the DLS4Gar APIs to properly define the LHS.
Rule Definition
Before defining the conditions to trigger a rule it is necessary to first declare the rule itself; this can be done with the newRule
method:
public static void main(String[] args) {
PackageDescr package = new PackageDescrBuilderImpl()
.name("eu.trentorise.game.model")
// Imports...
// Globals...
// Types declaration...
.newRule()
.name("my_amazing_rule")
.end()
.getDescr();
}
The rule can also be enriched by attributes such as the salience by using the attribute
method:
public static void main(String[] args) {
PackageDescr package = new PackageDescrBuilderImpl()
.name("eu.trentorise.game.model")
// Imports...
// Globals...
// Types declaration...
.newRule()
.name("my_amazing_rule")
.attribute("salience", "1000")
// Other attributes...
.end()
.getDescr();
}
LHS Definition
Constraint Types
A ConstraintType
instance allows to constraint the fact fields inside the LHS of a rule (i.e. we want a rule to fire when the Drools working memory contains a BadgeCollectionConcept
instance whose name is equal to amazing_collection
).
The constraint types provided by the DSL4GaR are the following:
LT
➡️ Less thanLTE
➡️ Less than or equal toGT
➡️ Greater thanGTE
➡️ Grater then or equal toEQ
➡️ Equal toNEQ
➡️ Not equal toCONTAINS
➡️ ContainsNOT_CONTAINS
➡️ Not contains
In the next paragraphs will be shown how they can be used.
Pattern and constraints
Once the rule is defined inside the package, it is possible to use the when
method to specify when it must be triggered:
- Java
- DRL
import eu.trentorise.game.model.BadgeCollectionConcept;
import eu.trentorise.game.model.Classification;
import static it.univaq.gamification.dsl.builders.lhs.ConstraintType.EQ;
import static it.univaq.gamification.dsl.builders.lhs.ConstraintType.NEQ;
public static void main(String[] args) {
PackageDescr package = new PackageDescrBuilderImpl()
.name("eu.trentorise.game.model")
.newImport(BadgeCollectionConcept.class).end()
.newImport(Classification.class).end()
.newRule()
.name("my_amazing_rule")
.attribute("salience", "1000")
.when()
.classification()
.name(EQ, "green classification")
.position(NEQ, 1)
.end()
.badgeCollection()
.name(EQ, "green leaves")
.badgeEarnedNotContains("gold-medal-green-1")
.end()
.end()
.end()
.getDescr();
}
package eu.trentorise.game.model
import eu.trentorise.game.notification.BadgeCollectionConcept
import eu.trentorise.game.task.Classification
rule "my_amazing_rule"
salience 1000
when
Classification( name == "green classification", position != 1 )
BadgeCollectionConcept( name == "green leaves", badgeEarned not contains "gold-medal-green-1" )
then
end
Use the tabs to switch from the Java code to the corresponding generated DRL code.
For each pattern used inside the when
method it is necessary to add the relative import inside the package definition (i.e. since the classification
method is used the newImport
method must be used to import the Classification
class).
Each gamification elements has an associated method that allows to apply the constraints to the relative pattern:
Action
➡️action()
BadgeCollectionConcept
➡️badgeCollection()
Challenge
➡️challenge()
Classification
➡️classification()
CustomData
➡️customData()
Game
➡️game()
InputData
➡️inputData()
Player
➡️player()
PointConcept
➡️pointConcept()
Propagation
➡️propagation()
Reward
➡️reward()
All the above methods provide a set of other methods to apply the constraints on the gamification elements.
For example, the action()
method provides the id()
and the name()
methods to apply the constraints on the relative fields of the action itself. All the available methods can be found here.
Of course you don't need to consult the source code every time you need to apply a constraint to a pattern; your IDE will suggest the available methods 😉.
Binding
In the LHS it is also possible to bind the patterns to variables:
- Java
- DRL
import eu.trentorise.game.model.Classification;
import it.univaq.gamification.dsl.binders.ClassificationBind;
import static it.univaq.gamification.dsl.builders.lhs.ConstraintType.EQ;
public static void main(String[] args) {
final ClassificationBind CLASSIFICATION_BIND = new ClassificationBind("classification");
PackageDescr package = new PackageDescrBuilderImpl()
.name("eu.trentorise.game.model")
.newImport(Classification.class).end()
.newRule()
.name("my_amazing_rule")
.attribute("salience", "1000")
.when()
.classification(CLASSIFICATION_BIND)
.name(EQ, "green classification")
.end()
.end()
.end()
.getDescr();
}
package eu.trentorise.game.model
import eu.trentorise.game.task.Classification
rule "my_amazing_rule"
salience 1000
when
$classification: Classification( name == "green classification", position != 1 )
BadgeCollectionConcept( name == "green leaves", badgeEarned not contains "gold-medal-green-1" )
then
end
Each gamification element has its relative binding class; in this case the ClassificationBind
class is used to create a binding for a Classification
instance.
Other then the pattern itself, also its fields can be bound to variables:
- Java
- DRL
import eu.trentorise.game.model.Classification;
import it.univaq.gamification.dsl.binders.Bind;
import static it.univaq.gamification.dsl.builders.lhs.ConstraintType.EQ;
public static void main(String[] args) {
final Bind CLASSIFICATION_NAME_BIND = new Bind("classificationName");
PackageDescr package = new PackageDescrBuilderImpl()
.name("eu.trentorise.game.model")
.newImport(Classification.class).end()
.newRule()
.name("my_amazing_rule")
.attribute("salience", "1000")
.when()
.classification()
.bindName(CLASSIFICATION_NAME_BIND)
.name(EQ, "green classification")
.end()
.end()
.end()
.getDescr();
}
package eu.trentorise.game.model
import eu.trentorise.game.task.Classification
rule "my_amazing_rule"
salience 1000
when
Classification( $classificationName : name, name == "green classification", position != 1 )
BadgeCollectionConcept( name == "green leaves", badgeEarned not contains "gold-medal-green-1" )
then
end
For each gamification element it is possible to bind all the relative fields.
In this case the class to define a binding is the Bind
class.
Furthermore, also the constraints can be bound:
- Java
- DRL
import eu.trentorise.game.model.Classification;
import it.univaq.gamification.dsl.binders.Bind;
import static it.univaq.gamification.dsl.builders.lhs.ConstraintType.EQ;
public static void main(String[] args) {
final Bind IS_GREEN_CLASSIFICATION_BIND = new Bind("isGreenClassification");
PackageDescr package = new PackageDescrBuilderImpl()
.name("eu.trentorise.game.model")
.newImport(Classification.class).end()
.newRule()
.name("my_amazing_rule")
.attribute("salience", "1000")
.when()
.classification()
.name(EQ, "green classification", IS_GREEN_CLASSIFICATION_BIND)
.end()
.end()
.end()
.getDescr();
}
package eu.trentorise.game.model
import eu.trentorise.game.task.Classification
rule "my_amazing_rule"
salience 1000
when
Classification( $isGreenClassification : name == "green classification", position != 1 )
BadgeCollectionConcept( name == "green leaves", badgeEarned not contains "gold-medal-green-1" )
then
end
For each method useful to apply a constraint to a gamification element's field it is possible to pass as last parameter a Bind
instance.
Conditional Elements
If a rule LHS contains multiples patterns their default conjunction is the logical and
; it is also possible to use other conjunctions such as or
, not
and exists
:
- Java
- DRL
import eu.trentorise.game.model.Action;
import eu.trentorise.game.model.Classification;
import eu.trentorise.game.model.BadgeCollectionConcept;
import static it.univaq.gamification.dsl.builders.lhs.ConstraintType.EQ;
public static void main(String[] args) {
PackageDescr package = new PackageDescrBuilderImpl()
.name("eu.trentorise.game.model")
.newImport(Classification.class).end()
.newRule()
.name("my_amazing_rule")
.when()
.or()
.action().name(EQ, "walk").end()
.action().name(EQ, "run").end()
.end()
.exists()
.classification().name(EQ, "green classification").end()
.end()
.not()
.badgeCollection().badgeEarnedContains("green-badge")
.end()
.end()
.end()
.getDescr();
}
package eu.trentorise.game.model
import eu.trentorise.game.task.Action
import eu.trentorise.game.task.Classification
import eu.trentorise.game.task.BadgeCollectionConcept
rule "my_amazing_rule"
when
Action( name == "walk" ) or Action( name == "run" )
exists( Classification( name == "green classification" ) )
not( BadgeCollectionConcept( badgeEarned contains "green-badge" ) )
then
end
Free Constraints
Sometimes the constraints might be more complex than others; in these cases it is possible to rely on the pattern
methods that accepts a String
representing the constraint:
- Java
- DRL
import eu.trentorise.game.model.InputData;
public static void main(String[] args) {
PackageDescr package = new PackageDescrBuilderImpl()
.name("eu.trentorise.game.model")
.newImport(Classification.class).end()
.newRule()
.name("my_amazing_rule")
.when()
.inputData()
.constraint("(data['walkDistance'] != null || data['bikeDistance'] != null) && ((data['busDistance'] == null || data['busDistance'] == 0) && (data['carDistance'] == null || data['carDistance'] == 0))")
.end()
.end()
.end()
.getDescr();
}
package eu.trentorise.game.model
import eu.trentorise.game.task.InputData
rule "my_amazing_rule"
when
InputData( (data['walkDistance'] != null || data['bikeDistance'] != null) && ((data['busDistance'] == null || data['busDistance'] == 0) && (data['carDistance'] == null || data['carDistance'] == 0)) )
then
end
The pattern
method should be used only when it is strictly necessary because while using it the main advantages of the DSL are lost (typo checking, autocomplete, etc.).