Its interesting, Conways game of life was created to demonstrate a "determinisitc universe" - if you think as the little "critters" as life forms its pretty cool. A good example of how very very simple rules (and only a small number) can create complex behaviour.



On 4/17/07, Mark Proctor <mproctor@redhat.com> wrote:
I have just moved Jeff Brown's "Conways Game of Life" to a stateful
example, this is now the best place to look to understand jboss rules.
It's also good to compare this stateful implementation to the old
stateless version. Trunk introduces a new feature to help deal with
recursion "lock-on-active" which stops a rule being able to create
activations while it's agenda-group/rule-flow-group has focus - so it's
a much stronger no-loop. Probably the most important thing this example
shows is how to think relationally, the old example used nested
properties and sets to maintain the "neighbour" information; this
example shows how to achieve the same but expressing it relationally,
using the Neighbor relation class, further it shows how we can exploit
the the cross-product relational information to drive the engine without
us having to write loops. Currently the example is using agenda-groups
to drive execution flow, I'm now in the process of moving this to
rule-flow-groups.

You'll need trunk to be able to run this, so checkout:
http://anonsvn.labs.jboss.com/labs/jbossrules/trunk/
Make sure you have maven 2.0.6 installed and then type the following
from the root directory:
mvn clean install -Declipse=true
The eclipse plugin will now be built in the drools-eclipse/target
directory, so open that up and unzip into your eclipse install (make
sure you use -clean to start eclipse). Then import drools-examples and
"run as application" the ConwayGUI class.

I plan to do a blog or two on how this exampe works, and my next
challenge will be to migrate the pacman game to jboss rules.

Mark

--
Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod
Street, Windsor, Berkshire,
SI4 1TE, United Kingdom.
Registered in UK and Wales under Company Registration No. 3798903
Directors: Michael Cunningham (USA), Charlie Peters (USA) and David
Owens (Ireland)



package org.drools.examples

import org.drools.examples.conway.Cell;
import org.drools.examples.conway.CellGrid;
import org.drools.examples.conway.Neighbor;
import org.drools.examples.conway.Phase ;
import org.drools.examples.conway.CellState;

import org.drools.WorkingMemory;
import org.drools.common.InternalWorkingMemoryActions;
import org.drools.RuleBase;


rule "register north east"
        agenda-group "register neighbor"
when
    CellGrid( $numberOfColumns : numberOfColumns )
        $cell: Cell( $row : row > 0, $col : col < ( $numberOfColumns - 1 ) )
        $northEast : Cell( row  == ($row - 1), col == $col )
then
        assert( new Neighbor( $cell, $northEast ) );
        assert( new Neighbor( $northEast, $cell ) );
end

rule "register north"
        agenda-group "register neighbor"
when
        $cell: Cell( $row : row > 0, $col : col )
        $north : Cell( row  == ($row - 1), col == $col )
then
        assert( new Neighbor( $cell, $north ) );
        assert( new Neighbor( $north, $cell ) );
end

rule "register north west"
        agenda-group "register neighbor"
when
        $cell: Cell( $row : row > 0, $col : col > 0 )
        $northWest : Cell( row  == ($row - 1), col == ( $col - 1 ) )
then
        assert( new Neighbor( $cell, $northWest ) );
        assert( new Neighbor( $northWest, $cell ) );
end

rule "register west"
        agenda-group "register neighbor"
when
        $cell: Cell( $row : row > 0, $col : col > 0 )
        $west : Cell( row  == $row, col == ( $col - 1 ) )
then
        assert( new Neighbor( $cell, $west ) );
        assert( new Neighbor( $west, $cell ) );
end

rule "Kill The Lonely"
        agenda-group "evaluate"
        no-loop
when
#       A live cell has fewer than 2 live neighbors
        theCell: Cell(liveNeighbors < 2, cellState == CellState.LIVE, phase == Phase.EVALUATE)
then
        theCell.setPhase(Phase.KILL);
        modify( theCell );
end

rule "Kill The Overcrowded"
        agenda-group "evaluate"
        no-loop
when
#       A live cell has more than 3 live neighbors
        theCell: Cell(liveNeighbors > 3, cellState == CellState.LIVE, phase == Phase.EVALUATE)
then
        theCell.setPhase(Phase.KILL);
        modify( theCell );
end

rule "Give Birth"
        agenda-group "evaluate"
        no-loop
when
#       A dead cell has 3 live neighbors
        theCell: Cell(liveNeighbors == 3, cellState == CellState.DEAD , phase == Phase.EVALUATE)
then
        theCell.setPhase(Phase.BIRTH);
        modify( theCell );
