- /*
- Conway: 元细胞自动机模型,俗称生命游戏
- 它有些类似于一张围棋棋盘,但是可以更大。每个棋盘上的格子(元细胞)只能是生/死两种状态之一。
- 游戏遵循下面的规则
- 1)格子的状态只能是生/死两种状态之一
- 2)格子以其上下左右及斜角方向的8个格子为邻居
- 3)一个格子的生死由其在该时刻本身的生死状态和周围八个邻居的状态 (确切讲是状态的和)决定:
- a、在当前时刻,如果一个格子状态为"生",且八个相邻格子中有两个或三个的状态为"生",
- 则在下--时刻该格子继续保持为"生",否则"死"去;
- b、在当前时刻。如果一个格子状态为"死"。且八个相邻格子中正好有三个为"生"。
- 则该格子在下一时刻 "复活"。否则保持为"死"。
- 元细胞自动机模型已在多方面得到应用。它的演化规则近似地描述了生物群体的生存繁殖规律:
- 在生命密度过小(相邻元胞数<2) 时,由于孤单、缺乏配种繁殖机会、缺乏互助也会出现生命危机,元胞状态值由1变为0;
- 在生命密度过大 (相邻元胞数>3)时,由于环境恶化、资源短缺以及相互竞争而出现生存危机,元胞状态值由1变为0;
- 只有处于个体适中(相邻元胞数为2或3)位置的生物才能生存(保持元胞的状态值为1)和繁衍后代(元胞状态值由0变为1)。
- 正由于它能够模拟生命活动中的生存、灭绝、竞争等等复杂现象,因而得名"生命游戏"。
- J·H·Conway还证明,这个元胞自动机具有通用图灵机的计算能力(谢惠民,1994;李才伟,1997),
- 与图灵机等价,也就是说给定适当的初始条件,生命游戏模型能够模拟任何一种计算机。
- 本示例设计到界面显示,用到的类比较多,这里简单说明一下每个类的用途,不再详细列举代码
- AbstractRunConway.java 建立GUI界面
- ConwayGUI.java 在这里定义好了界面,以及按钮相关事件
- CellGridCanvas.java 绘制界面
- ConwayRuleFlowGroupRun.java 继承AbstractRunConway,使用规则流模式启动程序
- ConwayAgendaGroupRun.java 继承AbstractRunConway,使用Agenda组模式启动程序,之前版本不推荐
- ConwayApplicationProperties.java 用于从conway.properties中读取预定义的属性
- ConwayRuleDelegate.java 作为Agenda和规则流两种模式的统一接口
- *RuleFlowDelegate.java 规则流模式行为响应代理,界面按钮的动作由该类转发给规则引擎,引起WorkingMemory中的变化
- 使用规则流控制规则执行时,针对应用的不同操作共使用了4组规则流如下:
- 初始化init - 规则流"register neighbor"
- 产生下一步的变化nextGeneration - 规则流"generation",连续变化通过重复调用该步骤进行
- 清除killAll - 规则流"kill all"
- 模式设置setPattern - 规则流"calculate"
- AgendaGroupDelegate.java Agenda模式行为响应代理,作用同上
- Cell.java 代表一个格子,有行列属性和状态属性
- CellGrid.java 控制表格的抽象接口
- *CellGridImpl.java 表格接口的实现,用于向规则引擎中插入数据,以及调用响应代理
- 数据的插入分为两种情况
- 1、初始化棋盘
- 将代表棋盘的CellGrid实例插入
- 将棋盘上的每一个格子产生一个Cell对象插入
- 2、设置初始模式
- 根据提供的模式数组长宽向棋盘中间对齐,对于True的数组数据
- 修改对应格子的Cell状态为Live
- CellState.java 格子状态(生/死)
- Phase.java 在规则中用于控制格子处理的阶段,间接影响格子的状态
- Neighbor.java 用于保存两个格子之间的邻居关系
- ConwayPattern.java 模型初始化数据读取接口
- Border.java 一种模型初始格式,实现ConwayPattern接口
- Hi.java 同上
- Pentadecathalon.java 同上
- Pulsar.java 同上
- SimpleGlider.java 同上
- 思路:
- 从元细胞自动机模型的规则中我们可以发现几个关键要点,
- 1、一个格子的状态由相邻格子的状态决定;因此在规则处理中我们要能够方便的确定每个格子的相邻格子情况
- 2、格子的状态变化是一次性完成计算的,格子状态的改变不会对本次变化产生影响;因此在规则处理时要注意
- 解决fact变化激活新的规则的问题
- 作者将所有规则按照规则流分组的方式分为下面几个组
- "register north east" - 建立格子间的邻居关系(再次提醒,规则引擎推理基于数据,应当将所有需要的数据事先准备好
- 数据模型的好坏直接影响规则的效率和质量)
- "evaluate" - 根据格子邻居的情况判定格子下一步的状态,并预设一个值;这里之所以没有直接改变格子
- 状态就是因为上面提到的一次性计算问题,如果直接改变状态会影响对其它邻居格子的判断
- Phase:只对Phase==EVALUATE的格子执行
- "calculate" - 遍历每一个格子,根据该格子的生存/死亡状态更新邻居格子的‘邻居’生存个数
- 这里留意的是,按照正常的编程思路,应当是找到格子后获得所有邻居状态,然后计算有多少
- 个邻居格子是生存状态的,然后更新本格子中的LiveNeighbors计数;但是要采用这样的
- 思路,必然涉及到对集合的处理以及一些循环语句,这样增加了规则的复杂度以及引入了过程
- 代码,这都是在规则中应当避免的问题;而换一个角度思考我们就会得到更为简单的规则。
- Phase:规则完成后将格子的Phase设为EVALUATE
- 注:使用规则引擎不只是掌握一门新技术,而是要求在编程思维上的改变。
- "reset calculate" - 取消"calculate"组激活的规则,因为每次格子重新设置pattern时,盘面上还有生存的格子
- 这时如果使用kill all规则来将格子设为死亡,如果之前的calcuate规则还起作用,那么就会
- 引起calcuate规则执行,建立新的生存格子,反过来又会引起kill all规则执行,造成无限循环
- "kill" - 将Phase为KILL的格子状态设为死亡
- "birth" - 将Phase为BIRTH的格子状态设为生存
- "kill all" - 将所有格子的状态设为死亡,是用在初始化棋盘时
- */
- 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;
- /*
- 下面的四条规则都属于ruleflow-group:"register neighbor"
- 它们的作用是建立格子之间彼此的邻居关系。
- 我们知道每个格子会有8个相邻的格子,而规则建立关系时都是成对建立的,
- 因此只用四条规则即可完成所有邻居关系建立
- */
- # 建立格子与其右上角格子的邻居关系
- rule "register north east"
- ruleflow-group "register neighbor"
- when
- # 获得棋盘列数
- CellGrid( $numberOfColumns : numberOfColumns )
- # 获得棋盘内的每个格子,最右边除外,因为最右边没有右上角
- $cell: Cell( $row : row > 0, $col : col < ( $numberOfColumns - 1 ) )
- # 获得上面格子的东北角格子
- $northEast : Cell( row == ($row - 1), col == ( $col + 1 ) )
- then
- # 为这两个格子建立邻居关系
- insert( new Neighbor( $cell, $northEast ) );
- insert( new Neighbor( $northEast, $cell ) );
- end
- # 建立格子与其正上方格子的邻居关系
- rule "register north"
- ruleflow-group "register neighbor"
- when
- $cell: Cell( $row : row > 0, $col : col )
- $north : Cell( row == ($row - 1), col == $col )
- then
- insert( new Neighbor( $cell, $north ) );
- insert( new Neighbor( $north, $cell ) );
- end
- # 建立格子与其左上角格子的邻居关系
- rule "register north west"
- ruleflow-group "register neighbor"
- when
- $cell: Cell( $row : row > 0, $col : col > 0 )
- $northWest : Cell( row == ($row - 1), col == ( $col - 1 ) )
- then
- insert( new Neighbor( $cell, $northWest ) );
- insert( new Neighbor( $northWest, $cell ) );
- end
- # 建立格子与其左边格子的邻居关系
- rule "register west"
- ruleflow-group "register neighbor"
- when
- $cell: Cell( $row : row >= 0, $col : col > 0 )
- $west : Cell( row == $row, col == ( $col - 1 ) )
- then
- insert( new Neighbor( $cell, $west ) );
- insert( new Neighbor( $west, $cell ) );
- end
- /*
- 下面的三条规则都属于ruleflow-group:"evaluate"
- 它们是用在进行下一步的变化评估中的,这3个规则中并没有直接改变Cell.CellState,
- 因为CellState是用来进行评估用的,随意改变会造成无限的循环调用,因此规则使用了Phase字段来控制格子状态变化
- 在评估中只是改变Phase,最后根据Phase的状态完成所有格子CellState的设置,并将Phase设回EVALUATE状态
- */
- # 将格子的邻居中少于两个是生存状态的格子的状态设为死
- rule "Kill The Lonely"
- ruleflow-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);
- update( theCell );
- end
- # 将格子的邻居中超过3个状态是生存的格子状态设为死
- rule "Kill The Overcrowded"
- ruleflow-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);
- update( theCell );
- end
- # 将格子的邻居中正好有3个是生存状态的死亡格子变为生
- rule "Give Birth"
- ruleflow-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);
- update( theCell );
- end
- # 取消ruleflow-group为"calculate"的所有激活规则
- # clearRuleFlowGroup - Clears the RuleFlow group, cancelling all its Activations
- # 因为在"generation"后,"calculate"组的规则还留在引擎中,如果不事先取消,就会引起无限循环
- rule "reset calculate"
- ruleflow-group "reset calculate"
- when
- then
- WorkingMemory wm = drools.getWorkingMemory();
- wm.clearRuleFlowGroup( "calculate" );
- end
- # 将所有格子的Phase为Kill的格子状态设置为死,并将处理阶段Phase设置为DONE
- rule "kill"
- ruleflow-group "kill"
- no-loop
- when
- theCell: Cell(phase == Phase.KILL)
- then
- theCell.setCellState(CellState.DEAD);
- theCell.setPhase(Phase.DONE);
- update( theCell );
- end
- # 将所有格子的Phase为Birth的格子状态设置为生,并将处理阶段Phase设置为完成
- rule "birth"
- ruleflow-group "birth"
- no-loop
- when
- theCell: Cell(phase == Phase.BIRTH)
- then
- theCell.setCellState(CellState.LIVE);
- theCell.setPhase(Phase.DONE);
- update( theCell );
- end
- # 根据格子的生存状态改变邻居格子中LiveNeighbors属性的计数
- rule "Calculate Live"
- ruleflow-group "calculate"
- lock-on-active # 本规则更新的数据在规则流处理完成前不激活新的规则
- when
- # 获得状态为生存的格子
- theCell: Cell(cellState == CellState.LIVE)
- # 找到该格子的每一个邻居
- Neighbor(cell == theCell, $neighbor : neighbor)
- then
- # 为这个格子的每一个邻居的LiveNeighbors属性加1
- $neighbor.setLiveNeighbors( $neighbor.getLiveNeighbors() + 1 );
- # 将邻居格子的处理阶段Phase设置为EVALUATE
- $neighbor.setPhase( Phase.EVALUATE );
- update( $neighbor );
- System.out.println( "--live--" );
- System.out.println( "theCell: row"+theCell.getRow()+"col"+theCell.getCol());
- System.out.println ( "Neighbor: row:"+$neighbor.getRow()+"col"+$neighbor.getCol()+";LiveNeighbors:"+ $neighbor.getLiveNeighbors()) ;
- end
- # 类似上一规则,只是进行递减操作
- # 对于这个规则,不太熟悉规则引擎的程序员可能会有所迷惑,所有的格子初始化状态都是DEAD
- # 那这样的话很多格子的LiveNeighbors就会变成负数了,有这样的想法是因为忽略了规则引擎不是从数据里找到规则合适的地方
- # 而是在数据插入或发生变化时找到规则,一开始初始化所有格子为DEAD时确实激活了"Calculate Dead"的很多实例,但是在
- # setPattern时首先执行了"reset calculate",这取消了之前的所有"Calculate Dead"的激活实例
- # 然后运行"kill all"规则,如果是第一次,该规则不改变任何数据也不会激发新的规则
- # 然后是根据数据设置格子的生存状态,此时上面的"Calculate Live"规则被激活
- # 在后面的规则运算中每次执行"evalaute"规则组都要运行"reset calculate"也是这个原因
- # 然后在运行了"birth"和"kill"规则后才执行"Calculate Live"和"Calculate Dead"规则
- rule "Calculate Dead"
- ruleflow-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 );
- update( $neighbor );
- System.out.println( "--dead--" );
- System.out.println( "theCell: row"+theCell.getRow()+"col"+theCell.getCol());
- System.out.println ( "Neighbor: row:"+$neighbor.getRow()+"col"+$neighbor.getCol()+";LiveNeighbors:"+ $neighbor.getLiveNeighbors()) ;
- end
- # 将所有生存状态的格子设为死亡
- rule "Kill All"
- ruleflow-group "kill all"
- no-loop
- when
- theCell: Cell(cellState == CellState.LIVE)
- then
- theCell.setCellState(CellState.DEAD);
- update( theCell );
- end