Sunday, September 09, 2012

Conditional Branches with sub blocks and 'switch' statements

Last week Mario introduced the new left hand side conditional element 'if/else if/else'. The next plans for this is to allow nested blocks in each conditional branch and to then to provide a 'switch' statement for sugar. You can see more examples of both, plus their logical rule equivalents here, https://community.jboss.org/wiki/BranchCEs

Some time ago I started to write a pacman example, it's still unfinished, but I can share with you how these new constructs will improve maintainability with reduced rules. The original drl can be found here:
https://raw.github.com/droolsjbpm/drools/master/drools-examples/src/main/resources/org/drools/examples/pacman/pacman.drl

The pacman.drl has the following three rules:
/**
 * By increasing the tick we slow down the time to the next move.
 * I use the CE 'or' here rathre than an infix "in" to maximise node sharing
 * with both the EatFood and EatPowerPill rules.
 */
rule SlowWhenEating dialect "mvel" no-loop salience 10when
    $char : Character( name == "Pacman" )
    $l : Location( character == $char )
    $target : Cell( row == $l.row, col == $l.col)
    (or $contents : CellContents( cell == $target, cellType == CellType.FOOD )
        $contents : CellContents( cell == $target, cellType == CellType.POWER_PILL ) )    
    $update : ScheduledLocationUpdate( character == $char )
then  
    modify ( $update ) { tock += 2 };
end


/**
 * When we move onto a FOOD cell, increase the score by 1
 */
rule EatFood dialect "mvel" when
    $char : Character( name == "Pacman" )
    $l : Location( character == $char )
    $target : Cell( row == $l.row, col == $l.col)
    $contents : CellContents( cell == $target, cellType == CellType.FOOD )
    $s : Score()   
then
    modify( $contents ) { cellType = CellType.EMPTY };
    modify( $s ) { score += 1 };    
end

/**
 * When we move onto a POWER_PILL cell, increase the score by 5
 */
rule EatPowerPill dialect "mvel" when
    $char : Character( name == "Pacman" )
    $l : Location( character == $char )
    $target : Cell( row == $l.row, col == $l.col)
    $contents : CellContents( cell == $target, cellType == CellType.POWER_PILL )
    $s : Score()   
then
    modify( $contents ) { cellType = CellType.EMPTY };
    modify( $s ) { score += 5 };    
end

Once we support 'switch' with nested blocks we should be able to do the following. Note I also removed the 'no-loop', as it's no longer needed with property reactive.

rule eatFoodOrPill when
    $s : Score()  
    $char : Character( name == "Pacman" )
    $l : Location( character == $char )
    $target : Cell( row == $l.row, col == $l.col)
    $contents : CellContents( cell == $target )
    switch( cellType ) {
        case CellType.FOOD : {  
            do[scorePlus1]      
            $update : ScheduledLocationUpdate( character == $char )
            do[slowWhenEating]
        }
        case CellType.POWER_PILL  : {
            do[scorePlus5]        
            $update : ScheduledLocationUpdate( character == $char )
            do[slowWhenEating]            
        }        
    }
then[slowWhenEating]  
    modify ( $update ) { tock += 2 };
then[scorePlus1]
    modify( $contents ) { cellType = CellType.EMPTY };
    modify( $s ) { score += 5 };    
then[scorePlus5]
    modify( $contents ) { cellType = CellType.EMPTY };
    modify( $s ) { score += 5 };     
end

3 comments:

  1. This multi-condition/multi-action rule seems like some kind of a decision table equivalent. My personal preference in this case would be (surprise, surprise) a decision table

    ReplyDelete
  2. Suppose that, in one of your consequences, you have a modify block that alters the facts that are to be reasoned upon later in the rule condition. Would this change be taken into account in the same rule?

    rule eatFoodOrPill when
    $s : Score()
    $char : Character( name == "Pacman" )
    $l : Location( character == $char )
    $target : Cell( row == $l.row, col == $l.col)
    $contents : CellContents( cell == $target )
    switch( cellType ) {
    case CellType.FOOD : {
    do[scorePlus1]
    $update : ScheduledLocationUpdate( character == $char )
    do[slowWhenEating]
    }
    case CellType.POWER_PILL : {
    do[scorePlus5]
    $update : ScheduledLocationUpdate( character == $char )
    do[slowWhenEating]
    }
    }
    then[slowWhenEating]
    modify ( $update ) { tock += 2 };
    then[scorePlus1]
    modify( $contents ) { cellType = CellType.POWERPILL };
    modify( $s ) { score += 5 };
    then[scorePlus5]
    modify( $contents ) { cellType = CellType.EMPTY };
    modify( $s ) { score += 5 };
    end

    Probably the answer is that that change will not be taken into account in this very rule, but it would be nice to have it clearly stated.

    Regards,
    Frank

    ReplyDelete
  3. It depends on how you use property reactive. You can control what field changes a pattern reacts too.

    ReplyDelete