end

rule "reset calculate"
        agenda-group "reset calculate"
when
then
        WorkingMemory wm = drools.getWorkingMemory();
        wm.getAgenda().clearAgendaGroup( "calculate" );
end

rule "kill"
        agenda-group "kill"
        no-loop
when
        theCell: Cell(phase == Phase.KILL)
then
        theCell.setCellState(CellState.DEAD);
        theCell.setPhase(Phase.DONE);
        modify( theCell );
end

rule "birth"
        agenda-group "birth"
        no-loop
when
        theCell: Cell(phase == Phase.BIRTH)
then
        theCell.setCellState(CellState.LIVE);
        theCell.setPhase(Phase.DONE);
        modify( theCell );
end

rule "Calculate Live"
        agenda-group "calculate"
        lock-on-active
when
        theCell: Cell(cellState == CellState.LIVE)
        Neighbor(cell == theCell, $neighbor : neighbor)
then
        $neighbor.setLiveNeighbors( $neighbor.getLiveNeighbors() + 1 );
        $neighbor.setPhase( Phase.EVALUATE );
        modify( $neighbor );
end

rule "Calculate Dead"
        agenda-group "calculate"
        lock-on-active
when
        theCell: Cell(cellState == CellState.DEAD)
    Neighbor(cell == theCell, $neighbor : neighbor )
then
        $neighbor.setLiveNeighbors( $neighbor.getLiveNeighbors() - 1 );
        $neighbor.setPhase( Phase.EVALUATE );
        modify( $neighbor );
end

rule "Kill All"
        agenda-group "kill all"
        no-loop
when
        theCell: Cell(cellState == CellState.LIVE)
then
        theCell.setCellState(CellState.DEAD);
        modify( theCell );
end
package org.drools.examples.conway;

/**
* A <code>Cell</code> represents a single cell within a <code>CellGrid</code>.
* A cell may be either live or dead. <p/>
*
* @author <a href="mailto:brown_j@ociweb.com">Jeff Brown</a>
* @see CellState
* @see CellGrid
*/
public class Cell {

    private CellState cellState = CellState.DEAD;

    private int       phase     = Phase.DONE;

    private int       liveNeighbors;

    private int       col;

    private int       row;

    public Cell(int col,
                int row) {
        this.col = col;
        this.row = row;
    }

    public int getCol() {
        return col;
    }

    public int getRow() {
        return row;
    }

    public int getPhase() {
        return this.phase;
    }

    public void setPhase(int phase) {
        this.phase = phase;
    }

    public int getLiveNeighbors() {
        return this.liveNeighbors;
    }

    public void setLiveNeighbors(int liveNeighbors) {
        this.liveNeighbors = liveNeighbors;
    }

    /**
     * @return this cell's current life state
     * @see #queueNextCellState(org.drools.examples.conway.CellState)
     * @see CellState
     */
    public CellState getCellState() {
        return this.cellState ;
    }

    /**
     * Sets this cells state
     *
     * @param newState
     *            new state for this cell
     * @see CellState
     */
    public void setCellState(final CellState newState) {
        this.cellState = newState;
    }

    public String toString() {
        return cellState + " col=" + this.col + " row=" + this.row + " phase '" + phase + "' liveNeighbors '" + liveNeighbors + "'";
    }
}


package org.drools.examples.conway;

import org.drools.RuleBase;
import org.drools.WorkingMemory;
import org.drools.event.AgendaGroupPoppedEvent;
import org.drools.event.DefaultAgendaEventListener ;
import org.drools.examples.conway.patterns.ConwayPattern;

/**
* A <code>CellGrid</code> represents a grid of <code>Cell</code> objects.
* <p/>
*
* @author <a href="mailto: brown_j@ociweb.com">Jeff Brown</a>
* @see Cell
*/
public class CellGrid {

    private final Cell[][] cells;

    private WorkingMemory  workingMemory;

    /**
     * Constructs a CellGrid
     *
     * @param rows
     *            number of rows in the grid
     * @param columns
     *            number of columns in the grid
     */
    public CellGrid(final int rows,
                    final int columns) {
        this.cells = new Cell[rows][columns];

        final RuleBase ruleBase = ConwayRuleBaseFactory.getRuleBase();
        this.workingMemory = ruleBase.newWorkingMemory ();

        DefaultAgendaEventListener listener = new DefaultAgendaEventListener() {
            public void agendaGroupPopped(AgendaGroupPoppedEvent event,
                                          WorkingMemory workingMemory) {
                System.out.println( "popped AgendaGroup = '" + event.getAgendaGroup().getName() + "'" );
                System.out.println( CellGrid.this.toString() );
                 System.out.println( "" );
            }
        };

        this.workingMemory.addEventListener( listener );

        this.workingMemory.assertObject( this );

        // populate the array of Cells and hook each
        // cell up with its neighbors...
        for ( int row = 0; row < rows; row++ ) {
            for ( int column = 0; column < columns; column++ ) {
                final Cell newCell = new Cell( column,
                                               row );
                this.cells[row][column] = newCell;
                this.workingMemory.assertObject( newCell );
            }
        }
        this.workingMemory.setFocus ( "register neighbor" );
        this.workingMemory.fireAllRules();
    }

