Feel free to join the next Helmholtz Hacky Hour #26 on Wednesday, April 21, 2021 from 2PM to 3PM!

Commit fb03999f authored by Martin Lange's avatar Martin Lange

code simplifications, growth formula, minor rewordings

parent 66dbe4a5
Pipeline #12171 passed with stage
in 12 seconds
......@@ -53,7 +53,7 @@ Besides benefits for performance and maintainability, ECS offers great flexibili
## Model purpose
The purpose of this model is to demonstrate the use of Entity-Component-Systems for individual-based models.
The purpose of this model is to demonstrate the use of the ECS approach for individual-based models.
## Entities, state variables and scales
......@@ -135,7 +135,7 @@ public class IsSearching extends com.artemis.Component {}
**Resources**
> Resources are "things" that exist only once in the model, contrary to entities, and are potentially available for all submodels. Any instance of a Java class can become a resource. However, only one instance of each class is possible.
> Resources are objects that exist only once in the model, contrary to entities, and are potentially available for all submodels. When using the Artemis-odb, any instance of a Java class can become a resource. However, only one instance of each class is possible.
Grassers live in a two-dimensional world. The world is represented by a grid of grass that grassers can consume.
......@@ -201,16 +201,16 @@ public class Main {
// ==> Parameters.
public static void main(String[] args) {
public static void main(String[] args) throws InterruptedException {
var setup = new WorldConfigurationBuilder()
.with(new LogisticGrassGrowth(GRASS_GROWTH_RATE))
.with(new Metabolism(ENERGY_CONSUMPTION))
.with(new Reproduction(REPRODUCTION_PROB))
.with(new GrassingBehaviour(GRASS_START_SEARCHING, GRASS_CONSUMPTION))
.with(new SearchBehaviour(GRASS_START_GRASSING, RANDOM_WALK_ANGLE, RANDOM_WALK_SPEED))
.with(new Graphics(6))
.with(new Graphics())
.build()
.register(new Randomness(System.currentTimeMillis()))
.register(new Randomness(0))
.register(new Grass(WORLD_WIDTH, WORLD_HEIGHT, 0.8f));
var world = new World(setup);
......@@ -219,9 +219,7 @@ public class Main {
for (int i=0; i < 1000000; i++) {
world.process();
try {
Thread.sleep(10);
} catch(InterruptedException ignored) {}
Thread.sleep(10);
}
}
......@@ -263,7 +261,13 @@ Submodels are described in their order of execution.
### Grass growth
Grass growth is logistic, with a capacity of 1.0.
Grass growth is logistic. The amount of grass is updated according
```math
p_{t+1} = p_t + r p_t \frac{k - p_t}{k}
```
where $`p_t`$ is the amount in the current step and $`p_{t+1}`$ in the next step. $`r`$ is the growth rate per step and $`k`$ is the capacity. Capacity is always 1.0.
> Since the grassing system does not use any entities, we simply extend `BaseSystem`. However, it requires access to the `Grass` grid, which is "injected" by the ECS through the line `@Wire Grass grass;`.
>
......@@ -321,7 +325,7 @@ import grassing.comp.Energy;
@All(Energy.class)
public class Metabolism extends IteratingSystem {
ComponentMapper<Energy> mEnergy;
ComponentMapper<Energy> energy;
private final float consumption;
......@@ -331,7 +335,7 @@ public class Metabolism extends IteratingSystem {
@Override
protected void process(int id) {
Energy e = mEnergy.get(id);
Energy e = energy.get(id);
e.value -= consumption;
if(e.value <= 0) {
world.delete(id);
......@@ -366,8 +370,8 @@ import grassing.res.Randomness;
@All({Position.class, Energy.class})
public class Reproduction extends IteratingSystem {
ComponentMapper<Position> mPosition;
ComponentMapper<Energy> mEnergy;
ComponentMapper<Position> position;
ComponentMapper<Energy> energy;
@Wire Randomness random;
......@@ -380,8 +384,8 @@ public class Reproduction extends IteratingSystem {
@Override
protected void process(int id) {
if(random.rng.nextFloat() < reproductionProb) {
var pos = mPosition.get(id);
var e = mEnergy.get(id);
var pos = position.get(id);
var e = energy.get(id);
int entity = world.create();
world.edit(entity)
.add(new Position(pos.x, pos.y))
......@@ -418,10 +422,10 @@ import grassing.res.Grass;
@All({Position.class, Energy.class, IsGrassing.class})
public class GrassingBehaviour extends IteratingSystem {
ComponentMapper<Position> mPosition;
ComponentMapper<Energy> mEnergy;
ComponentMapper<IsGrassing> mGrassing;
ComponentMapper<IsSearching> mSearching;
ComponentMapper<Position> position;
ComponentMapper<Energy> energy;
ComponentMapper<IsGrassing> grassing;
ComponentMapper<IsSearching> searching;
@Wire Grass grass;
......@@ -435,18 +439,18 @@ public class GrassingBehaviour extends IteratingSystem {
@Override
protected void process(int id) {
Energy e = mEnergy.get(id);
Energy e = energy.get(id);
if(e.value >= 1) {
return;
}
Position pos = mPosition.get(id);
Position pos = position.get(id);
float g = grass.grass.get((int) pos.x, (int) pos.y);
if( g - consumption > minGrass ) {
e.value += consumption;
grass.grass.set((int) pos.x, (int) pos.y, g - consumption);
} else {
mGrassing.remove(id);
mSearching.create(id);
grassing.remove(id);
searching.create(id);
}
}
}
......@@ -476,10 +480,10 @@ import grassing.util.MathUtil;
@All({Position.class, Heading.class, IsSearching.class})
public class SearchBehaviour extends IteratingSystem {
ComponentMapper<Position> mPosition;
ComponentMapper<Heading> mHeading;
ComponentMapper<IsGrassing> mGrassing;
ComponentMapper<IsSearching> mSearching;
ComponentMapper<Position> position;
ComponentMapper<Heading> heading;
ComponentMapper<IsGrassing> grassing;
ComponentMapper<IsSearching> searching;
@Wire Grass grass;
@Wire Randomness random;
......@@ -496,29 +500,29 @@ public class SearchBehaviour extends IteratingSystem {
@Override
protected void process(int id) {
Position pos = mPosition.get(id);
Position pos = position.get(id);
float g = grass.grass.get((int) pos.x, (int) pos.y);
if( g >= minGrass ) {
mSearching.remove(id);
mGrassing.create(id);
searching.remove(id);
grassing.create(id);
} else {
randomWalk(id);
}
}
private void randomWalk(int id) {
var pos = mPosition.get(id);
var heading = mHeading.get(id);
heading.angle += (float) random.rng.nextGaussian() * maxAngle;
var pos = position.get(id);
var head = heading.get(id);
head.angle += (float) random.rng.nextGaussian() * maxAngle;
var head = MathUtil.heading(heading.angle);
float xNew = pos.x + head.x * speed;
float yNew = pos.y + head.y * speed;
var h = MathUtil.heading(head.angle);
float xNew = pos.x + h.x * speed;
float yNew = pos.y + h.y * speed;
if(grass.grass.contains((int) xNew, (int) yNew)) {
pos.x = xNew;
pos.y = yNew;
} else {
heading.angle = (heading.angle + (float) Math.PI) % (2 * (float) Math.PI);
head.angle = (head.angle + (float) Math.PI) % (2 * (float) Math.PI);
}
}
}
......@@ -566,7 +570,7 @@ Grassers have a reproduction probability of 5% per step.
final static float REPRODUCTION_PROB = 0.05f;
```
Grassers walk with 0.25 units per step, and turn with values from a normal distribution with a standard deviation of 30掳.
Grassers walk with 0.25 units per step, and turn with values from a normal distribution with a mean of 0掳 and a standard deviation of 30掳.
```java
/// Parameters
......
......@@ -35,19 +35,19 @@ import java.awt.*;
@One({IsGrassing.class, IsSearching.class})
public class Graphics extends BaseEntitySystem {
protected ComponentMapper<Position> mPosition;
protected ComponentMapper<Heading> mHeading;
protected ComponentMapper<IsGrassing> mGrassing;
protected ComponentMapper<IsSearching> mSearching;
protected ComponentMapper<Position> position;
protected ComponentMapper<Heading> heading;
protected ComponentMapper<IsGrassing> grassing;
protected ComponentMapper<IsSearching> searching;
@Wire Grass grass;
final private int cellSize;
private JPanel canvas;
public Graphics(int cellSize) {
final private int CELL_SIZE = 6;
public Graphics() {
super();
this.cellSize = cellSize;
}
@Override
......@@ -62,7 +62,7 @@ public class Graphics extends BaseEntitySystem {
}
};
var dim = new Dimension(grass.grass.width * cellSize, grass.grass.height * cellSize);
var dim = new Dimension(grass.grass.width * CELL_SIZE, grass.grass.height * CELL_SIZE);
canvas.setPreferredSize( dim );
frame.add(canvas);
......@@ -73,12 +73,12 @@ public class Graphics extends BaseEntitySystem {
@Override
protected void processSystem() {
canvas.paintImmediately(0, 0, grass.grass.width * cellSize, grass.grass.height * cellSize);
canvas.paintImmediately(0, 0, grass.grass.width * CELL_SIZE, grass.grass.height * CELL_SIZE);
}
void paint(Graphics2D g2d) {
FloatGrid grass = this.grass.grass;
int s = cellSize;
int s = CELL_SIZE;
g2d.setBackground(Color.BLACK);
......@@ -94,11 +94,11 @@ public class Graphics extends BaseEntitySystem {
IntBag grassers = subscription.getEntities();
for (int i = 0; i < grassers.size(); i++) {
int entity = grassers.get(i);
var pos = mPosition.get(entity);
var head = mHeading.get(entity);
if(mGrassing.has(entity)) {
var pos = position.get(entity);
var head = heading.get(entity);
if(grassing.has(entity)) {
g2d.setPaint(Color.WHITE);
} else if(mSearching.has(entity)) {
} else if(searching.has(entity)) {
g2d.setPaint(Color.ORANGE);
} else {
g2d.setPaint(Color.MAGENTA);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment