Skip to content
Open
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: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,3 @@ Icon
Network Trash Folder
Temporary Items
.apdisk

17 changes: 0 additions & 17 deletions Oleksandr-JR/pom.xml

This file was deleted.

7 changes: 0 additions & 7 deletions Oleksandr-JR/src/main/java/ua/com/javarush/gnew/Main.java

This file was deleted.

79 changes: 79 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Animal Life Simulator

## Overview
This project simulates the life of animals on an island. Animals interact with their environment by moving, reproducing, eating plants or other animals depending on their diet type (herbivores, carnivores, or omnivores). Plants grow independently, creating a dynamic ecosystem.

---

## Multithreading
The simulation employs multithreading to manage different aspects of the ecosystem:
- **Animals**: Live and interact on the island in one thread.
- **Plants**: Grow in a separate thread.
- **Statistics**: Generated and displayed in a third thread.

---

## Statistics
The simulation provides comprehensive data for each type of organism, including plants. The statistics include:
- Initial population count.
- Current population count.
- Number of organisms born.
- Number of organisms that successfully hunted.
- Number of organisms that died of starvation.
- Number of organisms eaten by others.
- Total deaths.

Additionally, cumulative statistics for all organisms are displayed.

---

## Implementation

### Island Structure
The island is represented as a grid of locations, each populated with various animals and plants.

### Database
Initial parameters for each organism are stored in a dedicated `DataBase` class. These parameters are loaded from YAML files into a `Record` class, which holds the configuration for each organism.

### Initialization
Island initialization involves creating locations and randomly populating them with organisms.

---

### Lifecycle

#### Animals
Animal behavior is controlled by various controllers, each handling a specific action:
1. **Movement**: Animals move randomly between locations.
2. **Reproduction**: Animals find a mate and reproduce with a certain probability, producing a random number of offspring.
3. **Feeding**: Animals search for food based on a predefined diet matrix, indicating what they can eat and the likelihood of success.
4. **Starvation**: Animals have a hunger level that decreases over time. If it reaches zero, the animal dies.

After completing these actions, the cycle repeats.

#### Plants
Plants grow independently in their own thread, unaffected by animal actions.

---

## Tools and Technologies
- **Streams**: All organism iterations use the Stream API for efficient processing.
- **Reflections**: Used for initialization and dynamic behavior implementation.
- **YAML**: Configuration files for organisms and island parameters, parsed using Jackson libraries.
- **Annotations**: Facilitate mapping between YAML data and class structures.
- **Lombok**: Simplifies code with boilerplate-reducing annotations.
- **SOLID Principles**: Ensured modularity and scalability through reflection, annotations, and abstraction.

---

## Flexibility
The system is designed for extensibility:
- New organisms can be added with minimal changes.
- Actions and logic can be modified or expanded easily.

---

## Conclusion
The project offers a unique perspective on simulating an ecosystem. Developing it required rewriting logic multiple times, which provided valuable experience in using libraries and designing modular code. Although the project is still in progress, future updates aim to optimize the simulation further.

Thank you for your interest!
64 changes: 64 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.example</groupId>
<artifactId>Animal-Life-Simulator</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.15.2</version>
</dependency>

<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.7</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.7</version>
</dependency>
</dependencies>

</project>
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
# JavaRush M2 Final project group `NEW`
# JavaRush M2 Final project group `NEW`
60 changes: 60 additions & 0 deletions src/main/java/org/application/PlantGrowth.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package main.java.org.application;

import org.application.global.GlobalVariables;
import org.application.island.Island;
import org.application.island.Location;
import org.application.objects.Organism;
import org.application.objects.plants.Plant;