    /**
     * @param row
     *            row of the requested cell
     * @param column
     *            column of the requested cell
     * @return the cell at the specified coordinates
     * @see Cell
     */
    public Cell getCellAt(final int row,
                          final int column) {
        return this.cells[row][column];
    }

    /**
     * @return the number of rows in this grid
     * @see #getNumberOfColumns()
     */
    public int getNumberOfRows() {
        return this.cells.length;
    }

    /**
     * @return the number of columns in this grid
     * @see #getNumberOfRows()
     */
    public int getNumberOfColumns() {
        return this.cells[0].length;
    }

    /**
     * Moves this grid to its next generation
     *
     * @return <code>true</code> if the state changed, otherwise false
     * @see #transitionState()
     */
    public boolean nextGeneration() {
        System.out.println( "next generation" );
        workingMemory.setFocus( "calculate" );
        workingMemory.setFocus( "kill" );
        workingMemory.setFocus( "birth" );
        workingMemory.setFocus( "reset calculate" );
        workingMemory.setFocus( "rest" );
        workingMemory.setFocus( "evaluate" );
        workingMemory.fireAllRules();
        return workingMemory.getAgenda().getAgendaGroup( "evaluate" ).size() != 0;
    }

    /**
     * kills all cells in the grid
     */
    public void killAll() {
        this.workingMemory.setFocus( "calculate" );
        this.workingMemory.setFocus( "kill all" );
        this.workingMemory.setFocus( "reset calculate" );
        this.workingMemory.fireAllRules();
    }

    /**
     * Populates the grid with a <code>ConwayPattern</code>
     *
     * @param pattern
     *            pattern to populate the grid with
     * @see ConwayPattern
     */
    public void setPattern(final ConwayPattern pattern) {
        final boolean[][] gridData = pattern.getPattern();
        int gridWidth = gridData[0].length;
        int gridHeight = gridData.length;

        int columnOffset = 0;
        int rowOffset = 0;

        if ( gridWidth > getNumberOfColumns() ) {
            gridWidth = getNumberOfColumns();
        } else {
            columnOffset = (getNumberOfColumns() - gridWidth) / 2;
        }

        if ( gridHeight > getNumberOfRows() ) {
            gridHeight = getNumberOfRows();
        } else {
            rowOffset = (getNumberOfRows() - gridHeight) / 2;
        }

        killAll();

        for ( int column = 0; column < gridWidth; column++ ) {
            for ( int row = 0; row < gridHeight; row++ ) {
                if ( gridData[row][column] ) {
                    final Cell cell = getCellAt( row + rowOffset,
                                                 column + columnOffset );
                    cell.setCellState ( CellState.LIVE );
                    this.workingMemory.modifyObject( this.workingMemory.getFactHandle( cell ),
                                                     cell );
                }
            }
        }
        workingMemory.setFocus( "calculate" );
        workingMemory.fireAllRules();
        System.out.println( "" );
    }

    public String toString() {
        StringBuffer buf = new StringBuffer();

        for ( int i = 0; i < this.cells.length; i++ ) {
            for ( int j = 0; j < this.cells[i].length; j++ ) {
                Cell cell = this.cells[i][j];
                System.out.print( cell.getLiveNeighbors() + ((cell.getCellState() == CellState.DEAD) ? "D" : "L") + " " );
            }
            System.out.println( "" );
        }

        return buf.toString();
    }
}


package org.drools.examples.conway;

public class Neighbor {
    private Cell cell;
    private Cell neighbor;

    public Neighbor(Cell cell, Cell neighbor) {
        this.cell = cell;
        this.neighbor = neighbor;
    }

    public Cell getCell() {
        return cell;
    }

    public Cell getNeighbor() {
        return neighbor;
    }

    public String toString() {
        return "cell '"+ this.cell + "' neighbour '" + this.neighbor + "'";
    }

}


_______________________________________________
rules-users mailing list
rules-users@lists.jboss.org
https://lists.jboss.org/mailman/listinfo/rules-users