Repast Simphony——案例:僵尸感染人类
案例:僵尸感染人类
先放图
1. Repast Simphony
1.1 项目结构
修改项目为Repast Simphony视图
Window–>Perspective–>Open Perspective–>Other…–>Repast Simphony
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yj4p99D4-1619056046338)(image-20210421214919956.png)]
1.2 Projection
1.2.1 Continuous
描述空间,连续
ContinuousSpace<Object> space;
-
构建
ContinuousSpaceFactory spaceFactory = ContinuousSpaceFactoryFinder.createContinuousSpaceFactory(null); ContinuousSpace<Object> space = spaceFactory.createContinuousSpace("space", context, new RandomCartesianAdder<Object>(), new repast.simphony.space.continuous.WrapAroundBorders(), 50, 50);
-
某点位置
NdPoint pt = space.getLocation(obj);
-
位置移动
space.moveByVector(object,distance,angle); //例 space.moveByVector(this, 1, angle, 0);
space.moveTo(object,newLocation); //例 space.moveTo(zombie, spacePt.getX(), spacePt.getY());
1.2.2 Grid
描述空间,网格
Grid<Object> grid;
-
构建
GridFactory gridFactory = GridFactoryFinder.createGridFactory(null); Grid<Object> grid = gridFactory.createGrid("grid", context, new GridBuilderParameters<Object>( new repast.simphony.space.grid.WrapAroundBorders(), new SimpleGridAdder<Object>(), true, 50, 50));
-
某点位置
GridPoint pt = grid.getLocation(obj);
-
连续位置与网格位置相互转换
- 网格位置–>连续位置
NdPoint ptNd = new NdPoint(ptGrid.getX(), ptGrid.getY());
- 连续位置–>网格位置
GridPoint ptGrid = new GridPoint((int)ptNd.getX(), (int)ptNd.getY());
-
位置移动
grid.moveTo(obj,newLocation); //例 grid.moveTo(zombie, ptGridPoint.getX(), ptGridPoint.getY());
1.2.3 Network
描述网络
-
构建
NetworkBuilder<Object> netBuilder = new NetworkBuilder<Object>("infection network", context, true); netBuilder.buildNetwork();
-
添加边
Network<Object> net = (Network<Object>) context.getProjection("infection network"); net.addEdge(sourceObj, targetObj);
2. 构建模型
2.1 模型描述
僵尸
- 向人多的地方靠近
- 僵尸移动速度为1
- 僵尸接触到人会感染人
人
- 人向僵尸少的地方逃离
- 人移动速度为2,每次移动消耗1个能量
- 人的能量为0时,会在原地休息一个单位时间,能量恢复到最大
2.2 Agent
实体:僵尸、人
2.2.1 僵尸
-
属性
private ContinuousSpace<Object> space; //连续位置 private Grid<Object> grid; //网格位置 private boolean moved; //是否移动
-
构造函数
public Zombie(ContinuousSpace<Object> space, Grid<Object> grid) { this.space = space; this.grid = grid; };
-
成员函数
-
移动一步
public void step() { // 获取Zombie的网格位置 GridPoint pt = grid.getLocation(this); // 获取邻居 GridCellNgh<Human> nghCreator = new GridCellNgh<Human>(grid, pt, Human.class, 1, 1); List<GridCell<Human>> gridCells = nghCreator.getNeighborhood(true); SimUtilities.shuffle(gridCells, RandomHelper.getUniform()); GridPoint pointWithMostHumansGridPoint = null; int maxCount = -1; for (GridCell<Human> cell : gridCells) { if (cell.size() > maxCount) { pointWithMostHumansGridPoint = cell.getPoint(); maxCount = cell.size(); } } moveTowards(pointWithMostHumansGridPoint); infect(); }
-
位置移动
public void moveTowards(GridPoint pt) { if (!pt.equals(grid.getLocation(this))) { NdPoint myPoint = space.getLocation(this); NdPoint otherPoint = new NdPoint(pt.getX(), pt.getY());// 网格点转连续点 double angle = SpatialMath.calcAngleFor2DMovement(space, myPoint, otherPoint); space.moveByVector(this, 1, angle, 0);// 空间移动 myPoint = space.getLocation(this); grid.moveTo(this, (int) myPoint.getX(), (int) myPoint.getY());// 网格移动 moved = true; } }
-
感染人类
public void infect() { GridPoint ptGridPoint = grid.getLocation(this); List<Object> humans = new ArrayList<Object>(); for (Object obj : grid.getObjectsAt(ptGridPoint.getX(), ptGridPoint.getY())) { if (obj instanceof Human) { humans.add(obj); } } if (humans.size() > 0) { int index = RandomHelper.nextIntFromTo(0, humans.size() - 1); Object obj = humans.get(index); NdPoint spacePt = space.getLocation(obj); Context<Object> context = ContextUtils.getContext(obj); context.remove(obj); Zombie zombie = new Zombie(space, grid); context.add(zombie); space.moveTo(zombie, spacePt.getX(), spacePt.getY()); grid.moveTo(zombie, ptGridPoint.getX(), ptGridPoint.getY()); Network<Object> net = (Network<Object>) context.getProjection("infection network"); net.addEdge(this, zombie); } }
-
-
2.2.2 人
-
属性
private ContinuousSpace<Object> space; private Grid<Object> grid; private int energy, startingEnergy;
-
构造函数
public Human(ContinuousSpace<Object> space, Grid<Object> grid, int energy) { this.space = space; this.grid = grid; this.energy = startingEnergy = energy; }
-
成员函数
-
躲避僵尸
public void run() { GridPoint pt = grid.getLocation(this); GridCellNgh<Zombie> nghCreator = new GridCellNgh<Zombie>(grid, pt, Zombie.class, 1, 1); List<GridCell<Zombie>> gridCells = nghCreator.getNeighborhood(true); SimUtilities.shuffle(gridCells, RandomHelper.getUniform()); GridPoint pointWithLeastZombiesGridPoint = null; int minCount = Integer.MAX_VALUE; for (GridCell<Zombie> cell : gridCells) { if (cell.size() < minCount) { pointWithLeastZombiesGridPoint = cell.getPoint(); minCount = cell.size(); } } if (energy > 0) { moveTowards(pointWithLeastZombiesGridPoint); } else { energy = startingEnergy; } }
-
位置移动
public void moveTowards(GridPoint pt) { if (!pt.equals(grid.getLocation(this))) { NdPoint myPoint = space.getLocation(this); NdPoint otherPoint = new NdPoint(pt.getX(), pt.getY());// 网格点转连续点 double angle = SpatialMath.calcAngleFor2DMovement(space, myPoint, otherPoint); space.moveByVector(this, 2, angle, 0);// 空间移动 myPoint = space.getLocation(this); grid.moveTo(this, (int) myPoint.getX(), (int) myPoint.getY());// 网格移动 energy--; } }
-
-
2.2.3 为Agent添加动作触发条件
以给函数添加注解的方式添加触发条件
-
僵尸移动:每隔1个时间触发一次
@ScheduledMethod(start = 1, interval = 1)
-
人类逃跑:当附近一个格子(1个摩尔距离)内有僵尸出现时发生
@Watch(watcheeClassName = "jzombies.Zombie", watcheeFieldNames = "moved", query = "within_moore 1", whenToTrigger = WatcherTriggerSchedule.IMMEDIATE)
2.3 Builder
构建环境:实现接口ContextBuilder<>的build函数
-
设置id
context.setId("jzombies");
-
添加Projection
ContinuousSpaceFactory spaceFactory = ContinuousSpaceFactoryFinder.createContinuousSpaceFactory(null); ContinuousSpace<Object> space = spaceFactory.createContinuousSpace("space", context, new RandomCartesianAdder<Object>(), new repast.simphony.space.continuous.WrapAroundBorders(), 50, 50); GridFactory gridFactory = GridFactoryFinder.createGridFactory(null); Grid<Object> grid = gridFactory.createGrid("grid", context, new GridBuilderParameters<Object>( new repast.simphony.space.grid.WrapAroundBorders(), new SimpleGridAdder<Object>(), true, 50, 50)); NetworkBuilder<Object> netBuilder = new NetworkBuilder<Object>("infection network", context, true); netBuilder.buildNetwork();
-
添加Agent
Parameters params = RunEnvironment.getInstance().getParameters(); int zombieCount = params.getInteger("zombie_count"); for (int i = 0; i < zombieCount; i++) { context.add(new Zombie(space, grid)); } int humanCount = params.getInteger("human_count"); for (int i = 0; i < humanCount; i++) { int energy = RandomHelper.nextIntFromTo(4, 10); context.add(new Human(space, grid, energy)); } for (Object obj : context) { NdPoint pt = space.getLocation(obj); grid.moveTo(obj, (int) pt.getX(), (int) pt.getY()); }
2.4 context
在context.xml中注册projection便于GUI操作
<context id="jzombies"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://repast.org/scenario/context">
<projection type="continuous space" id="space"/>
<projection type="grid" id="grid"/>
<projection type="network" id="infection network"/>
</context>
2.5 完整代码
-
Zombie.java
/** * */ package jzombies; import java.util.ArrayList; import java.util.List; import repast.simphony.context.Context; import repast.simphony.engine.schedule.ScheduledMethod; import repast.simphony.query.space.grid.GridCell; import repast.simphony.query.space.grid.GridCellNgh; import repast.simphony.random.RandomHelper; import repast.simphony.space.SpatialMath; import repast.simphony.space.continuous.ContinuousSpace; import repast.simphony.space.continuous.NdPoint; import repast.simphony.space.graph.Network; import repast.simphony.space.grid.Grid; import repast.simphony.space.grid.GridPoint; import repast.simphony.util.ContextUtils; import repast.simphony.util.SimUtilities; /** * @author 60382 * */ public class Zombie { private ContinuousSpace<Object> space; private Grid<Object> grid; private boolean moved; public Zombie(ContinuousSpace<Object> space, Grid<Object> grid) { // TODO Auto-generated constructor stub this.space = space; this.grid = grid; }; @ScheduledMethod(start = 1, interval = 1) public void step() { // 获取Zombie的网格位置 GridPoint pt = grid.getLocation(this); // 获取邻居 GridCellNgh<Human> nghCreator = new GridCellNgh<Human>(grid, pt, Human.class, 1, 1); List<GridCell<Human>> gridCells = nghCreator.getNeighborhood(true); SimUtilities.shuffle(gridCells, RandomHelper.getUniform()); GridPoint pointWithMostHumansGridPoint = null; int maxCount = -1; for (GridCell<Human> cell : gridCells) { if (cell.size() > maxCount) { pointWithMostHumansGridPoint = cell.getPoint(); maxCount = cell.size(); } } moveTowards(pointWithMostHumansGridPoint); infect(); } public void moveTowards(GridPoint pt) { if (!pt.equals(grid.getLocation(this))) { NdPoint myPoint = space.getLocation(this); NdPoint otherPoint = new NdPoint(pt.getX(), pt.getY());// 网格点转连续点 double angle = SpatialMath.calcAngleFor2DMovement(space, myPoint, otherPoint); space.moveByVector(this, 1, angle, 0);// 空间移动 myPoint = space.getLocation(this); grid.moveTo(this, (int) myPoint.getX(), (int) myPoint.getY());// 网格移动 moved = true; } } public void infect() { GridPoint ptGridPoint = grid.getLocation(this); List<Object> humans = new ArrayList<Object>(); for (Object obj : grid.getObjectsAt(ptGridPoint.getX(), ptGridPoint.getY())) { if (obj instanceof Human) { humans.add(obj); } } if (humans.size() > 0) { int index = RandomHelper.nextIntFromTo(0, humans.size() - 1); Object obj = humans.get(index); NdPoint spacePt = space.getLocation(obj); Context<Object> context = ContextUtils.getContext(obj); context.remove(obj); Zombie zombie = new Zombie(space, grid); context.add(zombie); space.moveTo(zombie, spacePt.getX(), spacePt.getY()); grid.moveTo(zombie, ptGridPoint.getX(), ptGridPoint.getY()); Network<Object> net = (Network<Object>) context.getProjection("infection network"); net.addEdge(this, zombie); } } }
-
Human.java
/** * */ package jzombies; import java.util.List; import repast.simphony.engine.watcher.Watch; import repast.simphony.engine.watcher.WatcherTriggerSchedule; import repast.simphony.query.space.grid.GridCell; import repast.simphony.query.space.grid.GridCellNgh; import repast.simphony.random.RandomHelper; import repast.simphony.relogo.ide.dynamics.NetLogoSystemDynamicsParser.intg_return; import repast.simphony.space.SpatialMath; import repast.simphony.space.continuous.ContinuousSpace; import repast.simphony.space.continuous.NdPoint; import repast.simphony.space.grid.Grid; import repast.simphony.space.grid.GridPoint; import repast.simphony.util.SimUtilities; /** * @author 60382 * */ public class Human { private ContinuousSpace<Object> space; private Grid<Object> grid; private int energy, startingEnergy; public Human(ContinuousSpace<Object> space, Grid<Object> grid, int energy) { // TODO Auto-generated constructor stub this.space = space; this.grid = grid; this.energy = startingEnergy = energy; } @Watch(watcheeClassName = "jzombies.Zombie", watcheeFieldNames = "moved", query = "within_moore 1", whenToTrigger = WatcherTriggerSchedule.IMMEDIATE) public void run() { GridPoint pt = grid.getLocation(this); GridCellNgh<Zombie> nghCreator = new GridCellNgh<Zombie>(grid, pt, Zombie.class, 1, 1); List<GridCell<Zombie>> gridCells = nghCreator.getNeighborhood(true); SimUtilities.shuffle(gridCells, RandomHelper.getUniform()); GridPoint pointWithLeastZombiesGridPoint = null; int minCount = Integer.MAX_VALUE; for (GridCell<Zombie> cell : gridCells) { if (cell.size() < minCount) { pointWithLeastZombiesGridPoint = cell.getPoint(); minCount = cell.size(); } } if (energy > 0) { moveTowards(pointWithLeastZombiesGridPoint); } else { energy = startingEnergy; } } public void moveTowards(GridPoint pt) { if (!pt.equals(grid.getLocation(this))) { NdPoint myPoint = space.getLocation(this); NdPoint otherPoint = new NdPoint(pt.getX(), pt.getY());// 网格点转连续点 double angle = SpatialMath.calcAngleFor2DMovement(space, myPoint, otherPoint); space.moveByVector(this, 2, angle, 0);// 空间移动 myPoint = space.getLocation(this); grid.moveTo(this, (int) myPoint.getX(), (int) myPoint.getY());// 网格移动 energy--; } } }
-
JZombiesBuilder.java
package jzombies; import repast.simphony.context.Context; import repast.simphony.context.space.continuous.ContinuousSpaceFactory; import repast.simphony.context.space.continuous.ContinuousSpaceFactoryFinder; import repast.simphony.context.space.graph.NetworkBuilder; import repast.simphony.context.space.grid.GridFactory; import repast.simphony.context.space.grid.GridFactoryFinder; import repast.simphony.dataLoader.ContextBuilder; import repast.simphony.engine.environment.RunEnvironment; import repast.simphony.parameter.Parameters; import repast.simphony.random.RandomHelper; import repast.simphony.space.continuous.ContinuousSpace; import repast.simphony.space.continuous.NdPoint; import repast.simphony.space.continuous.RandomCartesianAdder; import repast.simphony.space.grid.Grid; import repast.simphony.space.grid.GridBuilderParameters; import repast.simphony.space.grid.SimpleGridAdder; public class JZombiesBuilder implements ContextBuilder<Object> { @Override public Context build(Context<Object> context) { // TODO Auto-generated method stub context.setId("jzombies"); ContinuousSpaceFactory spaceFactory = ContinuousSpaceFactoryFinder.createContinuousSpaceFactory(null); ContinuousSpace<Object> space = spaceFactory.createContinuousSpace("space", context, new RandomCartesianAdder<Object>(), new repast.simphony.space.continuous.WrapAroundBorders(), 50, 50); GridFactory gridFactory = GridFactoryFinder.createGridFactory(null); Grid<Object> grid = gridFactory.createGrid("grid", context, new GridBuilderParameters<Object>( new repast.simphony.space.grid.WrapAroundBorders(), new SimpleGridAdder<Object>(), true, 50, 50)); NetworkBuilder<Object> netBuilder = new NetworkBuilder<Object>("infection network", context, true); netBuilder.buildNetwork(); Parameters params = RunEnvironment.getInstance().getParameters(); int zombieCount = params.getInteger("zombie_count"); for (int i = 0; i < zombieCount; i++) { context.add(new Zombie(space, grid)); } int humanCount = params.getInteger("human_count"); for (int i = 0; i < humanCount; i++) { int energy = RandomHelper.nextIntFromTo(4, 10); context.add(new Human(space, grid, energy)); } for (Object obj : context) { NdPoint pt = space.getLocation(obj); grid.moveTo(obj, (int) pt.getX(), (int) pt.getY()); } return context; } }
-
context.xml
<context id="jzombies" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://repast.org/scenario/context"> <projection type="continuous space" id="space"/> <projection type="grid" id="grid"/> <projection type="network" id="infection network"/> </context>
3. 模型运行
3.1 Data Loaders
添加数据加载器
-
右键Data Loaders
-
Set Data Loader
-
Custom ContextBuilder Implementation
-
选择实现ContextBuilder<>接口所建立的类JZombiesBuilder
3.2 Displays
- 右键Displays
- Add Display
- 定义名字、2D/3D图形、选择Projection(Continuous和Grid二选一)
- 选择Agent,Zombie在上面(Human的动作为被动触发的)
- 选择Agent样式
- Continuous或Grid样式
- Network样式
- 时间
3.3 Data Sets
添加需要记录的数据,之后可以用图表显示或输出到文件
-
Aggregate数据
-
Non-Aggregate数据
Add Data Set选择要记录哪些数据
3.3.1 Charts
添加图表显示数据
- Histogram Chart
- Time Series Chart
3.3.2 Text Sinks
输出数据
-
Consloe Sink
-
File Sink
3.4 Run Options
运行时间、速度设置
-
设置暂停时间
-
设置结束时间(0表示无限)
-
设置演示速度
3.5 Parameters
添加GUI参数
在代码中可以通过Parameters类根据id解析
Parameters params = RunEnvironment.getInstance().getParameters();
int zombieCount = params.getInteger("zombie_count");
int humanCount = params.getInteger("human_count");
3.6 模型打包
Build Installer for Model
可打包成jar
4. 参考
Repast Simphony DownLoads: https://repast.github.io/download.html
Repast Java Getting Started: https://repast.github.io/docs/RepastJavaGettingStarted.pdf