diff --git a/.gitignore b/.gitignore index a418983..8c5c11a 100644 --- a/.gitignore +++ b/.gitignore @@ -156,4 +156,3 @@ Icon Network Trash Folder Temporary Items .apdisk - diff --git a/Oleksandr-JR/pom.xml b/Oleksandr-JR/pom.xml deleted file mode 100644 index a21958f..0000000 --- a/Oleksandr-JR/pom.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - 4.0.0 - - ua.com.javarush.gnew - Oleksandr-JR - 1.0-SNAPSHOT - - - 23 - 23 - UTF-8 - - - \ No newline at end of file diff --git a/Oleksandr-JR/src/main/java/ua/com/javarush/gnew/Main.java b/Oleksandr-JR/src/main/java/ua/com/javarush/gnew/Main.java deleted file mode 100644 index 898f62f..0000000 --- a/Oleksandr-JR/src/main/java/ua/com/javarush/gnew/Main.java +++ /dev/null @@ -1,7 +0,0 @@ -package ua.com.javarush.gnew; - -public class Main { - public static void main(String[] args) { - System.out.println("Hello world!"); - } -} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b03daeb --- /dev/null +++ b/README.md @@ -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! diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..9f5f814 --- /dev/null +++ b/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + + org.example + Animal-Life-Simulator + 1.0-SNAPSHOT + + + 17 + 17 + UTF-8 + + + + + + org.projectlombok + lombok + 1.18.28 + provided + + + + org.jetbrains + annotations + RELEASE + compile + + + + com.fasterxml.jackson.core + jackson-databind + 2.15.2 + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.15.2 + + + + org.reflections + reflections + 0.10.2 + + + + org.slf4j + slf4j-api + 2.0.7 + + + + org.slf4j + slf4j-simple + 2.0.7 + + + + \ No newline at end of file diff --git a/readme.md b/readme.md index 35cbc0d..b73f801 100644 --- a/readme.md +++ b/readme.md @@ -1 +1 @@ -# JavaRush M2 Final project group `NEW` +# JavaRush M2 Final project group `NEW` \ No newline at end of file diff --git a/src/main/java/org/application/PlantGrowth.java b/src/main/java/org/application/PlantGrowth.java new file mode 100644 index 0000000..8b4cf54 --- /dev/null +++ b/src/main/java/org/application/PlantGrowth.java @@ -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 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 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(); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/Simulation.java b/src/main/java/org/application/Simulation.java new file mode 100644 index 0000000..5083d18 --- /dev/null +++ b/src/main/java/org/application/Simulation.java @@ -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(); + } +} diff --git a/src/main/java/org/application/Survival.java b/src/main/java/org/application/Survival.java new file mode 100644 index 0000000..59bc259 --- /dev/null +++ b/src/main/java/org/application/Survival.java @@ -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); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/annotations/Config.java b/src/main/java/org/application/annotations/Config.java new file mode 100644 index 0000000..5e07c2d --- /dev/null +++ b/src/main/java/org/application/annotations/Config.java @@ -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(); +} \ No newline at end of file diff --git a/src/main/java/org/application/config/database/DataBase.java b/src/main/java/org/application/config/database/DataBase.java new file mode 100644 index 0000000..a1106a8 --- /dev/null +++ b/src/main/java/org/application/config/database/DataBase.java @@ -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, 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> setObjects() { + return reflections.getSubTypesOf(Organism.class) + .stream() + .filter(clazz -> clazz.isAnnotationPresent(Config.class)) + .collect(Collectors.toSet()); + } + + private Map, java.lang.Record> loadDataBase() { + return setObjects() + .stream() + .collect(Collectors.toMap(k -> k, this::loadObject)); + } + + private java.lang.Record loadObject(Class organism) { + ObjectMapper mapper = new YAMLMapper(); + try { + return mapper.readValue(getFilePath(organism), Record.class); + } catch (IOException e) { + throw new DataBaseLoadException(e); + } + } + + private URL getFilePath(Class organism) { + Config config = organism.getAnnotation(Config.class); + return organism.getClassLoader().getResource(config.filePath()); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/config/database/Record.java b/src/main/java/org/application/config/database/Record.java new file mode 100644 index 0000000..d30f943 --- /dev/null +++ b/src/main/java/org/application/config/database/Record.java @@ -0,0 +1,26 @@ +package main.java.org.application.config.database; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import org.application.enums.ObjectType; + +import java.util.Map; + +@NoArgsConstructor +@Setter +@Getter +@ToString +public class Record { + + private double weight; + private int maxCountOnCell; + private int speed; + private double maxSatiatingFood; + private int chanceToReproduce; + private int maxChild; + private ObjectType objectType; + private Map targetMatrix; + +} \ No newline at end of file diff --git a/src/main/java/org/application/config/factory/Factory.java b/src/main/java/org/application/config/factory/Factory.java new file mode 100644 index 0000000..9612edc --- /dev/null +++ b/src/main/java/org/application/config/factory/Factory.java @@ -0,0 +1,31 @@ +package main.java.org.application.config.factory; + +import org.application.config.database.Record; +import org.application.island.Location; +import org.application.objects.Organism; + +import java.lang.reflect.InvocationTargetException; + +public class Factory { + + private static Factory instance; + + private Factory() { + } + + public static Factory getInstance() { + if (instance == null) { + instance = new Factory(); + } + return instance; + } + + public Organism create(Class clazz, Record record, Location location) { + try { + return clazz.getConstructor(Record.class, Location.class).newInstance(record, location); + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | + InvocationTargetException exception) { + throw new RuntimeException(exception); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/application/console/Console.java b/src/main/java/org/application/console/Console.java new file mode 100644 index 0000000..78fdd8a --- /dev/null +++ b/src/main/java/org/application/console/Console.java @@ -0,0 +1,93 @@ +package main.java.org.application.console; + +import lombok.SneakyThrows; +import org.application.island.Island; +import org.application.island.Location; +import org.application.objects.Organism; +import org.application.objects.animals.Animal; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +public class Console implements Runnable { + + private Map, Location.Statistic> statistic; + private final Island island; + private final long start = System.currentTimeMillis(); + + public Console(Island island) { + this.island = island; + } + + @Override + public void run() { + displayInfo(); + } + + public void displayInfo() { + statistic = cloneMap(Location.getStatistic()); + AllStatistic allStatistic = new AllStatistic(statistic); + + System.out.println("_".repeat(98)); + System.out.printf("|%-12s| %-10s| %-10s| %-10s| %-10s| %-10s| %-10s| %-10s|%n", "Organism", "All", "Alive", "Born", "Ate", "Starving", "Killed", "Dead"); + System.out.printf("|%-12s|%-10s|%-10s|%-10s|%-10s|%-10s|%-10s|%-10s|%n", "_".repeat(12), "_".repeat(11), "_".repeat(11), "_".repeat(11), "_".repeat(11), "_".repeat(11), "_".repeat(11), "_".repeat(11)); + statistic.forEach((key, value) -> System.out.printf("|%-12s| %-10d| %-10d| %-10d| %-10d| %-10d| %-10d| %-10d|%n", key.getSimpleName(), value.getAll().get(), value.getAlive().get(), value.getBorn().get(), value.getAte().get(), value.getStarving().get(), value.getKilled().get(), value.getDead().get())); + System.out.println("|" + "_".repeat(96) + "|"); + System.out.printf("|%-12s| %-10d| %-10d| %-10d| %-10d| %-10d| %-10d| %-10d|%n", "All animals", allStatistic.all, allStatistic.alive, allStatistic.born, allStatistic.ate, allStatistic.starving, allStatistic.killed, allStatistic.dead); + System.out.println("|" + "_".repeat(96) + "|"); + + time(); + System.out.println("\n".repeat(1)); + } + + private Map, Location.Statistic> cloneMap(Map, Location.Statistic> original) { + Map, Location.Statistic> copy = new HashMap<>(); + synchronized (Collections.unmodifiableMap(original)) { + original.forEach((k, v) -> copy.put(k, v.clone())); + } + return copy; + } + + private void time() { + long end = System.currentTimeMillis(); + long millisDiff = end - start; + int second = (int) millisDiff / 1000; + int min = second / 60; + if (second < 60) System.out.printf("Life time: %d sec.%n", second); + if (second >= 60) System.out.printf("Life time: %d min. %d sec.%n", min, second - min * 60); + } + + private static class AllStatistic { + + private final int all; + private final int alive; + private final int born; + private final int ate; + private final int starving; + private final int killed; + private final int dead; + + private AllStatistic(Map, Location.Statistic> statistic) { + all = load(statistic, organismStatistic -> organismStatistic.getAll().get()); + alive = load(statistic, organismStatistic -> organismStatistic.getAlive().get()); + born = load(statistic, organismStatistic -> organismStatistic.getBorn().get()); + ate = load(statistic, organismStatistic -> organismStatistic.getAte().get()); + starving = load(statistic, organismStatistic -> organismStatistic.getStarving().get()); + killed = load(statistic, organismStatistic -> organismStatistic.getKilled().get()); + dead = load(statistic, organismStatistic -> organismStatistic.getDead().get()); + } + + + // TODO: implement Callable + @SneakyThrows + private Integer load(Map, Location.Statistic> statistic, Function function) { + return statistic.entrySet() + .stream() + .filter(entry -> entry.getKey().getSuperclass().getSuperclass() == Animal.class) + .map(Map.Entry::getValue) + .reduce(0, (total, val) -> total + function.apply(val), Integer::sum); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/application/controller/Controller.java b/src/main/java/org/application/controller/Controller.java new file mode 100644 index 0000000..2278085 --- /dev/null +++ b/src/main/java/org/application/controller/Controller.java @@ -0,0 +1,28 @@ +package main.java.org.application.controller; + +import org.application.global.GlobalVariables; +import org.application.island.Island; +import org.application.island.Location; + +import java.util.Arrays; + +public abstract class Controller { + + protected final Island island; + protected final Location[][] locations; + + public Controller(Island island) { + this.island = island; + this.locations = island.getLocations(); + } + + protected abstract void doAction(Location location); + + public void start() { + GlobalVariables.lock.lock(); + Arrays.stream(locations) + .flatMap(Arrays::stream) + .forEach(this::doAction); + GlobalVariables.lock.unlock(); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/controller/EatController.java b/src/main/java/org/application/controller/EatController.java new file mode 100644 index 0000000..73b1982 --- /dev/null +++ b/src/main/java/org/application/controller/EatController.java @@ -0,0 +1,39 @@ +package main.java.org.application.controller; + +import org.application.global.GlobalVariables; +import org.application.island.Island; +import org.application.island.Location; +import org.application.objects.Organism; +import org.application.objects.animals.Animal; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class EatController extends Controller { + + public EatController(Island island) { + super(island); + } + + @Override + protected void doAction(Location location) { + location.getSetOrganismsOnLocation() + .stream() + .filter(organism -> organism instanceof Animal animal) + .map(Animal.class::cast) + .filter(Animal::canEat) + .forEach(animal -> eat(animal, location)); + } + + private void eat(Animal animal, Location location) { + List targets = location.getSetOrganismsOnLocation() + .stream() + .filter(organism -> animal.getTargetMatrix().containsKey(organism.getObjectType())) + .collect(Collectors.toList()); + if (targets.isEmpty()) return; + Collections.shuffle(targets); + Organism organism = targets.get(GlobalVariables.random.nextInt(targets.size())); + animal.eat(organism); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/controller/MoveController.java b/src/main/java/org/application/controller/MoveController.java new file mode 100644 index 0000000..f7a1a6e --- /dev/null +++ b/src/main/java/org/application/controller/MoveController.java @@ -0,0 +1,94 @@ +package main.java.org.application.controller; + +import org.application.enums.Direction; +import org.application.global.GlobalVariables; +import org.application.interfaces.Movable; +import org.application.island.Island; +import org.application.island.Location; +import org.application.objects.Organism; +import org.application.objects.animals.Animal; + +import java.util.*; + +public class MoveController extends Controller { + + + public MoveController(Island island) { + super(island); + } + + @Override + public void start() { + GlobalVariables.lock.lock(); + Arrays.stream(locations) + .flatMap(Arrays::stream) + .peek(this::doAction) + .flatMap(location -> location.getSetOrganismsOnLocation().stream()) + .filter(organism -> organism instanceof Animal animal && !animal.isCanMove()) + .forEach(organism -> ((Animal) organism).setCanMove(true)); + GlobalVariables.lock.unlock(); + } + + @Override + protected void doAction(Location location) { + location.getSetOrganismsOnLocation() + .stream() + .filter(organism -> organism instanceof Animal animal && animal.isCanMove()) + .map(organism -> (Movable) organism) + .forEach(movable -> moveAnimal(movable, location)); + } + + private void moveAnimal(Movable movable, Location currentLocation) { + Direction[] steps = movable.move(); + + Location newLocation = currentLocation; + + for (Direction direction : steps) { + int x = checkX(newLocation, direction); + int y = checkY(newLocation, direction); + + newLocation = locations[y][x]; + } + + if (newLocation == currentLocation) return; + + Animal animal = (Animal) movable; + if (newLocation.checkMaxCountOrganismOnLocation(animal)) return; + + Map, Set> organismOnLocation = newLocation.getOrganisms(); + + organismOnLocation.merge(animal.getClass(), Collections.synchronizedSet(new HashSet<>()), (set1, set2) -> { + set1.addAll(set2); + return set1; + }); + + Set newLocationOrganismSet = organismOnLocation.get(animal.getClass()); + + currentLocation.removeOrganism(animal); + newLocationOrganismSet.add(animal); + animal.setLocation(newLocation); + animal.setCanMove(false); + } + + private int checkX(Location location, Direction direction) { + int x = location.getX(); + switch (direction) { + case RIGHT -> ++x; + case LEFT -> --x; + } + if (x < 0) x++; + if (x >= locations[0].length) x--; + return x; + } + + private int checkY(Location location, Direction direction) { + int y = location.getY(); + switch (direction) { + case DOWN -> ++y; + case UP -> --y; + } + if (y < 0) y++; + if (y >= locations.length) y--; + return y; + } +} \ No newline at end of file diff --git a/src/main/java/org/application/controller/ReproduceAnimalController.java b/src/main/java/org/application/controller/ReproduceAnimalController.java new file mode 100644 index 0000000..b36818c --- /dev/null +++ b/src/main/java/org/application/controller/ReproduceAnimalController.java @@ -0,0 +1,81 @@ +package main.java.org.application.controller; + +import org.application.global.GlobalVariables; +import org.application.interfaces.Reproducible; +import org.application.island.Island; +import org.application.island.Location; +import org.application.objects.Organism; +import org.application.objects.animals.Animal; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class ReproduceAnimalController extends Controller { + + ThreadLocalRandom random = GlobalVariables.random; + + public ReproduceAnimalController(Island island) { + super(island); + } + + @Override + public void start() { + GlobalVariables.lock.lock(); + Arrays.stream(locations) + .flatMap(Arrays::stream) + .peek(this::doAction) + .flatMap(location -> location.getSetOrganismsOnLocation().stream()) + .filter(organism -> organism instanceof Animal animal && !animal.isCanReproduce()) + .forEach(organism -> organism.setCanReproduce(true)); + GlobalVariables.lock.unlock(); + } + + @Override + protected void doAction(Location location) { + location.getSetOrganismsOnLocation() + .stream() + .filter(organism -> organism instanceof Animal animal && animal.isCanReproduce()) + .forEach(organism -> reproduce(organism, location)); + } + + private void reproduce(Reproducible reproducible, Location location) { + Organism organism = (Organism) reproducible; + + int countOnLocation = location.getSpeciesAnimalsOnLocation(organism).size(); + int maxCountOnCell = organism.getMaxCountOnCell(); + if (countOnLocation >= maxCountOnCell) return; + + Set reproducibleAnimalSameSpeciesOnLocation = reproducibleAnimalSameSpeciesOnLocation(organism, location); + long countReproduceAnimalOnLocation = reproducibleAnimalSameSpeciesOnLocation.size(); + + double chance = random.nextInt(100) + 1; + if (chance > organism.getChanceToReproduce() || countReproduceAnimalOnLocation < 1) return; + + Organism organismToReproduce = reproducibleAnimalSameSpeciesOnLocation.stream().findAny().get(); + + int countChild = random.nextInt(((Animal) organism).getMaxChild()) + 1; + countChild = Math.min(countChild, (maxCountOnCell - countOnLocation)); + + Set child = Collections.synchronizedSet(Stream.generate(organism::multiply) + .limit(countChild) + .collect(Collectors.toSet())); + + location.getOrganisms().merge(organism.getClass(), child, (set1, set2) -> { + set1.addAll(set2); + return set1; + }); + organism.setCanReproduce(false); + organismToReproduce.setCanReproduce(false); + } + + private Set reproducibleAnimalSameSpeciesOnLocation(Organism organism, Location location) { + return location.getSpeciesAnimalsOnLocation(organism) + .stream() + .filter(rec -> rec instanceof Animal animal && animal.isCanReproduce() && rec != organism) + .collect(Collectors.toSet()); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/controller/StarvingController.java b/src/main/java/org/application/controller/StarvingController.java new file mode 100644 index 0000000..5b5cdff --- /dev/null +++ b/src/main/java/org/application/controller/StarvingController.java @@ -0,0 +1,35 @@ +package main.java.org.application.controller; + +import org.application.global.GlobalVariables; +import org.application.island.Island; +import org.application.island.Location; +import org.application.objects.animals.Animal; + +public class StarvingController extends Controller { + + private final double starvingPercent = 0.30; + + public StarvingController(Island island) { + super(island); + } + + @Override + protected void doAction(Location location) { + location.getSetOrganismsOnLocation() + .stream() + .filter(organism -> organism instanceof Animal animal) + .map(Animal.class::cast) + .filter(animal -> animal.getMaxSatiatingFood() > 0) + .forEach(animal -> starving(animal, location)); + } + + private void starving(Animal animal, Location location) { + double starvingPerStep = animal.getMaxSatiatingFood() * starvingPercent; + double saturation = animal.getSaturation(); + if (saturation <= starvingPerStep) { + location.removeOrganism(animal); + Location.getOrganismStatistic(animal.getClass()).logStarvingOrganisms(); + Location.getOrganismStatistic(animal.getClass()).logDeadOrganisms(); + } else animal.setSaturation(saturation - starvingPerStep); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/enums/Direction.java b/src/main/java/org/application/enums/Direction.java new file mode 100644 index 0000000..01eeed5 --- /dev/null +++ b/src/main/java/org/application/enums/Direction.java @@ -0,0 +1,8 @@ +package main.java.org.application.enums; + +public enum Direction { + LEFT, + RIGHT, + UP, + DOWN +} diff --git a/src/main/java/org/application/enums/ObjectType.java b/src/main/java/org/application/enums/ObjectType.java new file mode 100644 index 0000000..5c04a88 --- /dev/null +++ b/src/main/java/org/application/enums/ObjectType.java @@ -0,0 +1,34 @@ +package main.java.org.application.enums; + +import lombok.Getter; +import org.application.objects.Organism; +import org.application.objects.animals.herbivorous.*; +import org.application.objects.animals.predators.*; +import org.application.objects.plants.*; + +@Getter +public enum ObjectType { + + Boar(Boar.class), + Buffalo(Buffalo.class), + Caterpillar(Caterpillar.class), + Deer(Deer.class), + Duck(Duck.class), + Goat(Goat.class), + Horse(Horse.class), + Mouse(Mouse.class), + Rabbit(Rabbit.class), + Sheep(Sheep.class), + Bear(Bear.class), + Boa(Boa.class), + Eagle(Eagle.class), + Fox(Fox.class), + Wolf(Wolf.class), + Grass(Grass.class); + + private final Class clazz; + + ObjectType(Class clazz) { + this.clazz = clazz; + } +} \ No newline at end of file diff --git a/src/main/java/org/application/exception/DataBaseLoadException.java b/src/main/java/org/application/exception/DataBaseLoadException.java new file mode 100644 index 0000000..ec8cfda --- /dev/null +++ b/src/main/java/org/application/exception/DataBaseLoadException.java @@ -0,0 +1,7 @@ +package main.java.org.application.exception; + +public class DataBaseLoadException extends RuntimeException { + public DataBaseLoadException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/org/application/global/GlobalVariables.java b/src/main/java/org/application/global/GlobalVariables.java new file mode 100644 index 0000000..bd2c214 --- /dev/null +++ b/src/main/java/org/application/global/GlobalVariables.java @@ -0,0 +1,10 @@ +package main.java.org.application.global; + +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.locks.ReentrantLock; + +public class GlobalVariables { + + public static final ReentrantLock lock = new ReentrantLock(); + public static final ThreadLocalRandom random = ThreadLocalRandom.current(); +} diff --git a/src/main/java/org/application/interfaces/Eatable.java b/src/main/java/org/application/interfaces/Eatable.java new file mode 100644 index 0000000..e1cc114 --- /dev/null +++ b/src/main/java/org/application/interfaces/Eatable.java @@ -0,0 +1,7 @@ +package main.java.org.application.interfaces; + +import org.application.objects.Organism; + +public interface Eatable { + void eat(Organism organism); +} diff --git a/src/main/java/org/application/interfaces/Movable.java b/src/main/java/org/application/interfaces/Movable.java new file mode 100644 index 0000000..2305409 --- /dev/null +++ b/src/main/java/org/application/interfaces/Movable.java @@ -0,0 +1,7 @@ +package main.java.org.application.interfaces; + +import org.application.enums.Direction; + +public interface Movable { + Direction[] move(); +} diff --git a/src/main/java/org/application/interfaces/Reproducible.java b/src/main/java/org/application/interfaces/Reproducible.java new file mode 100644 index 0000000..a87e662 --- /dev/null +++ b/src/main/java/org/application/interfaces/Reproducible.java @@ -0,0 +1,7 @@ +package main.java.org.application.interfaces; + +import org.application.objects.Organism; + +public interface Reproducible { + Organism multiply(); +} diff --git a/src/main/java/org/application/island/Island.java b/src/main/java/org/application/island/Island.java new file mode 100644 index 0000000..3cb5804 --- /dev/null +++ b/src/main/java/org/application/island/Island.java @@ -0,0 +1,83 @@ +package main.java.org.application.island; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.application.annotations.Config; +import org.application.config.database.DataBase; +import org.application.config.database.Record; +import org.application.objects.Organism; +import org.yaml.snakeyaml.Yaml; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +@Getter +@Setter +@NoArgsConstructor +@Config(filePath = "island/island.yaml") +public class Island { + + private final Map, Record> dataBase = DataBase.getInstance().getDataBase(); + + private int width; + private int height; + private long msToReloadConsole; + private volatile Location[][] locations; + + { + loadIslandConfig(); + locations = new Location[height][width]; + initIsland(); + } + + public void initIsland() { + for (int y = 0; y < locations.length; y++) { + for (int x = 0; x < locations[y].length; x++) { + locations[y][x] = createLocation(x, y); + } + } + } + + private Location createLocation(int x, int y) { + Location location = new Location(x, y); + DataBase.setObjects() + .forEach(clazz -> { + Record record = dataBase.get(clazz); + int maxCountOnCell = location.countOfAnimals(record.getMaxCountOnCell()); + if (maxCountOnCell != 0) { + location.fillLocation(clazz, record, maxCountOnCell); + } + + }); + return location; + } + + private void loadIslandConfig() { + try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("island/island.yaml")) { + Yaml yaml = new Yaml(); + Map map = yaml.load(inputStream); + this.width = map.get("width"); + this.height = map.get("height"); + this.msToReloadConsole = map.get("msToReloadConsole"); + } catch (IOException exception) { + throw new RuntimeException("file not found", exception); + } + } + + public Map, Set> getGroupingOrganismMap() { + return Arrays.stream(locations) + .flatMap(Arrays::stream) + .map(Location::getOrganisms) + .map(Map::entrySet) + .flatMap(Collection::stream) + .collect(Collectors.groupingBy(Map.Entry::getKey, + Collectors.mapping(Map.Entry::getValue, + Collectors.flatMapping(Collection::stream, Collectors.toSet())))); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/island/Location.java b/src/main/java/org/application/island/Location.java new file mode 100644 index 0000000..b6ff779 --- /dev/null +++ b/src/main/java/org/application/island/Location.java @@ -0,0 +1,137 @@ +package main.java.org.application.island; + +import lombok.Getter; +import lombok.Setter; +import org.application.config.database.Record; +import org.application.config.factory.Factory; +import org.application.global.GlobalVariables; +import org.application.objects.Organism; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +@Getter +@Setter +public class Location { + + private final Factory factory = Factory.getInstance(); + private ThreadLocalRandom random = GlobalVariables.random; + private ConcurrentMap, Set> organisms = new ConcurrentHashMap<>(); + @Getter + private static ConcurrentMap, Statistic> statistic = new ConcurrentHashMap<>(); + + private final int x; + private final int y; + + public Location(int x, int y) { + this.x = x; + this.y = y; + } + + public void fillLocation(Class value, Record record, int countOrganisms) { + Set set = Collections.synchronizedSet(new HashSet<>()); + for (int i = 0; i < countOrganisms; i++) { + set.add(factory.create(value, record, this)); + } + organisms.put(value, set); + } + + public int countOfAnimals(int maxCountOnCell) { + return random.nextInt(maxCountOnCell + 1); + } + + public Set getSetOrganismsOnLocation() { + return organisms.values() + .stream() + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + } + + public void removeOrganism(Organism organism) { + organisms.get(organism.getClass()).remove(organism); + } + + public Set getSpeciesAnimalsOnLocation(Organism organism) { + Map, Set> organismOnLocation = getOrganisms(); + if (!organismOnLocation.containsKey(organism.getClass())) return new HashSet<>(); + return organismOnLocation.get(organism.getClass()); + } + + public boolean checkMaxCountOrganismOnLocation(Organism organism) { + int countOnLocation = getSpeciesAnimalsOnLocation(organism).size(); + int maxCountOnCell = organism.getMaxCountOnCell(); + return countOnLocation >= maxCountOnCell; + } + + public static Statistic getOrganismStatistic(Class clazz) { + if (!statistic.containsKey(clazz)) { + statistic.put(clazz, new Statistic()); + } + return statistic.get(clazz); + } + + @Getter + public static class Statistic implements Cloneable { + + private AtomicInteger all = new AtomicInteger(); + private AtomicInteger alive = new AtomicInteger(); + private AtomicInteger born = new AtomicInteger(); + private AtomicInteger ate = new AtomicInteger(); + private AtomicInteger starving = new AtomicInteger(); + private AtomicInteger killed = new AtomicInteger(); + private AtomicInteger dead = new AtomicInteger(); + + private Statistic() { + } + + public void logAllOrganisms() { + all.incrementAndGet(); + } + + public void logAliveOrganisms() { + alive.incrementAndGet(); + } + + public void logBornOrganisms() { + born.incrementAndGet(); + } + + public void logAteOrganisms() { + ate.incrementAndGet(); + } + + public void logStarvingOrganisms() { + starving.incrementAndGet(); + } + + public void logKilledOrganisms() { + killed.incrementAndGet(); + } + + public void logDeadOrganisms() { + dead.incrementAndGet(); + alive.decrementAndGet(); + } + + @Override + public Statistic clone() { + try { + Statistic clone = (Statistic) super.clone(); + clone.all = new AtomicInteger(all.get()); + clone.alive = new AtomicInteger(alive.get()); + clone.born = new AtomicInteger(born.get()); + clone.ate = new AtomicInteger(ate.get()); + clone.starving = new AtomicInteger(starving.get()); + clone.killed = new AtomicInteger(killed.get()); + clone.dead = new AtomicInteger(dead.get()); + return clone; + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/Organism.java b/src/main/java/org/application/objects/Organism.java new file mode 100644 index 0000000..0a29ce1 --- /dev/null +++ b/src/main/java/org/application/objects/Organism.java @@ -0,0 +1,40 @@ +package main.java.org.application.objects; + +import lombok.Getter; +import lombok.Setter; +import org.application.config.database.Record; +import org.application.enums.ObjectType; +import org.application.interfaces.Reproducible; +import org.application.island.Location; + +@Getter +@Setter +public abstract class Organism implements Reproducible { + + private double weight; + private int maxCountOnCell; + private ObjectType objectType; + private int chanceToReproduce; + + private boolean canReproduce = true; + + private Location location; + private Location.Statistic statistic; + + public Organism(Record record, Location location) { + weight = record.getWeight(); + maxCountOnCell = record.getMaxCountOnCell(); + objectType = record.getObjectType(); + chanceToReproduce = record.getChanceToReproduce(); + + this.location = location; + + statistic = Location.getOrganismStatistic(this.getClass()); + statistic.logAllOrganisms(); + statistic.logBornOrganisms(); + statistic.logAliveOrganisms(); + } + + public abstract Organism multiply(); + +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/Animal.java b/src/main/java/org/application/objects/animals/Animal.java new file mode 100644 index 0000000..1f4a094 --- /dev/null +++ b/src/main/java/org/application/objects/animals/Animal.java @@ -0,0 +1,68 @@ +package main.java.org.application.objects.animals; + +import lombok.Getter; +import lombok.Setter; +import org.application.config.database.Record; +import org.application.enums.Direction; +import org.application.enums.ObjectType; +import org.application.global.GlobalVariables; +import org.application.interfaces.Eatable; +import org.application.interfaces.Movable; +import org.application.interfaces.Reproducible; +import org.application.island.Location; +import org.application.objects.Organism; + +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; + +@Getter +@Setter +public abstract class Animal extends Organism implements Movable, Reproducible, Eatable { + + private ThreadLocalRandom random = GlobalVariables.random; + + private int speed; + private double maxSatiatingFood; + private double saturation; + private int maxChild; + + private boolean canMove = true; + + private Map targetMatrix; + + public Animal(Record record, Location location) { + super(record, location); + this.speed = record.getSpeed(); + this.maxSatiatingFood = record.getMaxSatiatingFood(); + this.saturation = maxSatiatingFood * 0.85; + this.targetMatrix = record.getTargetMatrix(); + this.maxChild = record.getMaxChild(); + } + + @Override + public Direction[] move() { + int countSteps = random.nextInt(speed + 1); + Direction[] steps = new Direction[countSteps]; + Direction[] directions = Direction.values(); + for (int i = 0; i < countSteps; i++) { + steps[i] = directions[random.nextInt(directions.length)]; + } + return steps; + } + + @Override + public void eat(Organism organism) { + Integer chanceToEat = targetMatrix.get(organism.getObjectType()); + if (GlobalVariables.random.nextInt(100) + 1 <= chanceToEat) { + this.saturation += organism.getWeight(); + getLocation().removeOrganism(organism); + Location.getOrganismStatistic(this.getClass()).logAteOrganisms(); + Location.getOrganismStatistic(organism.getClass()).logKilledOrganisms(); + Location.getOrganismStatistic(organism.getClass()).logDeadOrganisms(); + } + } + + public boolean canEat() { + return saturation < maxSatiatingFood || (!targetMatrix.keySet().isEmpty() && maxSatiatingFood == 0); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/Herbivorous.java b/src/main/java/org/application/objects/animals/Herbivorous.java new file mode 100644 index 0000000..b0c44da --- /dev/null +++ b/src/main/java/org/application/objects/animals/Herbivorous.java @@ -0,0 +1,40 @@ +package main.java.org.application.objects.animals; + +import org.application.config.database.Record; +import org.application.global.GlobalVariables; +import org.application.island.Location; +import org.application.objects.Organism; + +import java.util.HashSet; +import java.util.Set; + +public abstract class Herbivorous extends Animal { + + public Herbivorous(Record record, Location location) { + super(record, location); + } + + @Override + public void eat(Organism organism) { + Integer chanceToEat = getTargetMatrix().get(organism.getObjectType()); + if (GlobalVariables.random.nextInt(100) + 1 <= chanceToEat) { + eatPlant(organism); + } + } + + protected void eatPlant(Organism organism) { + double organismWeight = organism.getWeight(); + int countEatOrganisms = (int) Math.max(Math.ceil(getMaxSatiatingFood() * 0.5 / organismWeight), 1); + Set copy = new HashSet<>(getLocation().getSetOrganismsOnLocation()); + copy.stream() + .filter(org -> org.getClass() == organism.getClass()) + .limit(countEatOrganisms) + .peek(org -> this.setSaturation(this.getSaturation() + org.getWeight())) + .peek(org -> { + Location.getOrganismStatistic(this.getClass()).logAteOrganisms(); + Location.getOrganismStatistic(organism.getClass()).logKilledOrganisms(); + Location.getOrganismStatistic(organism.getClass()).logDeadOrganisms(); + }) + .forEach(getLocation()::removeOrganism); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/Predator.java b/src/main/java/org/application/objects/animals/Predator.java new file mode 100644 index 0000000..3b0ba62 --- /dev/null +++ b/src/main/java/org/application/objects/animals/Predator.java @@ -0,0 +1,11 @@ +package main.java.org.application.objects.animals; + +import org.application.config.database.Record; +import org.application.island.Location; + +public abstract class Predator extends Animal { + + public Predator(Record record, Location location) { + super(record, location); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/herbivorous/Boar.java b/src/main/java/org/application/objects/animals/herbivorous/Boar.java new file mode 100644 index 0000000..b785624 --- /dev/null +++ b/src/main/java/org/application/objects/animals/herbivorous/Boar.java @@ -0,0 +1,41 @@ +package main.java.org.application.objects.animals.herbivorous; + +import org.application.annotations.Config; +import org.application.config.database.Record; +import org.application.global.GlobalVariables; +import org.application.island.Location; +import org.application.objects.Organism; +import org.application.objects.animals.Herbivorous; +import org.application.objects.plants.Plant; + +@Config(filePath = "animals/boar.yaml") +public class Boar extends Herbivorous { + + private final Record record; + + public Boar(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Boar multiply() { + return new Boar(record, getLocation()); + } + + @Override + public void eat(Organism organism) { + Integer chanceToEat = getTargetMatrix().get(organism.getObjectType()); + if (GlobalVariables.random.nextInt(100) + 1 <= chanceToEat) { + if (organism instanceof Plant) { + eatPlant(organism); + return; + } + this.setSaturation(this.getSaturation() + organism.getWeight()); + getLocation().removeOrganism(organism); + Location.getOrganismStatistic(this.getClass()).logAteOrganisms(); + Location.getOrganismStatistic(organism.getClass()).logKilledOrganisms(); + Location.getOrganismStatistic(organism.getClass()).logDeadOrganisms(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/herbivorous/Buffalo.java b/src/main/java/org/application/objects/animals/herbivorous/Buffalo.java new file mode 100644 index 0000000..ff854f6 --- /dev/null +++ b/src/main/java/org/application/objects/animals/herbivorous/Buffalo.java @@ -0,0 +1,22 @@ +package main.java.org.application.objects.animals.herbivorous; + +import org.application.annotations.Config; +import org.application.config.database.Record; +import org.application.island.Location; +import org.application.objects.animals.Herbivorous; + +@Config(filePath = "animals/buffalo.yaml") +public class Buffalo extends Herbivorous { + + private final Record record; + + public Buffalo(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Buffalo multiply() { + return new Buffalo(record, getLocation()); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/herbivorous/Caterpillar.java b/src/main/java/org/application/objects/animals/herbivorous/Caterpillar.java new file mode 100644 index 0000000..6a04055 --- /dev/null +++ b/src/main/java/org/application/objects/animals/herbivorous/Caterpillar.java @@ -0,0 +1,22 @@ +package main.java.org.application.objects.animals.herbivorous; + +import org.application.annotations.Config; +import org.application.config.database.Record; +import org.application.island.Location; +import org.application.objects.animals.Herbivorous; + +@Config(filePath = "animals/caterpillar.yaml") +public class Caterpillar extends Herbivorous { + + private final Record record; + + public Caterpillar(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Caterpillar multiply() { + return new Caterpillar(record, getLocation()); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/herbivorous/Deer.java b/src/main/java/org/application/objects/animals/herbivorous/Deer.java new file mode 100644 index 0000000..6753b9b --- /dev/null +++ b/src/main/java/org/application/objects/animals/herbivorous/Deer.java @@ -0,0 +1,22 @@ +package main.java.org.application.objects.animals.herbivorous; + +import org.application.annotations.Config; +import org.application.config.database.Record; +import org.application.island.Location; +import org.application.objects.animals.Herbivorous; + +@Config(filePath = "animals/deer.yaml") +public class Deer extends Herbivorous { + + private final Record record; + + public Deer(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Deer multiply() { + return new Deer(record, getLocation()); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/herbivorous/Duck.java b/src/main/java/org/application/objects/animals/herbivorous/Duck.java new file mode 100644 index 0000000..04c86ca --- /dev/null +++ b/src/main/java/org/application/objects/animals/herbivorous/Duck.java @@ -0,0 +1,41 @@ +package main.java.org.application.objects.animals.herbivorous; + +import org.application.annotations.Config; +import org.application.config.database.Record; +import org.application.global.GlobalVariables; +import org.application.island.Location; +import org.application.objects.Organism; +import org.application.objects.animals.Herbivorous; +import org.application.objects.plants.Plant; + +@Config(filePath = "animals/duck.yaml") +public class Duck extends Herbivorous { + + private final Record record; + + public Duck(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Duck multiply() { + return new Duck(record, getLocation()); + } + + @Override + public void eat(Organism organism) { + Integer chanceToEat = getTargetMatrix().get(organism.getObjectType()); + if (GlobalVariables.random.nextInt(100) + 1 <= chanceToEat) { + if (organism instanceof Plant) { + eatPlant(organism); + return; + } + this.setSaturation(this.getSaturation() + organism.getWeight()); + getLocation().removeOrganism(organism); + Location.getOrganismStatistic(this.getClass()).logAteOrganisms(); + Location.getOrganismStatistic(organism.getClass()).logKilledOrganisms(); + Location.getOrganismStatistic(organism.getClass()).logDeadOrganisms(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/herbivorous/Goat.java b/src/main/java/org/application/objects/animals/herbivorous/Goat.java new file mode 100644 index 0000000..893120d --- /dev/null +++ b/src/main/java/org/application/objects/animals/herbivorous/Goat.java @@ -0,0 +1,22 @@ +package main.java.org.application.objects.animals.herbivorous; + +import org.application.annotations.Config; +import org.application.config.database.Record; +import org.application.island.Location; +import org.application.objects.animals.Herbivorous; + +@Config(filePath = "animals/goat.yaml") +public class Goat extends Herbivorous { + + private final Record record; + + public Goat(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Goat multiply() { + return new Goat(record, getLocation()); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/herbivorous/Horse.java b/src/main/java/org/application/objects/animals/herbivorous/Horse.java new file mode 100644 index 0000000..166557a --- /dev/null +++ b/src/main/java/org/application/objects/animals/herbivorous/Horse.java @@ -0,0 +1,22 @@ +package main.java.org.application.objects.animals.herbivorous; + +import org.application.annotations.Config; +import org.application.config.database.Record; +import org.application.island.Location; +import org.application.objects.animals.Herbivorous; + +@Config(filePath = "animals/horse.yaml") +public class Horse extends Herbivorous { + + private final Record record; + + public Horse(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Horse multiply() { + return new Horse(record, getLocation()); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/herbivorous/Mouse.java b/src/main/java/org/application/objects/animals/herbivorous/Mouse.java new file mode 100644 index 0000000..1504d4e --- /dev/null +++ b/src/main/java/org/application/objects/animals/herbivorous/Mouse.java @@ -0,0 +1,41 @@ +package main.java.org.application.objects.animals.herbivorous; + +import org.application.annotations.Config; +import org.application.config.database.Record; +import org.application.global.GlobalVariables; +import org.application.island.Location; +import org.application.objects.Organism; +import org.application.objects.animals.Herbivorous; +import org.application.objects.plants.Plant; + +@Config(filePath = "animals/mouse.yaml") +public class Mouse extends Herbivorous { + + private final Record record; + + public Mouse(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Mouse multiply() { + return new Mouse(record, getLocation()); + } + + @Override + public void eat(Organism organism) { + Integer chanceToEat = getTargetMatrix().get(organism.getObjectType()); + if (GlobalVariables.random.nextInt(100) + 1 <= chanceToEat) { + if (organism instanceof Plant) { + eatPlant(organism); + return; + } + this.setSaturation(this.getSaturation() + organism.getWeight()); + getLocation().removeOrganism(organism); + Location.getOrganismStatistic(this.getClass()).logAteOrganisms(); + Location.getOrganismStatistic(organism.getClass()).logKilledOrganisms(); + Location.getOrganismStatistic(organism.getClass()).logDeadOrganisms(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/herbivorous/Rabbit.java b/src/main/java/org/application/objects/animals/herbivorous/Rabbit.java new file mode 100644 index 0000000..83c3d78 --- /dev/null +++ b/src/main/java/org/application/objects/animals/herbivorous/Rabbit.java @@ -0,0 +1,22 @@ +package main.java.org.application.objects.animals.herbivorous; + +import org.application.annotations.Config; +import org.application.config.database.Record; +import org.application.island.Location; +import org.application.objects.animals.Herbivorous; + +@Config(filePath = "animals/rabbit.yaml") +public class Rabbit extends Herbivorous { + + private final Record record; + + public Rabbit(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Rabbit multiply() { + return new Rabbit(record, getLocation()); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/herbivorous/Sheep.java b/src/main/java/org/application/objects/animals/herbivorous/Sheep.java new file mode 100644 index 0000000..1481dee --- /dev/null +++ b/src/main/java/org/application/objects/animals/herbivorous/Sheep.java @@ -0,0 +1,23 @@ +package main.java.org.application.objects.animals.herbivorous; + +import org.application.annotations.Config; +import org.application.console.Console; +import org.application.island.Location; +import org.application.objects.animals.Herbivorous; +import org.application.config.database.Record; + +@Config(filePath = "animals/sheep.yaml") +public class Sheep extends Herbivorous { + + private final Record record; + + public Sheep(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Sheep multiply() { + return new Sheep(record, getLocation()); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/predators/Bear.java b/src/main/java/org/application/objects/animals/predators/Bear.java new file mode 100644 index 0000000..684d2ec --- /dev/null +++ b/src/main/java/org/application/objects/animals/predators/Bear.java @@ -0,0 +1,22 @@ +package main.java.org.application.objects.animals.predators; + +import org.application.annotations.Config; +import org.application.config.database.Record; +import org.application.island.Location; +import org.application.objects.animals.Predator; + +@Config(filePath = "animals/bear.yaml") +public class Bear extends Predator { + + private final Record record; + + public Bear(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Bear multiply() { + return new Bear(record, getLocation()); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/predators/Boa.java b/src/main/java/org/application/objects/animals/predators/Boa.java new file mode 100644 index 0000000..2c72863 --- /dev/null +++ b/src/main/java/org/application/objects/animals/predators/Boa.java @@ -0,0 +1,22 @@ +package main.java.org.application.objects.animals.predators; + +import org.application.annotations.Config; +import org.application.config.database.Record; +import org.application.island.Location; +import org.application.objects.animals.Predator; + +@Config(filePath = "animals/boa.yaml") +public class Boa extends Predator { + + private final Record record; + + public Boa(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Boa multiply() { + return new Boa(record, getLocation()); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/predators/Eagle.java b/src/main/java/org/application/objects/animals/predators/Eagle.java new file mode 100644 index 0000000..617febb --- /dev/null +++ b/src/main/java/org/application/objects/animals/predators/Eagle.java @@ -0,0 +1,22 @@ +package main.java.org.application.objects.animals.predators; + +import org.application.annotations.Config; +import org.application.config.database.Record; +import org.application.island.Location; +import org.application.objects.animals.Predator; + +@Config(filePath = "animals/eagle.yaml") +public class Eagle extends Predator { + + private final Record record; + + public Eagle(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Eagle multiply() { + return new Eagle(record, getLocation()); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/predators/Fox.java b/src/main/java/org/application/objects/animals/predators/Fox.java new file mode 100644 index 0000000..cc6a0c3 --- /dev/null +++ b/src/main/java/org/application/objects/animals/predators/Fox.java @@ -0,0 +1,22 @@ +package main.java.org.application.objects.animals.predators; + +import org.application.annotations.Config; +import org.application.config.database.Record; +import org.application.island.Location; +import org.application.objects.animals.Predator; + +@Config(filePath = "animals/fox.yaml") +public class Fox extends Predator { + + private final Record record; + + public Fox(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Fox multiply() { + return new Fox(record, getLocation()); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/animals/predators/Wolf.java b/src/main/java/org/application/objects/animals/predators/Wolf.java new file mode 100644 index 0000000..b47fd4d --- /dev/null +++ b/src/main/java/org/application/objects/animals/predators/Wolf.java @@ -0,0 +1,22 @@ +package main.java.org.application.objects.animals.predators; + +import org.application.annotations.Config; +import org.application.config.database.Record; +import org.application.island.Location; +import org.application.objects.animals.Predator; + +@Config(filePath = "animals/wolf.yaml") +public class Wolf extends Predator { + + private final Record record; + + public Wolf(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Wolf multiply() { + return new Wolf(record, getLocation()); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/plants/Grass.java b/src/main/java/org/application/objects/plants/Grass.java new file mode 100644 index 0000000..b320e88 --- /dev/null +++ b/src/main/java/org/application/objects/plants/Grass.java @@ -0,0 +1,21 @@ +package main.java.org.application.objects.plants; + +import org.application.annotations.Config; +import org.application.config.database.Record; +import org.application.island.Location; + +@Config(filePath = "plants/grass.yaml") +public class Grass extends Plant { + + private final Record record; + + public Grass(Record record, Location location) { + super(record, location); + this.record = record; + } + + @Override + public Grass multiply() { + return new Grass(record, getLocation()); + } +} \ No newline at end of file diff --git a/src/main/java/org/application/objects/plants/Plant.java b/src/main/java/org/application/objects/plants/Plant.java new file mode 100644 index 0000000..62763aa --- /dev/null +++ b/src/main/java/org/application/objects/plants/Plant.java @@ -0,0 +1,14 @@ +package main.java.org.application.objects.plants; + +import lombok.Getter; +import org.application.config.database.Record; +import org.application.island.Location; +import org.application.objects.Organism; + +@Getter +public abstract class Plant extends Organism { + + public Plant(Record record, Location location) { + super(record, location); + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..13eb4a2 --- /dev/null +++ b/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: org.application.Survival + diff --git a/src/main/resources/animals/bear.yaml b/src/main/resources/animals/bear.yaml new file mode 100644 index 0000000..f15341a --- /dev/null +++ b/src/main/resources/animals/bear.yaml @@ -0,0 +1,18 @@ +objectType: Bear +weight: 500 +maxCountOnCell: 5 +speed: 2 +maxSatiatingFood: 80 +chanceToReproduce: 30 +maxChild: 3 +targetMatrix: + Boa: 80 + Horse: 40 + Deer: 80 + Rabbit: 80 + Mouse: 90 + Goat: 70 + Sheep: 70 + Boar: 50 + Buffalo: 20 + Duck: 10 \ No newline at end of file diff --git a/src/main/resources/animals/boa.yaml b/src/main/resources/animals/boa.yaml new file mode 100644 index 0000000..40b580d --- /dev/null +++ b/src/main/resources/animals/boa.yaml @@ -0,0 +1,12 @@ +objectType: Boa +weight: 15 +maxCountOnCell: 30 +speed: 1 +maxSatiatingFood: 3 +chanceToReproduce: 15 +maxChild: 3 +targetMatrix: + Fox: 15 + Rabbit: 20 + Mouse: 40 + Duck: 10 \ No newline at end of file diff --git a/src/main/resources/animals/boar.yaml b/src/main/resources/animals/boar.yaml new file mode 100644 index 0000000..a89986f --- /dev/null +++ b/src/main/resources/animals/boar.yaml @@ -0,0 +1,11 @@ +objectType: Boar +weight: 400 +maxCountOnCell: 50 +speed: 2 +maxSatiatingFood: 50 +chanceToReproduce: 10 +maxChild: 4 +targetMatrix: + Mouse: 50 + Caterpillar: 90 + Grass: 100 \ No newline at end of file diff --git a/src/main/resources/animals/buffalo.yaml b/src/main/resources/animals/buffalo.yaml new file mode 100644 index 0000000..8d5f9a8 --- /dev/null +++ b/src/main/resources/animals/buffalo.yaml @@ -0,0 +1,9 @@ +objectType: Buffalo +weight: 700 +maxCountOnCell: 10 +speed: 3 +maxSatiatingFood: 100 +chanceToReproduce: 25 +maxChild: 2 +targetMatrix: + Grass: 100 \ No newline at end of file diff --git a/src/main/resources/animals/caterpillar.yaml b/src/main/resources/animals/caterpillar.yaml new file mode 100644 index 0000000..49d8712 --- /dev/null +++ b/src/main/resources/animals/caterpillar.yaml @@ -0,0 +1,9 @@ +objectType: Caterpillar +weight: 0.01 +maxCountOnCell: 1000 +speed: 0 +maxSatiatingFood: 0 +chanceToReproduce: 20 +maxChild: 1 +targetMatrix: + Grass: 100 \ No newline at end of file diff --git a/src/main/resources/animals/deer.yaml b/src/main/resources/animals/deer.yaml new file mode 100644 index 0000000..2b40ebe --- /dev/null +++ b/src/main/resources/animals/deer.yaml @@ -0,0 +1,9 @@ +objectType: Deer +weight: 300 +maxCountOnCell: 20 +speed: 4 +maxSatiatingFood: 50 +chanceToReproduce: 15 +maxChild: 3 +targetMatrix: + Grass: 100 \ No newline at end of file diff --git a/src/main/resources/animals/duck.yaml b/src/main/resources/animals/duck.yaml new file mode 100644 index 0000000..a67b03b --- /dev/null +++ b/src/main/resources/animals/duck.yaml @@ -0,0 +1,10 @@ +objectType: Duck +weight: 1 +maxCountOnCell: 200 +speed: 4 +maxSatiatingFood: 0.15 +chanceToReproduce: 15 +maxChild: 5 +targetMatrix: + Caterpillar: 90 + Grass: 100 \ No newline at end of file diff --git a/src/main/resources/animals/eagle.yaml b/src/main/resources/animals/eagle.yaml new file mode 100644 index 0000000..a9dba00 --- /dev/null +++ b/src/main/resources/animals/eagle.yaml @@ -0,0 +1,12 @@ +objectType: Eagle +weight: 6 +maxCountOnCell: 20 +speed: 3 +maxSatiatingFood: 1 +chanceToReproduce: 15 +maxChild: 3 +targetMatrix: + Fox: 10 + Rabbit: 90 + Mouse: 90 + Duck: 80 \ No newline at end of file diff --git a/src/main/resources/animals/fox.yaml b/src/main/resources/animals/fox.yaml new file mode 100644 index 0000000..2f8bfbd --- /dev/null +++ b/src/main/resources/animals/fox.yaml @@ -0,0 +1,12 @@ +objectType: Fox +weight: 8 +maxCountOnCell: 30 +speed: 2 +maxSatiatingFood: 2 +chanceToReproduce: 15 +maxChild: 3 +targetMatrix: + Rabbit: 70 + Mouse: 90 + Duck: 60 + Caterpillar: 40 \ No newline at end of file diff --git a/src/main/resources/animals/goat.yaml b/src/main/resources/animals/goat.yaml new file mode 100644 index 0000000..4e1e6b9 --- /dev/null +++ b/src/main/resources/animals/goat.yaml @@ -0,0 +1,9 @@ +objectType: Goat +weight: 60 +maxCountOnCell: 140 +speed: 3 +maxSatiatingFood: 10 +chanceToReproduce: 7 +maxChild: 4 +targetMatrix: + Grass: 100 \ No newline at end of file diff --git a/src/main/resources/animals/horse.yaml b/src/main/resources/animals/horse.yaml new file mode 100644 index 0000000..f4c27cc --- /dev/null +++ b/src/main/resources/animals/horse.yaml @@ -0,0 +1,9 @@ +objectType: Horse +weight: 400 +maxCountOnCell: 20 +speed: 4 +maxSatiatingFood: 60 +chanceToReproduce: 11 +maxChild: 3 +targetMatrix: + Grass: 100 \ No newline at end of file diff --git a/src/main/resources/animals/mouse.yaml b/src/main/resources/animals/mouse.yaml new file mode 100644 index 0000000..525baae --- /dev/null +++ b/src/main/resources/animals/mouse.yaml @@ -0,0 +1,10 @@ +objectType: Mouse +weight: 0.05 +maxCountOnCell: 500 +speed: 1 +maxSatiatingFood: 0.01 +chanceToReproduce: 12 +maxChild: 3 +targetMatrix: + Caterpillar: 90 + Grass: 100 \ No newline at end of file diff --git a/src/main/resources/animals/rabbit.yaml b/src/main/resources/animals/rabbit.yaml new file mode 100644 index 0000000..9f90813 --- /dev/null +++ b/src/main/resources/animals/rabbit.yaml @@ -0,0 +1,9 @@ +objectType: Rabbit +weight: 2 +maxCountOnCell: 150 +speed: 2 +maxSatiatingFood: 0.45 +chanceToReproduce: 17 +maxChild: 3 +targetMatrix: + Grass: 100 \ No newline at end of file diff --git a/src/main/resources/animals/sheep.yaml b/src/main/resources/animals/sheep.yaml new file mode 100644 index 0000000..02ee0b5 --- /dev/null +++ b/src/main/resources/animals/sheep.yaml @@ -0,0 +1,9 @@ +objectType: Sheep +weight: 70 +maxCountOnCell: 140 +speed: 3 +maxSatiatingFood: 15 +chanceToReproduce: 15 +maxChild: 3 +targetMatrix: + Grass: 100 \ No newline at end of file diff --git a/src/main/resources/animals/wolf.yaml b/src/main/resources/animals/wolf.yaml new file mode 100644 index 0000000..6912dcd --- /dev/null +++ b/src/main/resources/animals/wolf.yaml @@ -0,0 +1,17 @@ +objectType: Wolf +weight: 50 +maxCountOnCell: 30 +speed: 3 +maxSatiatingFood: 8 +chanceToReproduce: 10 +maxChild: 2 +targetMatrix: + Horse: 50 + Deer: 15 + Rabbit: 60 + Mouse: 80 + Goat: 60 + Sheep: 70 + Boar: 15 + Buffalo: 10 + Duck: 40 \ No newline at end of file diff --git a/src/main/resources/island/island.yaml b/src/main/resources/island/island.yaml new file mode 100644 index 0000000..4bf461f --- /dev/null +++ b/src/main/resources/island/island.yaml @@ -0,0 +1,3 @@ +width: 5 +height: 10 +msToReloadConsole: 1000 \ No newline at end of file diff --git a/src/main/resources/plants/grass.yaml b/src/main/resources/plants/grass.yaml new file mode 100644 index 0000000..5026f75 --- /dev/null +++ b/src/main/resources/plants/grass.yaml @@ -0,0 +1,4 @@ +weight: 1 +maxCountOnCell: 200 +chanceToReproduce: 1000 +objectType: Grass \ No newline at end of file