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 <[EMAIL PROTECTED]> 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:[EMAIL PROTECTED]">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:[EMAIL PROTECTED]">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
_______________________________________________ rules-users mailing list rules-users@lists.jboss.org https://lists.jboss.org/mailman/listinfo/rules-users