欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Repast Simphony——案例:僵尸感染人类

程序员文章站 2024-03-02 19:32:04
...

案例:僵尸感染人类

先放图
Repast Simphony——案例:僵尸感染人类
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
  3. 僵尸接触到人会感染人

  1. 人向僵尸少的地方逃离
  2. 人移动速度为2,每次移动消耗1个能量
  3. 人的能量为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

添加数据加载器

  1. 右键Data Loaders

  2. Set Data Loader

  3. Custom ContextBuilder Implementation

  4. 选择实现ContextBuilder<>接口所建立的类JZombiesBuilder

3.2 Displays

  1. 右键Displays
  2. Add Display
  3. 定义名字、2D/3D图形、选择Projection(Continuous和Grid二选一)
  4. 选择Agent,Zombie在上面(Human的动作为被动触发的)
  5. 选择Agent样式
  6. Continuous或Grid样式
  7. Network样式
  8. 时间

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