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.
<br><br><br><br><div><span class="gmail_quote">On 4/17/07, <b class="gmail_sendername">Mark Proctor</b> <<a href="mailto:mproctor@redhat.com">mproctor@redhat.com</a>> wrote:</span><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
I have just moved Jeff Brown's "Conways Game of Life" to a stateful<br>example, this is now the best place to look to understand jboss rules.<br>It's also good to compare this stateful implementation to the old
<br>stateless version. Trunk introduces a new feature to help deal with<br>recursion "lock-on-active" which stops a rule being able to create<br>activations while it's agenda-group/rule-flow-group has focus - so it's
<br>a much stronger no-loop. Probably the most important thing this example<br>shows is how to think relationally, the old example used nested<br>properties and sets to maintain the "neighbour" information; this
<br>example shows how to achieve the same but expressing it relationally,<br>using the Neighbor relation class, further it shows how we can exploit<br>the the cross-product relational information to drive the engine without
<br>us having to write loops. Currently the example is using agenda-groups<br>to drive execution flow, I'm now in the process of moving this to<br>rule-flow-groups.<br><br>You'll need trunk to be able to run this, so checkout:
<br><a href="http://anonsvn.labs.jboss.com/labs/jbossrules/trunk/">http://anonsvn.labs.jboss.com/labs/jbossrules/trunk/</a><br>Make sure you have maven 2.0.6 installed and then type the following<br>from the root directory:
<br>mvn clean install -Declipse=true<br>The eclipse plugin will now be built in the drools-eclipse/target<br>directory, so open that up and unzip into your eclipse install (make<br>sure you use -clean to start eclipse). Then import drools-examples and
<br>"run as application" the ConwayGUI class.<br><br>I plan to do a blog or two on how this exampe works, and my next<br>challenge will be to migrate the pacman game to jboss rules.<br><br>Mark<br><br>--<br>Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod
<br>Street, Windsor, Berkshire,<br>SI4 1TE, United Kingdom.<br>Registered in UK and Wales under Company Registration No. 3798903<br>Directors: Michael Cunningham (USA), Charlie Peters (USA) and David<br>Owens (Ireland)<br>
<br><br><br>package org.drools.examples<br><br>import org.drools.examples.conway.Cell;<br>import org.drools.examples.conway.CellGrid;<br>import org.drools.examples.conway.Neighbor;<br>import org.drools.examples.conway.Phase
;<br>import org.drools.examples.conway.CellState;<br><br>import org.drools.WorkingMemory;<br>import org.drools.common.InternalWorkingMemoryActions;<br>import org.drools.RuleBase;<br><br><br>rule "register north east"
<br> agenda-group "register neighbor"<br>when<br> CellGrid( $numberOfColumns : numberOfColumns )<br> $cell: Cell( $row : row > 0, $col : col < ( $numberOfColumns - 1 ) )<br> $northEast : Cell( row == ($row - 1), col == $col )
<br>then<br> assert( new Neighbor( $cell, $northEast ) );<br> assert( new Neighbor( $northEast, $cell ) );<br>end<br><br>rule "register north"<br> agenda-group "register neighbor"<br>
when<br> $cell: Cell( $row : row > 0, $col : col )<br> $north : Cell( row == ($row - 1), col == $col )<br>then<br> assert( new Neighbor( $cell, $north ) );<br> assert( new Neighbor( $north, $cell ) );
<br>end<br><br>rule "register north west"<br> agenda-group "register neighbor"<br>when<br> $cell: Cell( $row : row > 0, $col : col > 0 )<br> $northWest : Cell( row == ($row - 1), col == ( $col - 1 ) )
<br>then<br> assert( new Neighbor( $cell, $northWest ) );<br> assert( new Neighbor( $northWest, $cell ) );<br>end<br><br>rule "register west"<br> agenda-group "register neighbor"<br>
when<br> $cell: Cell( $row : row > 0, $col : col > 0 )<br> $west : Cell( row == $row, col == ( $col - 1 ) )<br>then<br> assert( new Neighbor( $cell, $west ) );<br> assert( new Neighbor( $west, $cell ) );
<br>end<br><br>rule "Kill The Lonely"<br> agenda-group "evaluate"<br> no-loop<br>when<br># A live cell has fewer than 2 live neighbors<br> theCell: Cell(liveNeighbors < 2, cellState ==
CellState.LIVE, phase == Phase.EVALUATE)<br>then<br> theCell.setPhase(Phase.KILL);<br> modify( theCell );<br>end<br><br>rule "Kill The Overcrowded"<br> agenda-group "evaluate"<br> no-loop
<br>when<br># A live cell has more than 3 live neighbors<br> theCell: Cell(liveNeighbors > 3, cellState == CellState.LIVE, phase == Phase.EVALUATE)<br>then<br> theCell.setPhase(Phase.KILL);<br> modify( theCell );
<br>end<br><br>rule "Give Birth"<br> agenda-group "evaluate"<br> no-loop<br>when<br># A dead cell has 3 live neighbors<br> theCell: Cell(liveNeighbors == 3, cellState == CellState.DEAD
, phase == Phase.EVALUATE)<br>then<br> theCell.setPhase(Phase.BIRTH);<br> modify( theCell );<br>end<br><br>rule "reset calculate"<br> agenda-group "reset calculate"<br>when<br>then
<br> WorkingMemory wm = drools.getWorkingMemory();<br> wm.getAgenda().clearAgendaGroup( "calculate" );<br>end<br><br>rule "kill"<br> agenda-group "kill"<br> no-loop
<br>when<br> theCell: Cell(phase == Phase.KILL)<br>then<br> theCell.setCellState(CellState.DEAD);<br> theCell.setPhase(Phase.DONE);<br> modify( theCell );<br>end<br><br>rule "birth"<br>
agenda-group "birth"<br> no-loop<br>when<br> theCell: Cell(phase == Phase.BIRTH)<br>then<br> theCell.setCellState(CellState.LIVE);<br> theCell.setPhase(Phase.DONE);<br> modify( theCell );
<br>end<br><br>rule "Calculate Live"<br> agenda-group "calculate"<br> lock-on-active<br>when<br> theCell: Cell(cellState == CellState.LIVE)<br> Neighbor(cell == theCell, $neighbor : neighbor)
<br>then<br> $neighbor.setLiveNeighbors( $neighbor.getLiveNeighbors() + 1 );<br> $neighbor.setPhase( Phase.EVALUATE );<br> modify( $neighbor );<br>end<br><br>rule "Calculate Dead"<br> agenda-group "calculate"
<br> lock-on-active<br>when<br> theCell: Cell(cellState == CellState.DEAD)<br> Neighbor(cell == theCell, $neighbor : neighbor )<br>then<br> $neighbor.setLiveNeighbors( $neighbor.getLiveNeighbors() - 1 );
<br> $neighbor.setPhase( Phase.EVALUATE );<br> modify( $neighbor );<br>end<br><br>rule "Kill All"<br> agenda-group "kill all"<br> no-loop<br>when<br> theCell: Cell(cellState ==
CellState.LIVE)<br>then<br> theCell.setCellState(CellState.DEAD);<br> modify( theCell );<br>end<br>package org.drools.examples.conway;<br><br>/**<br> * A <code>Cell</code> represents a single cell within a <code>CellGrid</code>.
<br> * A cell may be either live or dead. <p/><br> *<br> * @author <a href="mailto:<a href="mailto:brown_j@ociweb.com">brown_j@ociweb.com</a>">Jeff Brown</a><br> * @see CellState<br> * @see CellGrid
<br> */<br>public class Cell {<br><br> private CellState cellState = CellState.DEAD;<br><br> private int phase = Phase.DONE;<br><br> private int liveNeighbors;<br><br> private int col;<br>
<br> private int row;<br><br> public Cell(int col,<br> int row) {<br> this.col = col;<br> this.row = row;<br> }<br><br> public int getCol() {<br> return col;<br> }<br>
<br> public int getRow() {<br> return row;<br> }<br><br> public int getPhase() {<br> return this.phase;<br> }<br><br> public void setPhase(int phase) {<br> this.phase = phase;<br> }<br>
<br> public int getLiveNeighbors() {<br> return this.liveNeighbors;<br> }<br><br> public void setLiveNeighbors(int liveNeighbors) {<br> this.liveNeighbors = liveNeighbors;<br> }<br><br> /**<br>
* @return this cell's current life state<br> * @see #queueNextCellState(org.drools.examples.conway.CellState)<br> * @see CellState<br> */<br> public CellState getCellState() {<br> return this.cellState
;<br> }<br><br> /**<br> * Sets this cells state<br> *<br> * @param newState<br> * new state for this cell<br> * @see CellState<br> */<br> public void setCellState(final CellState newState) {
<br> this.cellState = newState;<br> }<br><br> public String toString() {<br> return cellState + " col=" + this.col + " row=" + this.row + " phase '" + phase + "' liveNeighbors '" + liveNeighbors + "'";
<br> }<br>}<br><br><br>package org.drools.examples.conway;<br><br>import org.drools.RuleBase;<br>import org.drools.WorkingMemory;<br>import org.drools.event.AgendaGroupPoppedEvent;<br>import org.drools.event.DefaultAgendaEventListener
;<br>import org.drools.examples.conway.patterns.ConwayPattern;<br><br>/**<br> * A <code>CellGrid</code> represents a grid of <code>Cell</code> objects.<br> * <p/><br> *<br> * @author <a href="mailto:
<a href="mailto:brown_j@ociweb.com">brown_j@ociweb.com</a>">Jeff Brown</a><br> * @see Cell<br> */<br>public class CellGrid {<br><br> private final Cell[][] cells;<br><br> private WorkingMemory workingMemory;
<br><br> /**<br> * Constructs a CellGrid<br> *<br> * @param rows<br> * number of rows in the grid<br> * @param columns<br> * number of columns in the grid<br> */<br> public CellGrid(final int rows,
<br> final int columns) {<br> this.cells = new Cell[rows][columns];<br><br> final RuleBase ruleBase = ConwayRuleBaseFactory.getRuleBase();<br> this.workingMemory = ruleBase.newWorkingMemory
();<br><br> DefaultAgendaEventListener listener = new DefaultAgendaEventListener() {<br> public void agendaGroupPopped(AgendaGroupPoppedEvent event,<br> WorkingMemory workingMemory) {
<br> System.out.println( "popped AgendaGroup = '" + event.getAgendaGroup().getName() + "'" );<br> System.out.println( CellGrid.this.toString() );<br>
System.out.println( "" );<br> }<br> };<br><br> this.workingMemory.addEventListener( listener );<br><br> this.workingMemory.assertObject( this );<br><br> // populate the array of Cells and hook each
<br> // cell up with its neighbors...<br> for ( int row = 0; row < rows; row++ ) {<br> for ( int column = 0; column < columns; column++ ) {<br> final Cell newCell = new Cell( column,
<br> row );<br> this.cells[row][column] = newCell;<br> this.workingMemory.assertObject( newCell );<br> }<br> }<br> this.workingMemory.setFocus
( "register neighbor" );<br> this.workingMemory.fireAllRules();<br> }<br><br> /**<br> * @param row<br> * row of the requested cell<br> * @param column<br> * column of the requested cell
<br> * @return the cell at the specified coordinates<br> * @see Cell<br> */<br> public Cell getCellAt(final int row,<br> final int column) {<br> return this.cells[row][column];
<br> }<br><br> /**<br> * @return the number of rows in this grid<br> * @see #getNumberOfColumns()<br> */<br> public int getNumberOfRows() {<br> return this.cells.length;<br> }<br><br> /**
<br> * @return the number of columns in this grid<br> * @see #getNumberOfRows()<br> */<br> public int getNumberOfColumns() {<br> return this.cells[0].length;<br> }<br><br> /**<br> * Moves this grid to its next generation
<br> *<br> * @return <code>true</code> if the state changed, otherwise false<br> * @see #transitionState()<br> */<br> public boolean nextGeneration() {<br> System.out.println( "next generation" );
<br> workingMemory.setFocus( "calculate" );<br> workingMemory.setFocus( "kill" );<br> workingMemory.setFocus( "birth" );<br> workingMemory.setFocus( "reset calculate" );
<br> workingMemory.setFocus( "rest" );<br> workingMemory.setFocus( "evaluate" );<br> workingMemory.fireAllRules();<br> return workingMemory.getAgenda().getAgendaGroup( "evaluate" ).size() != 0;
<br> }<br><br> /**<br> * kills all cells in the grid<br> */<br> public void killAll() {<br> this.workingMemory.setFocus( "calculate" );<br> this.workingMemory.setFocus( "kill all" );
<br> this.workingMemory.setFocus( "reset calculate" );<br> this.workingMemory.fireAllRules();<br> }<br><br> /**<br> * Populates the grid with a <code>ConwayPattern</code><br> *
<br> * @param pattern<br> * pattern to populate the grid with<br> * @see ConwayPattern<br> */<br> public void setPattern(final ConwayPattern pattern) {<br> final boolean[][] gridData =
pattern.getPattern();<br> int gridWidth = gridData[0].length;<br> int gridHeight = gridData.length;<br><br> int columnOffset = 0;<br> int rowOffset = 0;<br><br> if ( gridWidth > getNumberOfColumns() ) {
<br> gridWidth = getNumberOfColumns();<br> } else {<br> columnOffset = (getNumberOfColumns() - gridWidth) / 2;<br> }<br><br> if ( gridHeight > getNumberOfRows() ) {<br> gridHeight = getNumberOfRows();
<br> } else {<br> rowOffset = (getNumberOfRows() - gridHeight) / 2;<br> }<br><br> killAll();<br><br> for ( int column = 0; column < gridWidth; column++ ) {<br> for ( int row = 0; row < gridHeight; row++ ) {
<br> if ( gridData[row][column] ) {<br> final Cell cell = getCellAt( row + rowOffset,<br> column + columnOffset );<br> cell.setCellState
( CellState.LIVE );<br> this.workingMemory.modifyObject( this.workingMemory.getFactHandle( cell ),<br> cell );<br> }<br> }<br>
}<br> workingMemory.setFocus( "calculate" );<br> workingMemory.fireAllRules();<br> System.out.println( "" );<br> }<br><br> public String toString() {<br> StringBuffer buf = new StringBuffer();
<br><br> for ( int i = 0; i < this.cells.length; i++ ) {<br> for ( int j = 0; j < this.cells[i].length; j++ ) {<br> Cell cell = this.cells[i][j];<br> System.out.print(
cell.getLiveNeighbors() + ((cell.getCellState() == CellState.DEAD) ? "D" : "L") + " " );<br> }<br> System.out.println( "" );<br> }<br><br> return
buf.toString();<br> }<br>}<br><br><br>package org.drools.examples.conway;<br><br>public class Neighbor {<br> private Cell cell;<br> private Cell neighbor;<br><br> public Neighbor(Cell cell, Cell neighbor) {<br>
this.cell = cell;<br> this.neighbor = neighbor;<br> }<br><br> public Cell getCell() {<br> return cell;<br> }<br><br> public Cell getNeighbor() {<br> return neighbor;<br> }<br><br>
public String toString() {<br> return "cell '"+ this.cell + "' neighbour '" + this.neighbor + "'";<br> }<br><br>}<br><br><br>_______________________________________________
<br>rules-users mailing list<br><a href="mailto:rules-users@lists.jboss.org">rules-users@lists.jboss.org</a><br><a href="https://lists.jboss.org/mailman/listinfo/rules-users">https://lists.jboss.org/mailman/listinfo/rules-users
</a><br><br></blockquote></div><br>