import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class PlantGrowth implements Runnable {

protected final Island island;
protected final Location[][] locations;

private final ThreadLocalRandom random = GlobalVariables.random;

public PlantGrowth(Island island) {
this.island = island;
this.locations = island.getLocations();
}

protected void doAction(Location location) {
location.getSetOrganismsOnLocation()
.stream()
.filter(organism -> organism instanceof Plant)
.map(Plant.class::cast)
.map(Plant::getClass)
.distinct()
.forEach(clazz -> growthPlant(clazz, location));
}

private void growthPlant(Class<? extends Plant> clazz, Location location) {
Plant plant = (Plant) location.getOrganisms().get(clazz).stream().findFirst().get();
int randomCount = random.nextInt(plant.getMaxCountOnCell() + 1);
int newPlants = randomCount * plant.getChanceToReproduce() / 100;

Set<Organism> plants = Stream.generate(plant::multiply)
.limit(newPlants)
.collect(Collectors.toSet());

location.getOrganisms().merge(plant.getClass(), plants, (set1, set2) -> {
set1.addAll(set2);
return set1;
});
}

@Override
public void run() {
GlobalVariables.lock.lock();
Arrays.stream(locations)
.flatMap(Arrays::stream)
.forEach(this::doAction);
GlobalVariables.lock.unlock();
}
}
30 changes: 30 additions & 0 deletions src/main/java/org/application/Simulation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package main.java.org.application;

import org.application.controller.*;
import org.application.island.Island;

public class Simulation implements Runnable {

private final Island island;

private final Controller move;
private final Controller reproduce;
private final Controller starving;
private final Controller eat;

public Simulation(Island island) {
this.island = island;
move = new MoveController(island);
reproduce = new ReproduceAnimalController(island);
starving = new StarvingController(island);
eat = new EatController(island);
}

@Override
public void run() {
move.start();
reproduce.start();
eat.start();
starving.start();
}
}
22 changes: 22 additions & 0 deletions src/main/java/org/application/Survival.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package main.java.org.application;

import org.application.console.Console;
import org.application.island.Island;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Survival {
public static void main(String[] args) {
Island island = new Island();
Console console = new Console(island);
Simulation simulation = new Simulation(island);
PlantGrowth plantGrowth = new PlantGrowth(island);

ScheduledExecutorService service = Executors.newScheduledThreadPool(3);
service.scheduleAtFixedRate(plantGrowth, 0, 1, TimeUnit.SECONDS);
service.scheduleAtFixedRate(simulation, 1, 1, TimeUnit.SECONDS);
service.scheduleAtFixedRate(console, 0, island.getMsToReloadConsole(), TimeUnit.MILLISECONDS);
}
}
12 changes: 12 additions & 0 deletions src/main/java/org/application/annotations/Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main.java.org.application.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Config {
String filePath();
}
62 changes: 62 additions & 0 deletions src/main/java/org/application/config/database/DataBase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package main.java.org.application.config.database;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import lombok.Getter;
import org.application.annotations.Config;
import org.application.exception.DataBaseLoadException;
import org.application.objects.Organism;
import org.reflections.Reflections;

import java.io.IOException;
import java.lang.Record;
import java.net.URL;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Getter
public class DataBase {

private static DataBase instance;
private final Map<Class<? extends Organism>, java.lang.Record> dataBase;
private static final Reflections reflections = new Reflections("org.application");

private DataBase() {
dataBase = loadDataBase();
}

public static DataBase getInstance() {
if (instance == null) {
instance = new DataBase();
}
return instance;
}

public static Set<Class<? extends Organism>> setObjects() {
return reflections.getSubTypesOf(Organism.class)
.stream()
.filter(clazz -> clazz.isAnnotationPresent(Config.class))
.collect(Collectors.toSet());
}

private Map<Class<? extends Organism>, java.lang.Record> loadDataBase() {
return setObjects()
.stream()
.collect(Collectors.toMap(k -> k, this::loadObject));
}

private java.lang.Record loadObject(Class<? extends Organism> organism) {
ObjectMapper mapper = new YAMLMapper();
try {
return mapper.readValue(getFilePath(organism), Record.class);
} catch (IOException e) {
throw new DataBaseLoadException(e);
}
}

private URL getFilePath(Class<? extends Organism> organism) {
Config config = organism.getAnnotation(Config.class);
return organism.getClassLoader().getResource(config.filePath());
}
}
Loading