[rules-users] conways game of life
Michael Neale
michael.neale at gmail.com
Tue Apr 17 01:57:46 EDT 2007
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 at 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 at 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 at 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 at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/rules-users
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/rules-users/attachments/20070417/8ba56f3c/attachment.html
More information about the rules-users
mailing list