Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ we-get-these-100s.iml
latex/main.aux
latex/main.log
.DS_Store
main.out
.env
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# WE GET THESE 100S

This simulation project integrates multiple components to create a dynamic ecosystem that a researcher can use to
study predator-prey-plant interactions, with the ability to add or remove any species or environmental factors easily
by editing the given JSON file. Infact, almost every aspect of the simulation can be controlled in the JSON file.
The simulation is designed to simulate without any grid-based restrictions, allowing entities to move freely in a
continuous space. Specifically, the coordinates of the entities are stored as doubles from zero to the width and height
of the field. The entities also have their own genetics, which are inherited from parent(s) and may mutate,
all of which are initialised in the JSON file, where it is intended for all users of the program to modify it
to play with the simulation parameters.

Authors: Mehmet Kutay Bozkurt and Anas Ahmed
Version: 1.0

---

[Google Docs](https://docs.google.com/document/d/14-HuoK5tUVpEVj2pL6A5acK-5UiRciUfZDvJvdfa1uY/edit)

To open the GUI, add port 6080 under the "Ports" section and open it in the browser.
Expand Down
Binary file modified latex/main.pdf
Binary file not shown.
395 changes: 281 additions & 114 deletions latex/main.tex

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion src/Main.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import view.Engine;

/**
* Main class to start the simulation through the Engine class.
*
* @author Mehmet Kutay Bozkurt and Anas Ahmed
* @version 1.0
*/
public class Main {
public static void main(String[] args) {
int fps = 60;
Engine engine = new Engine(600, 600, fps);
int width = 600;
int height = 600;
Engine engine = new Engine(width, height, fps);
engine.start();
}
}
25 changes: 15 additions & 10 deletions src/entities/Plant.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package entities;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

Expand All @@ -11,7 +12,8 @@
import util.Vector;

/**
* A class that holds the properties of a plant entity.
* A class that holds the properties of a plant entity. Plants can multiply and
* spread their seeds (basically new plants).
*
* @author Anas Ahmed and Mehmet Kutay Bozkurt
* @version 1.0
Expand All @@ -28,8 +30,8 @@ public Plant(PlantGenetics genetics, Vector position) {
}

/**
* Spawn new plants around this plant. The new plants have the same
* genetics as the parent plant (may be mutated).
* Spawn new plants around this plant. The new plants have the same genetics
* as the parent plant, though it may be mutated.
* @return A list of new plants if the plant can multiply, empty list otherwise.
*/
public List<Plant> multiply(Field field) {
Expand All @@ -39,38 +41,40 @@ public List<Plant> multiply(Field field) {

if (!(canMultiply() && Math.random() < multiplyingRate)) return Collections.emptyList();

if (!field.environment.isDay()){ // If it's night, very low odds of multiplying
if (!field.environment.isDay()) { // If it's night, very low odds of multiplying.
if (Math.random() > 0.3) {
return Collections.emptyList();
}
}

// Growth factor affects number of seeds and range of growth
// Growth factor affects number of seeds and range of growth:
int seeds = (int) (genetics.getNumberOfSeeds() * growthFactor);
Plant[] newPlants = new Plant[seeds];
List<Plant> newPlants = new ArrayList<>();
for (int i = 0; i < seeds; i++) {
Vector seedPos = position.getRandomPointInRadius(genetics.getMaxOffspringSpawnDistance() + growthFactor - 1);
newPlants[i] = new Plant(genetics.getOffspringGenetics(), seedPos);
newPlants.add(new Plant(genetics.getOffspringGenetics(), seedPos));
}

return List.of(newPlants);
return newPlants;
}

/**
* Update this plant entity.
* Update this plant entity. This includes multiplying and handling overcrowding.
*/
@Override
public void update(Field field, double deltaTime) {
if (!isAlive()) return;
super.update(field, deltaTime);

List<Entity> nearbyEntities = searchNearbyEntities(field, genetics.getOvercrowdingRadius());

List<Plant> newPlants = multiply(field);
for (Plant plant : newPlants) {
field.putInBounds(plant, plant.getSize());
field.addEntity(plant);
}

handleOvercrowding(field);
handleOvercrowding(nearbyEntities);
}

/**
Expand All @@ -84,6 +88,7 @@ public void draw(Display display, double scaleFactor) {
size = Math.max(2, size);
int x = (int) (position.x() / scaleFactor);
int y = (int) (position.y() / scaleFactor);

display.drawEqualTriangle(x, y, size, genetics.getColour());
}
}
6 changes: 6 additions & 0 deletions src/entities/PlantTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
import util.Vector;
import genetics.PlantGenetics;

/**
* Test class for the Plant entity. Tests methods underneath it.
*
* @author Mehmet Kutay Bozkurt and Anas Ahmed
* @version 1.0
*/
class PlantTest {
private PlantGenetics genetics;
private Field field;
Expand Down
3 changes: 1 addition & 2 deletions src/entities/Predator.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import util.Vector;

/**
* An arbitrary predator entity. Predators move around randomly and can reproduce.
* A class for representing an arbitrary predator entity.
*
* @author Mehmet Kutay Bozkurt and Anas Ahmed
* @version 1.0
Expand All @@ -31,7 +31,6 @@ public void draw(Display display, double scaleFactor) {
int x = (int) ((position.x() - (double) size / 2) / scaleFactor); // Draw rectangle centered around x, y of predator.
int y = (int) ((position.y() - (double) size / 2) / scaleFactor);

// drawSightRadius(display, scaleFactor);
display.drawRectangle(x, y, size * 2, size * 2, genetics.getColour());
}

Expand Down
3 changes: 2 additions & 1 deletion src/entities/Prey.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import util.Vector;

/**
* An arbitrary prey entity that moves around randomly and can reproduce.
* A class for representing an arbitrary prey entity.
*
* @author Anas Ahmed and Mehmet Kutay Bozkurt
* @version 1.0
Expand All @@ -30,6 +30,7 @@ public void draw(Display display, double scaleFactor) {
size = Math.max(1, size);
int x = (int) (position.x() / scaleFactor);
int y = (int) (position.y() / scaleFactor);

display.drawCircle(x, y, size, genetics.getColour());
}

Expand Down
60 changes: 42 additions & 18 deletions src/entities/generic/Animal.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,22 @@
import simulation.simulationData.Data;

/**
* Abstract class for all animals in the simulation.
* Contains controllers for moving, breeding, handling hunger, and the behaviour
* of the animal.
* Abstract class for all animals in the simulation. Contains controllers for
* moving, breeding, handling hunger, and the behaviour of the animal. Contains
* the update method to handle all the controllers and update the behaviour of the animal.
*
* @author Mehmet Kutay Bozkurt and Anas Ahmed
* @version 1.0
*/
public abstract class Animal extends Entity {
protected AnimalGenetics genetics; // Re-cast to AnimalGenetics
protected AnimalGenetics genetics; // Re-cast to AnimalGenetics. Stores the genetics of this animal.

protected boolean isMovingToMate = false; // Stores if the animal is currently attempting to mate
protected boolean isAsleep = false; // Stores if the animal is currently asleep
protected boolean isAsleep = false; // Stores if the animal is currently asleep.

protected final AnimalMovementController movementController; // Controller for moving the animal
protected final AnimalBreedingController breedingController; // Controller for breeding the animal
protected final AnimalHungerController hungerController; // Controller for handling hunger
protected final AnimalBehaviourController behaviourController; // Controller for handling decision logic of animals
protected final AnimalMovementController movementController; // Controller for moving the animal.
protected final AnimalBreedingController breedingController; // Controller for reproduction.
protected final AnimalHungerController hungerController; // Controller for handling hunger.
protected final AnimalBehaviourController behaviourController; // Controller for handling decision logic of animals.

/**
* Constructor -- Create a new Animal.
Expand All @@ -49,32 +48,57 @@ public void update(Field field, double deltaTime) {
if (!isAlive()) return;
super.update(field, deltaTime);

// Cache the nearby entities to avoid multiple searches over the field, which would slow down the simulation.
List<Entity> nearbyEntities = searchNearbyEntities(field, genetics.getSight());

// Delagate the breeding to the controller. If breeding occurs, add the offspring to the field,
// otherwise the list will be empty.
List<Animal> newEntities = breedingController.breed(nearbyEntities);
for (Animal entity : newEntities) {
field.putInBounds(entity, entity.getSize());
field.addEntity(entity);
}

handleOvercrowding(field);
handleOvercrowding(nearbyEntities);

hungerController.handleHunger(deltaTime, newEntities.size());

if (field.environment.getWeather() == Weather.WINDY || field.environment.getWeather() == Weather.STORM) {
Vector windVector = field.environment.getWindVector().multiply(Data.getWindStrength());
setPosition(position.add(windVector));
}
windyCondition(field); // Handle windy condition.

Vector lastPosition = position;
movementController.setLastPosition(lastPosition); // Update last position before moving.

// Update the behaviour of the animal, according to the nearby entities and the field:
behaviourController.updateBehaviour(field, nearbyEntities, deltaTime);

stormyCondition(field, lastPosition); // Handle stormy condition.
}

/**
* Handle windy condition for the animal, moving it in the direction of the wind.
* @param field The field the animal is in. Used to get access the environment.
*/
private void windyCondition(Field field) {
// If wind is present, move the animal in the direction of the wind.
if (field.environment.getWeather() == Weather.WINDY || field.environment.getWeather() == Weather.STORM) {
// Wind strength is applied here:
Vector windVector = field.environment.getWindVector().multiply(Data.getWindStrength());
this.setPosition(position.add(windVector));
}
}

/**
* Handle stormy condition for the animal, hindering its movement.
* @param field The field the animal is in. Used to get access the environment.
* @param lastPosition The last position of the animal to calculate the speed.
*/
private void stormyCondition(Field field, Vector lastPosition) {
// Hinder the speed of the animal if it is stormy:
if (field.environment.getWeather() == Weather.STORM) {
Vector differenceVector = position.subtract(lastPosition);
double speed = differenceVector.getMagnitude();
speed *= Data.getStormMovementSpeedFactor() / ((double) getSize() / 4);
// The speed is decreased by the storm movement speed factor and the size of the animal:
speed *= Data.getStormMovementSpeedFactor() / (getSize() / 4d);
setPosition(lastPosition.add(differenceVector.multiply(speed)));
}
}
Expand All @@ -89,7 +113,7 @@ public void update(Field field, double deltaTime) {
* Used for a rather specific use case in prey detecting predators that can eat them,
* so that the prey can run away.
*/
public AnimalHungerController getHungerController() {
return hungerController;
public boolean canEat(Entity entity) {
return hungerController.canEat(entity);
}
}
47 changes: 18 additions & 29 deletions src/entities/generic/AnimalBehaviourController.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
package entities.generic;

import entities.Predator;
import simulation.Field;

import java.util.List;
import java.util.function.Predicate;

/**
* Handles animal behaviour. All animal behaviour, predator or prey, is identical.
* They move to food and away from things that eat them.
* They move to food and away from things that eat them. They try to move to
* mates when they are not hungry. They sleep at night if they are not hungry.
*
* @author Anas Ahmed and Mehmet Kutay Bozkurt
* @version 1.0
*/
public class AnimalBehaviourController {
private final Animal animal; // The animal this controller is controlling
private final Animal animal; // The animal this controller is controlling.

private boolean isMovingToMate = false; // Stores if the animal is currently attempting to mate.

/**
* Constructor.
Expand All @@ -34,48 +35,36 @@ public void updateBehaviour(Field field, List<Entity> nearbyEntities, double del
animal.isAsleep = false;

boolean isHungry = animal.hungerController.isHungry();

// Extreme case for prey to prioritise food over fleeing from predators:
boolean isDyingOfHunger = animal.hungerController.isDyingOfHunger();

if (isHungry) animal.hungerController.eat(nearbyEntities);

if (!isDyingOfHunger) { // If not dying of hunger, attempt to flee from predators.
// If not dying of hunger and not moving to mate, attempt to flee from predators.
if (!isDyingOfHunger && !isMovingToMate) {
// If fleeing, stop other behaviour:
if (fleeFromPredators(nearbyEntities, deltaTime)) return;
if (animal.movementController.fleeFromPredators(nearbyEntities, deltaTime)) return;
}

// If not hungry, its nighttime and no predator is nearby, sleep (do nothing):
if (!isHungry && !field.environment.isDay()) {
// If not hungry, its nighttime, isn't moving to mate and no predator is nearby, sleep (do nothing):
if (!isHungry && !field.environment.isDay() && !isMovingToMate) {
animal.isAsleep = true;
return; // Stop other behaviour from occuring
return; // Stop other behaviour from occurring.
}

boolean movingToFood = false;
if (isHungry && !animal.isMovingToMate) { // If is hungry and not currently attempting to mate
// If is hungry and not currently attempting to mate, move to food:
if (isHungry && !isMovingToMate) {
movingToFood = animal.movementController.moveToNearestFood(nearbyEntities, deltaTime);
}

if (!movingToFood) { // If not moving to food and not hungry, look for mate
animal.isMovingToMate = animal.movementController.moveToNearestMate(nearbyEntities, deltaTime);
if (!animal.isMovingToMate) { // If can't find mate, wander.
// If not moving to food and not hungry, look for mate:
if (!movingToFood) {
isMovingToMate = animal.movementController.moveToNearestMate(nearbyEntities, deltaTime);
if (!isMovingToMate) { // If can't find mate, just wander.
animal.movementController.wander(field, deltaTime);
}
}
}

/**
* Searches for nearby predators and flees if found.
* @return True if it flees, false otherwise.
*/
private boolean fleeFromPredators(List<Entity> nearbyEntities, double deltaTime) {
// Find the nearest predator:
Predicate<Entity> condition = e -> e instanceof Predator p && p.getHungerController().canEat(animal);
Predator nearestPredator = (Predator) animal.movementController.getNearestEntity(nearbyEntities, condition);

if (nearestPredator == null) return false;

// If a predator is found, flee!
animal.movementController.fleeFromEntity(nearestPredator, deltaTime);
return true;
}
}
Loading
Loading