diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..cf27853
--- /dev/null
+++ b/README.md
@@ -0,0 +1,24 @@
+# NewIsland
+
+Симуляция острова с животными и растениями.
+
+## 🚀 Запуск программы
+
+1. Открыть проект в IntelliJ IDEA
+2. Найти класс `Main`
+3. Запустить метод `main()` → Run
+
+## ⚙️ Настройки симуляции
+
+В классе `Main` можно изменить параметры (в коде есть комментарии):
+
+- размер острова (карты)
+- количество животных и растений
+- количество шагов симуляции
+
+После изменения параметров перезапусти программу.
+
+## 📌 Описание
+
+Программа моделирует жизнь экосистемы острова:
+перемещение животных, размножение, питание и рост растений.
\ No newline at end of file
diff --git a/src/Fauna/AllHerbivores/Boar.java b/src/Fauna/AllHerbivores/Boar.java
new file mode 100644
index 0000000..7069146
--- /dev/null
+++ b/src/Fauna/AllHerbivores/Boar.java
@@ -0,0 +1,22 @@
+package Fauna.AllHerbivores;
+
+import Fauna.Animal;
+import MapEngine.Coordinate;
+
+public class Boar extends Herbivores{
+
+
+ public Boar() {
+ super("Boar", 400, 50, 2, 50);
+ setMaxAge(10);
+ }
+
+ @Override
+ protected Animal createChild() {
+ Boar baby = new Boar();
+ if (this.getPosition() != null) {
+ baby.setPosition(new Coordinate(this.getPosition().x, this.getPosition().y));
+ }
+ return baby;
+ }
+}
diff --git a/src/Fauna/AllHerbivores/Buffalo.java b/src/Fauna/AllHerbivores/Buffalo.java
new file mode 100644
index 0000000..60fadb3
--- /dev/null
+++ b/src/Fauna/AllHerbivores/Buffalo.java
@@ -0,0 +1,22 @@
+package Fauna.AllHerbivores;
+
+import Fauna.Animal;
+import MapEngine.Coordinate;
+
+public class Buffalo extends Herbivores{
+
+
+ public Buffalo() {
+ super("Buffalo", 700, 10, 3, 100);
+ setMaxAge(10);
+ }
+
+ @Override
+ protected Animal createChild() {
+ Buffalo baby = new Buffalo();
+ if (this.getPosition() != null) {
+ baby.setPosition(new Coordinate(this.getPosition().x, this.getPosition().y));
+ }
+ return baby;
+ }
+}
diff --git a/src/Fauna/AllHerbivores/Caterpillar.java b/src/Fauna/AllHerbivores/Caterpillar.java
new file mode 100644
index 0000000..1555905
--- /dev/null
+++ b/src/Fauna/AllHerbivores/Caterpillar.java
@@ -0,0 +1,22 @@
+package Fauna.AllHerbivores;
+
+import Fauna.Animal;
+import MapEngine.Coordinate;
+
+public class Caterpillar extends Herbivores{
+
+
+ public Caterpillar() {
+ super("Caterpillar", 0.01, 1000, 0, 0);
+ setMaxAge(10);
+ }
+
+ @Override
+ protected Animal createChild() {
+ Caterpillar baby = new Caterpillar();
+ if (this.getPosition() != null) {
+ baby.setPosition(new Coordinate(this.getPosition().x, this.getPosition().y));
+ }
+ return baby;
+ }
+}
diff --git a/src/Fauna/AllHerbivores/Deer.java b/src/Fauna/AllHerbivores/Deer.java
new file mode 100644
index 0000000..dc6952d
--- /dev/null
+++ b/src/Fauna/AllHerbivores/Deer.java
@@ -0,0 +1,22 @@
+package Fauna.AllHerbivores;
+
+import Fauna.Animal;
+import MapEngine.Coordinate;
+
+public class Deer extends Herbivores{
+
+
+ public Deer() {
+ super("Deer", 300, 20, 4, 50);
+ setMaxAge(10);
+ }
+
+ @Override
+ protected Animal createChild() {
+ Deer baby = new Deer();
+ if (this.getPosition() != null) {
+ baby.setPosition(new Coordinate(this.getPosition().x, this.getPosition().y));
+ }
+ return baby;
+ }
+}
diff --git a/src/Fauna/AllHerbivores/Duck.java b/src/Fauna/AllHerbivores/Duck.java
new file mode 100644
index 0000000..ec704b8
--- /dev/null
+++ b/src/Fauna/AllHerbivores/Duck.java
@@ -0,0 +1,22 @@
+package Fauna.AllHerbivores;
+
+import Fauna.Animal;
+import MapEngine.Coordinate;
+
+public class Duck extends Herbivores{
+
+
+ public Duck() {
+ super("Duck", 1, 200, 4, 0.15);
+ setMaxAge(10);
+ }
+
+ @Override
+ protected Animal createChild() {
+ Duck baby = new Duck();
+ if (this.getPosition() != null) {
+ baby.setPosition(new Coordinate(this.getPosition().x, this.getPosition().y));
+ }
+ return baby;
+ }
+}
diff --git a/src/Fauna/AllHerbivores/Goat.java b/src/Fauna/AllHerbivores/Goat.java
new file mode 100644
index 0000000..9c5008e
--- /dev/null
+++ b/src/Fauna/AllHerbivores/Goat.java
@@ -0,0 +1,22 @@
+package Fauna.AllHerbivores;
+
+import Fauna.Animal;
+import MapEngine.Coordinate;
+
+public class Goat extends Herbivores{
+
+
+ public Goat() {
+ super("Goat", 60, 140, 3, 10);
+ setMaxAge(10);
+ }
+
+ @Override
+ protected Animal createChild() {
+ Goat baby = new Goat();
+ if (this.getPosition() != null) {
+ baby.setPosition(new Coordinate(this.getPosition().x, this.getPosition().y));
+ }
+ return baby;
+ }
+}
diff --git a/src/Fauna/AllHerbivores/Herbivores.java b/src/Fauna/AllHerbivores/Herbivores.java
new file mode 100644
index 0000000..4d57e73
--- /dev/null
+++ b/src/Fauna/AllHerbivores/Herbivores.java
@@ -0,0 +1,52 @@
+package Fauna.AllHerbivores;
+
+import Fauna.Herbs;
+import Fauna.Animal;
+import SupportClasses.Statistics;
+
+import java.util.List;
+
+public abstract class Herbivores extends Animal {
+
+ public Herbivores(String name, double maxWeight, int maxCountOnCell, int speed, double foodNeed) {
+ super(name, maxWeight, maxCountOnCell, speed, foodNeed);
+ }
+
+ /**
+ * Общая логика травоядных — они едят растения.
+ */
+ @Override
+ public void eat(List others) {
+ for (Object other : others) {
+ if (other instanceof Herbs herb && herb.isAlive()) {
+ System.out.println(name + " ate some grass 🌿");
+ herb.beEaten();
+ hunger = 1.0;
+
+ // ✅ добавляем запись в статистику
+ Statistics.markGrassEaten(this);
+
+ return;
+ }
+ }
+ }
+
+ /**
+ * Попытка съесть траву (из отдельного списка растений, если он есть).
+ */
+ public boolean tryEatPlants(List plants) {
+ if (plants != null && !plants.isEmpty()) {
+ Herbs herb = plants.remove(0); // съели один пучок
+ if (herb != null && herb.isAlive()) {
+// System.out.println(name + " ate some grass 🌿");
+ hunger = 1.0;
+
+ // ✅ статистика
+ Statistics.markGrassEaten(this);
+
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/Fauna/AllHerbivores/Horse.java b/src/Fauna/AllHerbivores/Horse.java
new file mode 100644
index 0000000..f81d2b9
--- /dev/null
+++ b/src/Fauna/AllHerbivores/Horse.java
@@ -0,0 +1,22 @@
+package Fauna.AllHerbivores;
+
+import Fauna.Animal;
+import MapEngine.Coordinate;
+
+public class Horse extends Herbivores{
+
+
+ public Horse() {
+ super("Horse", 400, 20, 4, 60);
+ setMaxAge(10);
+ }
+
+ @Override
+ protected Animal createChild() {
+ Horse baby = new Horse();
+ if (this.getPosition() != null) {
+ baby.setPosition(new Coordinate(this.getPosition().x, this.getPosition().y));
+ }
+ return baby;
+ }
+}
diff --git a/src/Fauna/AllHerbivores/Mouse.java b/src/Fauna/AllHerbivores/Mouse.java
new file mode 100644
index 0000000..b14489b
--- /dev/null
+++ b/src/Fauna/AllHerbivores/Mouse.java
@@ -0,0 +1,22 @@
+package Fauna.AllHerbivores;
+
+import Fauna.Animal;
+import MapEngine.Coordinate;
+
+public class Mouse extends Herbivores{
+
+
+ public Mouse() {
+ super("Mouse", 0.05, 500, 1, 0.01);
+ setMaxAge(10);
+ }
+
+ @Override
+ protected Animal createChild() {
+ Mouse baby = new Mouse();
+ if (this.getPosition() != null) {
+ baby.setPosition(new Coordinate(this.getPosition().x, this.getPosition().y));
+ }
+ return baby;
+ }
+}
diff --git a/src/Fauna/AllHerbivores/Rabbit.java b/src/Fauna/AllHerbivores/Rabbit.java
new file mode 100644
index 0000000..b660998
--- /dev/null
+++ b/src/Fauna/AllHerbivores/Rabbit.java
@@ -0,0 +1,21 @@
+package Fauna.AllHerbivores;
+
+import Fauna.Animal;
+import MapEngine.Coordinate;
+
+public class Rabbit extends Herbivores {
+
+ public Rabbit() {
+ super("Rabbit", 2, 150, 2, 0.45);
+ setMaxAge(10);
+ }
+
+ @Override
+ protected Animal createChild() {
+ Rabbit baby = new Rabbit();
+ if (this.getPosition() != null) {
+ baby.setPosition(new Coordinate(this.getPosition().x, this.getPosition().y));
+ }
+ return baby;
+ }
+}
diff --git a/src/Fauna/AllHerbivores/Sheep.java b/src/Fauna/AllHerbivores/Sheep.java
new file mode 100644
index 0000000..37f7f95
--- /dev/null
+++ b/src/Fauna/AllHerbivores/Sheep.java
@@ -0,0 +1,22 @@
+package Fauna.AllHerbivores;
+
+import Fauna.Animal;
+import MapEngine.Coordinate;
+
+public class Sheep extends Herbivores{
+
+
+ public Sheep() {
+ super("Sheep", 70, 140, 3, 15);
+ setMaxAge(10);
+ }
+
+ @Override
+ protected Animal createChild() {
+ Sheep baby = new Sheep();
+ if (this.getPosition() != null) {
+ baby.setPosition(new Coordinate(this.getPosition().x, this.getPosition().y));
+ }
+ return baby;
+ }
+}
diff --git a/src/Fauna/AllPredators/Bear.java b/src/Fauna/AllPredators/Bear.java
new file mode 100644
index 0000000..ceff2fb
--- /dev/null
+++ b/src/Fauna/AllPredators/Bear.java
@@ -0,0 +1,35 @@
+package Fauna.AllPredators;
+
+import Fauna.Animal;
+import MapEngine.Coordinate;
+
+public class Bear extends Predators{
+
+
+ public Bear() {
+ super("Bear", 500, 5, 2, 80);
+ setMaxAge(50);
+ }
+
+ @Override
+ protected double getEatChance(Animal prey) {
+ // Упрощённая таблица вероятностей
+ return switch (prey.getName()) {
+ case "Mouse"-> 0.9;
+ case "Rabbit","Boa","Deer" -> 0.8;
+ case "Goat", "Sheep" -> 0.7;
+ case "Horse" -> 0.4;
+ default -> 0.0;
+ };
+ }
+
+ @Override
+ protected Animal createChild() {
+ Bear baby = new Bear();
+ // Копируем позицию родителя, если она установлена
+ if (this.getPosition() != null) {
+ baby.setPosition(new Coordinate(this.getPosition().x, this.getPosition().y));
+ }
+ return baby;
+ }
+}
diff --git a/src/Fauna/AllPredators/Boa.java b/src/Fauna/AllPredators/Boa.java
new file mode 100644
index 0000000..c3d82a8
--- /dev/null
+++ b/src/Fauna/AllPredators/Boa.java
@@ -0,0 +1,34 @@
+
+package Fauna.AllPredators;
+import Fauna.Animal;
+import MapEngine.Coordinate;
+
+ public class Boa extends Predators {
+
+ public Boa() {
+ super("Boa", 15, 30, 1, 3);
+ setMaxAge(40);
+ }
+
+ @Override
+ protected double getEatChance(Animal prey) {
+ return switch (prey.getName()) {
+ case "Rabbit" -> 0.2;
+ case "Mouse" -> 0.4;
+ case "Fox" -> 0.15;
+
+ default -> 0.0;
+ };
+ }
+
+ @Override
+ protected Animal createChild() {
+ Boa baby = new Boa();
+ if (this.getPosition() != null) {
+ baby.setPosition(new Coordinate(this.getPosition().x, this.getPosition().y));
+ }
+ return baby;
+ }
+ }
+
+
diff --git a/src/Fauna/AllPredators/Eagle.java b/src/Fauna/AllPredators/Eagle.java
new file mode 100644
index 0000000..f646c25
--- /dev/null
+++ b/src/Fauna/AllPredators/Eagle.java
@@ -0,0 +1,33 @@
+package Fauna.AllPredators;
+
+import Fauna.Animal;
+import MapEngine.Coordinate;
+
+public class Eagle extends Predators{
+
+
+ public Eagle() {
+ super("Eagle", 6, 20, 3, 1);
+ setMaxAge(50);
+ }
+
+ @Override
+ protected double getEatChance(Animal prey) {
+ // Упрощённая таблица вероятностей
+ return switch (prey.getName()) {
+ case "Rabbit","Mouse" -> 0.9;
+ case "Fox" -> 0.1;
+ default -> 0.0;
+ };
+ }
+
+ @Override
+ protected Animal createChild() {
+ Eagle baby = new Eagle();
+ // Копируем позицию родителя, если она установлена
+ if (this.getPosition() != null) {
+ baby.setPosition(new Coordinate(this.getPosition().x, this.getPosition().y));
+ }
+ return baby;
+ }
+}
diff --git a/src/Fauna/AllPredators/Fox.java b/src/Fauna/AllPredators/Fox.java
new file mode 100644
index 0000000..22d5e73
--- /dev/null
+++ b/src/Fauna/AllPredators/Fox.java
@@ -0,0 +1,31 @@
+package Fauna.AllPredators;
+
+import Fauna.Animal;
+import MapEngine.Coordinate;
+
+public class Fox extends Predators {
+
+ public Fox() {
+ super("Fox", 8, 30, 2, 2);
+ setMaxAge(40);
+ }
+
+ @Override
+ protected double getEatChance(Animal prey) {
+ return switch (prey.getName()) {
+ case "Rabbit" -> 0.8;
+ case "Mouse" -> 0.7;
+ case "Goat", "Sheep" -> 0.5;
+ default -> 0.0;
+ };
+ }
+
+ @Override
+ protected Animal createChild() {
+ Fox baby = new Fox();
+ if (this.getPosition() != null) {
+ baby.setPosition(new Coordinate(this.getPosition().x, this.getPosition().y));
+ }
+ return baby;
+ }
+}
diff --git a/src/Fauna/AllPredators/Predators.java b/src/Fauna/AllPredators/Predators.java
new file mode 100644
index 0000000..d68508f
--- /dev/null
+++ b/src/Fauna/AllPredators/Predators.java
@@ -0,0 +1,56 @@
+package Fauna.AllPredators;
+
+import Fauna.AllHerbivores.Herbivores;
+import Fauna.Animal;
+import SupportClasses.Statistics;
+
+import java.util.List;
+import java.util.Random;
+
+public abstract class Predators extends Animal {
+
+ protected final Random random = new Random();
+
+ public Predators(String name, double maxWeight, int maxCountOnCell, int speed, double foodNeed) {
+ super(name, maxWeight, maxCountOnCell, speed, foodNeed);
+ }
+
+ /**
+ * Логика кормления хищника: ищет травоядных в списке и ест.
+ */
+ @Override
+ public void eat(List others) {
+ for (Animal target : others) {
+ if (target instanceof Herbivores && target.isAlive()) {
+ double chance = getEatChance(target);
+ if (random.nextDouble() < chance) {
+// System.out.println(getName() + " attacked and ate the " + target.getName());
+ setHunger(1.0); // полностью сытый
+ target.setAlive(false);
+
+ // травоядное помечаем как съеденное
+ Statistics.markDeath(target, true);
+
+ // хищник увеличивает счетчик съеденных животных
+ Statistics.markAnimalEatenByPredator(this);
+
+ return;
+ }
+ }
+ }
+
+ // если никого не съел — становится голоднее
+ setHunger(getHunger() - 0.2);
+ }
+
+ /**
+ * Вероятность съесть конкретного травоядного.
+ * Каждый вид хищника должен переопределить этот метод.
+ */
+ protected abstract double getEatChance(Animal prey);
+
+ /**
+ * Метод для создания потомства конкретного вида хищника.
+ */
+ protected abstract Animal createChild();
+}
diff --git a/src/Fauna/AllPredators/Wolf.java b/src/Fauna/AllPredators/Wolf.java
new file mode 100644
index 0000000..e23246f
--- /dev/null
+++ b/src/Fauna/AllPredators/Wolf.java
@@ -0,0 +1,33 @@
+package Fauna.AllPredators;
+
+import Fauna.Animal;
+import MapEngine.Coordinate;
+
+public class Wolf extends Predators {
+
+ public Wolf() {
+ super("Wolf", 50, 30, 3, 8);
+ setMaxAge(50);
+ }
+
+ @Override
+ protected double getEatChance(Animal prey) {
+ // Упрощённая таблица вероятностей
+ return switch (prey.getName()) {
+ case "Rabbit" -> 0.6;
+ case "Mouse" -> 0.8;
+ case "Goat", "Sheep" -> 0.7;
+ default -> 0.0;
+ };
+ }
+
+ @Override
+ protected Animal createChild() {
+ Wolf baby = new Wolf();
+ // Копируем позицию родителя, если она установлена
+ if (this.getPosition() != null) {
+ baby.setPosition(new Coordinate(this.getPosition().x, this.getPosition().y));
+ }
+ return baby;
+ }
+}
diff --git a/src/Fauna/Animal.java b/src/Fauna/Animal.java
new file mode 100644
index 0000000..0965bab
--- /dev/null
+++ b/src/Fauna/Animal.java
@@ -0,0 +1,201 @@
+package Fauna;
+
+import MapEngine.Coordinate;
+import MapEngine.Island;
+import SupportClasses.Statistics;
+
+import java.util.List;
+import java.util.Random;
+
+public abstract class Animal {
+ // ==== Основные характеристики ====
+ protected String name;
+ protected double weight;
+ protected double maxWeight;
+ protected int maxCountOnCell;
+ protected int speed;
+ protected double foodNeed;
+ protected boolean alive = true;
+
+ // ==== Состояние ====
+ protected double hunger; // 1.0 = сыт, 0.0 = умирает
+ protected int age = 0;
+ protected int maxAge;
+ protected Coordinate position;
+
+ protected final Random random = new Random();
+
+ // ==== Конструктор ====
+ public Animal(String name, double maxWeight, int maxCountOnCell, int speed, double foodNeed) {
+ this.name = name;
+ this.maxWeight = maxWeight;
+ this.weight = maxWeight;
+ this.maxCountOnCell = maxCountOnCell;
+ this.speed = speed;
+ this.foodNeed = foodNeed;
+ this.hunger = 1.0;
+
+ // ✅ Регистрируем животное в статистике
+ Statistics.registerAnimal(this);
+ }
+
+ // ==== Логика жизни ====
+
+ /** Один шаг жизни животного */
+ public void liveCycle(Island island) {
+ if (!alive) return;
+
+ age++;
+ decreaseHunger();
+ move(island);
+ deathCheck();
+ }
+
+ /** Уменьшаем уровень сытости каждый шаг */
+ protected void decreaseHunger() {
+ hunger -= 0.1; // теряет 10% сытости за шаг
+ if (hunger < 0) hunger = 0;
+ }
+
+ /** Проверка смерти от старости или голода */
+ protected void deathCheck() {
+ if (!alive) return;
+
+ if (hunger <= 0) {
+ alive = false;
+ Statistics.markDeath(this, false);
+ // System.out.println(name + " died of hunger ☠️");
+ } else if (age >= maxAge) {
+ alive = false;
+ Statistics.markDeath(this, false);
+ // System.out.println(name + " died of old age 🕯️");
+ }
+ }
+
+ // ==== Основные действия ====
+
+ /** Двигается в случайном направлении */
+ public void move(Island island) {
+ if (!alive || position == null) return;
+
+ int dx = random.nextInt(speed * 2 + 1) - speed;
+ int dy = random.nextInt(speed * 2 + 1) - speed;
+
+ int newX = Math.max(0, Math.min(island.getWidth() - 1, position.x + dx));
+ int newY = Math.max(0, Math.min(island.getHeight() - 1, position.y + dy));
+
+ position = new Coordinate(newX, newY);
+ }
+
+ /** Еда — реализуется в наследниках */
+ public abstract void eat(List others);
+
+ /** Размножение — если рядом есть партнёр */
+ public Animal reproduce(List sameSpecies) {
+ if (!alive || sameSpecies.size() < 2) return null;
+
+ if (random.nextDouble() < 0.3) { // шанс 30%
+ Animal child = createChild();
+
+ // ✅ ставим ту же позицию, что у родителя
+ if (this.position != null) {
+ child.setPosition(new Coordinate(this.position.x, this.position.y));
+ }
+
+ // ✅ регистрируем в статистике
+ Statistics.markReproduce(child);
+
+ return child;
+ }
+ return null;
+ }
+
+ /** Абстрактный метод создания детёныша */
+ protected abstract Animal createChild();
+
+ // ==== Геттеры и сеттеры ====
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isAlive() {
+ return alive;
+ }
+
+ public void setAlive(boolean alive) {
+ this.alive = alive;
+ }
+
+ public double getHunger() {
+ return hunger;
+ }
+
+ public void setHunger(double hunger) {
+ this.hunger = hunger;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public Coordinate getPosition() {
+ return position;
+ }
+
+ public void setPosition(Coordinate position) {
+ this.position = position;
+ }
+
+ public double getWeight() {
+ return weight;
+ }
+
+ public void setWeight(double weight) {
+ this.weight = weight;
+ }
+
+ public double getMaxWeight() {
+ return maxWeight;
+ }
+
+ public void setMaxWeight(double maxWeight) {
+ this.maxWeight = maxWeight;
+ }
+
+ public int getMaxCountOnCell() {
+ return maxCountOnCell;
+ }
+
+ public void setMaxCountOnCell(int maxCountOnCell) {
+ this.maxCountOnCell = maxCountOnCell;
+ }
+
+ public int getSpeed() {
+ return speed;
+ }
+
+ public void setSpeed(int speed) {
+ this.speed = speed;
+ }
+
+ public double getFoodNeed() {
+ return foodNeed;
+ }
+
+ public void setFoodNeed(double foodNeed) {
+ this.foodNeed = foodNeed;
+ }
+
+ public int getMaxAge() {
+ return maxAge;
+ }
+
+ public void setMaxAge(int maxAge) {
+ this.maxAge = maxAge;
+ }
+}
diff --git a/src/Fauna/Herbs.java b/src/Fauna/Herbs.java
new file mode 100644
index 0000000..6236e7a
--- /dev/null
+++ b/src/Fauna/Herbs.java
@@ -0,0 +1,28 @@
+package Fauna;
+
+import MapEngine.Island;
+
+public class Herbs {
+ private final String name = "Herb";
+ private double weight = 1.0;
+ private boolean alive = true;
+
+ public Herbs() {}
+
+ public String getName() {
+ return name;
+ }
+
+ public double getWeight() {
+ return weight;
+ }
+
+ public boolean isAlive() {
+ return alive;
+ }
+
+ public void beEaten() {
+ this.alive = false;
+ System.out.println("🌿 The herb was eaten.");
+ }
+}
diff --git a/src/Fauna/Main.java b/src/Fauna/Main.java
new file mode 100644
index 0000000..f6be035
--- /dev/null
+++ b/src/Fauna/Main.java
@@ -0,0 +1,109 @@
+package Fauna;
+
+import Fauna.AllHerbivores.*;
+import Fauna.AllPredators.*;
+import MapEngine.Island;
+import SupportClasses.Statistics;
+
+import java.util.Random;
+
+public class Main {
+
+ public static void main(String[] args) {
+ // 🏝️ Создаём остров
+ Island island = new Island(100, 20);
+ Random random = new Random();
+
+ // 🔄 Сбрасываем старую статистику
+ Statistics.reset();
+
+ // 🌿 Добавляем траву
+ addHerbs(island, 200, random);
+
+ // 🐾 Добавляем животных
+ addAnimals(island, 30, Wolf.class, random, "🐺");
+ addAnimals(island, 150, Rabbit.class, random, "🐇");
+ addAnimals(island, 30, Fox.class, random, "🦊");
+ addAnimals(island, 30, Boa.class, random, "🐍");
+ addAnimals(island, 5, Bear.class, random, "🐻");
+ addAnimals(island, 20, Eagle.class, random, "🦅");
+ addAnimals(island, 20, Horse.class, random, "🐎");
+ addAnimals(island, 20, Deer.class, random, "🦌");
+ addAnimals(island, 100, Mouse.class, random, "🐁");
+ addAnimals(island, 140, Goat.class, random, "🐐");
+ addAnimals(island, 140, Sheep.class, random, "🐑");
+ addAnimals(island, 50, Boar.class, random, "🐗");
+ addAnimals(island, 50, Buffalo.class, random, "🐃");
+ addAnimals(island, 50, Duck.class, random, "🦆");
+ addAnimals(island, 50, Caterpillar.class, random, "🐛");
+
+ // 🕒 Симуляция количества шагов
+ for (int i = 1; i <= 100; i++) {
+ System.out.println("\n===== Step " + i + " =====");
+
+ // Сбрасываем счётчик травы перед шагом
+ Statistics.resetGrassCounter();
+
+ // 🚀 Запускаем шаг симуляции
+ island.simulateStep();
+
+ // 📊 Выводим статистику
+ Statistics.print();
+
+ // 🗺️ Отображаем карту
+ island.printMap();
+
+ // 💀 Подсчёт смертей
+ System.out.println("💀 " + Statistics.getDeathsThisStep() + " animals died this step.");
+
+ // 🌱 Регенерация травы
+ regenerateGrass(island, 100, random);
+ }
+
+ // 🧾 Финальный отчёт
+ System.out.println("\n===== FINAL SUMMARY =====");
+ Statistics.print();
+ System.out.println("=========================");
+
+ // 🔚 Завершаем пул потоков
+ island.shutdown();
+ }
+
+ // 🌿 Метод для добавления травы
+ private static void addHerbs(Island island, int count, Random random) {
+ for (int i = 0; i < count; i++) {
+ Herbs herbs = new Herbs();
+ int x = random.nextInt(island.getWidth());
+ int y = random.nextInt(island.getHeight());
+ island.addPlant(herbs, x, y);
+ }
+ System.out.println("🌿 Added " + count + " grass patches to the map.");
+ }
+
+ // 🌱 Метод для регенерации травы каждый шаг
+ private static void regenerateGrass(Island island, int count, Random random) {
+ for (int i = 0; i < count; i++) {
+ Herbs herbs = new Herbs();
+ int x = random.nextInt(island.getWidth());
+ int y = random.nextInt(island.getHeight());
+ island.addPlant(herbs, x, y);
+ }
+ System.out.println("🌱 Grass regrew by " + count + " patches.");
+ }
+
+ // 🐾 Метод для добавления животных
+ private static void addAnimals(Island island, int count, Class extends Animal> animalClass, Random random, String emoji) {
+ for (int i = 0; i < count; i++) {
+ try {
+ Animal animal = animalClass.getDeclaredConstructor().newInstance();
+ int x = random.nextInt(island.getWidth());
+ int y = random.nextInt(island.getHeight());
+ island.addAnimal(animal, x, y);
+ Statistics.registerAnimal(animal);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ System.out.println(emoji + " Added " + count + " " + animalClass.getSimpleName() + "(s) to the map.");
+ }
+}
diff --git a/src/Main.java b/src/Main.java
deleted file mode 100644
index 3d87d6e..0000000
--- a/src/Main.java
+++ /dev/null
@@ -1,13 +0,0 @@
-//TIP To Run code, press or
-// click the icon in the gutter.
-void main() {
- //TIP Press with your caret at the highlighted text
- // to see how IntelliJ IDEA suggests fixing it.
- IO.println(String.format("Hello and welcome!"));
-
- for (int i = 1; i <= 5; i++) {
- //TIP Press to start debugging your code. We have set one breakpoint
- // for you, but you can always add more by pressing .
- IO.println("i = " + i);
- }
-}
diff --git a/src/MapEngine/Cell.java b/src/MapEngine/Cell.java
new file mode 100644
index 0000000..1961921
--- /dev/null
+++ b/src/MapEngine/Cell.java
@@ -0,0 +1,164 @@
+package MapEngine;
+
+import Fauna.Animal;
+import Fauna.Herbs;
+import Fauna.AllHerbivores.Herbivores;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
+
+public class Cell {
+
+ private final Coordinate coordinate; // координаты клетки
+ private final List animals = new ArrayList<>(); // животные в клетке
+ private final List plants = new ArrayList<>(); // растения в клетке
+ private final Queue movedIn = new ConcurrentLinkedQueue<>(); // очередь прибывших
+
+ private final ReentrantLock lock = new ReentrantLock(); // 🔒 для синхронизации
+
+ public Cell(Coordinate coordinate) {
+ this.coordinate = coordinate;
+ }
+
+ /** Добавить животное в клетку (безопасно для многопоточности) */
+ public void addAnimal(Animal animal) {
+ if (animal == null) return;
+
+ lock.lock();
+ try {
+ long sameTypeCount = animals.stream()
+ .filter(a -> a.getClass() == animal.getClass())
+ .count();
+
+ if (sameTypeCount < animal.getMaxCountOnCell()) {
+ animals.add(animal);
+ animal.setPosition(coordinate);
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /** Используется другими потоками для прибытия животных */
+ public void enqueueIncoming(Animal animal) {
+ if (animal != null) movedIn.add(animal);
+ }
+
+ /** Добавить растение (потокобезопасно) */
+ public void addPlant(Herbs herb) {
+ if (herb != null) {
+ lock.lock();
+ try {
+ plants.add(herb);
+ } finally {
+ lock.unlock();
+ }
+ }
+ }
+
+ public List getAnimals() {
+ lock.lock();
+ try {
+ return new ArrayList<>(animals); // возвращаем копию
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public List getPlants() {
+ lock.lock();
+ try {
+ return new ArrayList<>(plants);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /** Один цикл жизни клетки (выполняется в отдельном потоке) */
+ public void liveCycle(Island island) {
+ lock.lock();
+ try {
+ // 1️⃣ Еда
+ List snapshot = new ArrayList<>(animals);
+ for (Animal animal : snapshot) {
+ if (!animal.isAlive()) continue;
+
+ if (animal instanceof Herbivores herbivore) {
+ boolean ate = herbivore.tryEatPlants(plants);
+ if (!ate) {
+ herbivore.eat(snapshot);
+ }
+ } else {
+ animal.eat(snapshot);
+ }
+ }
+
+ // 2️⃣ Размножение
+ List newborns = new ArrayList<>();
+ Map, List> byType = animals.stream()
+ .filter(Animal::isAlive)
+ .collect(Collectors.groupingBy(Object::getClass));
+
+ for (List group : byType.values()) {
+ for (Animal parent : group) {
+ Animal child = parent.reproduce(group);
+ if (child != null) {
+ child.setPosition(coordinate); // сразу ставим координаты
+ newborns.add(child);
+ }
+ }
+ }
+ for (Animal baby : newborns) addAnimal(baby);
+
+ // 3️⃣ Передвижение — только решение, без изменения чужих клеток
+ List toMove = new ArrayList<>();
+ for (Animal animal : new ArrayList<>(animals)) {
+ if (!animal.isAlive()) continue;
+
+ Coordinate oldPos = animal.getPosition();
+ animal.move(island);
+ Coordinate newPos = animal.getPosition();
+
+ if (oldPos.x != newPos.x || oldPos.y != newPos.y) {
+ toMove.add(animal);
+ }
+ }
+
+ // Убираем уехавших
+ animals.removeAll(toMove);
+
+ // Передаём их в новые клетки через очередь
+ for (Animal moving : toMove) {
+ Cell target = island.getCell(moving.getPosition().x, moving.getPosition().y);
+ if (target != null) {
+ target.enqueueIncoming(moving);
+ }
+ }
+
+ // 4️⃣ Старение и смерть
+ Iterator iterator = animals.iterator();
+ while (iterator.hasNext()) {
+ Animal a = iterator.next();
+ a.liveCycle(island);
+ if (!a.isAlive()) {
+ iterator.remove();
+ }
+ }
+
+ // 5️⃣ Приём прибывших животных (в конце фазы)
+ Animal arrived;
+ while ((arrived = movedIn.poll()) != null) {
+ addAnimal(arrived);
+ }
+
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public Coordinate getCoordinate() {
+ return coordinate;
+ }
+}
diff --git a/src/MapEngine/Coordinate.java b/src/MapEngine/Coordinate.java
new file mode 100644
index 0000000..72241ac
--- /dev/null
+++ b/src/MapEngine/Coordinate.java
@@ -0,0 +1,11 @@
+package MapEngine;
+
+public class Coordinate {
+ public final int x;
+ public final int y;
+
+ public Coordinate(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+}
diff --git a/src/MapEngine/Island.java b/src/MapEngine/Island.java
new file mode 100644
index 0000000..cd3c148
--- /dev/null
+++ b/src/MapEngine/Island.java
@@ -0,0 +1,149 @@
+package MapEngine;
+
+import Fauna.Animal;
+import Fauna.Herbs;
+
+import java.util.concurrent.*;
+
+public class Island {
+
+ private final int width;
+ private final int height;
+ private final Cell[][] cells;
+
+ // Один общий пул потоков для острова
+ private final ExecutorService pool;
+
+ public Island(int width, int height) {
+ this.width = width;
+ this.height = height;
+ this.cells = new Cell[height][width];
+
+ // создаём клетки
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ cells[y][x] = new Cell(new Coordinate(x, y));
+ }
+ }
+
+ // создаём пул потоков под число процессоров
+ this.pool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+ }
+
+ /** Возвращает клетку по координатам */
+ public Cell getCell(int x, int y) {
+ if (x < 0 || y < 0 || x >= width || y >= height) return null;
+ return cells[y][x];
+ }
+
+ /** Симуляция одного шага (все клетки работают параллельно) */
+ public void simulateStep() {
+ CompletionService completionService = new ExecutorCompletionService<>(pool);
+
+ int taskCount = 0;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ Cell cell = cells[y][x];
+ completionService.submit(() -> {
+ cell.liveCycle(this);
+ return null;
+ });
+ taskCount++;
+ }
+ }
+
+ for (int i = 0; i < taskCount; i++) {
+ try {
+ completionService.take(); // ждём, пока каждая задача завершится
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ }
+ }
+
+ /** Добавить животное в клетку по координатам */
+ public void addAnimal(Animal animal, int x, int y) {
+ Cell cell = getCell(x, y);
+ if (cell != null) {
+ cell.addAnimal(animal);
+ animal.setPosition(new Coordinate(x, y));
+ }
+ }
+
+ /** Добавить растение в клетку по координатам */
+ public void addPlant(Herbs herb, int x, int y) {
+ Cell cell = getCell(x, y);
+ if (cell != null) {
+ cell.addPlant(herb);
+ }
+ }
+
+ /** Печать карты (безопасный снимок данных) */
+ public void printMap() {
+ System.out.println("\n===== ISLAND MAP =====");
+
+ for (int y = 0; y < height; y++) {
+ StringBuilder row = new StringBuilder();
+
+ for (int x = 0; x < width; x++) {
+ Cell cell = cells[y][x];
+ String symbol = "⬜ ";
+
+ var animals = cell.getAnimals();
+ if (!animals.isEmpty()) {
+ symbol = getAnimalSymbol(animals.get(0)) + " ";
+ } else if (!cell.getPlants().isEmpty()) {
+ symbol = "🌿 ";
+ }
+
+ row.append(symbol);
+ }
+ System.out.println(row);
+ }
+ }
+
+ /** Эмодзи для животных */
+ private String getAnimalSymbol(Animal animal) {
+ return switch (animal.getName()) {
+ case "Rabbit" -> "🐇";
+ case "Fox" -> "🦊";
+ case "Wolf" -> "🐺";
+ case "Boa" -> "🐍";
+ case "Bear" -> "\uD83D\uDC3B";
+ case "Eagle" -> "\uD83E\uDD85";
+ case "Horse" -> "\uD83D\uDC0E";
+ case "Deer" -> "\uD83E\uDD8C";
+ case "Mouse" -> "\uD83D\uDC01";
+ case "Goat" -> "\uD83D\uDC10";
+ case "Sheep" -> "\uD83D\uDC11";
+ case "Boar" -> "\uD83D\uDC17";
+ case "Buffalo" -> "\uD83D\uDC03";
+ case "Duck" -> "\uD83E\uDD86";
+ case "Caterpillar" -> "\uD83D\uDC1B";
+ default -> "❓";
+ };
+ }
+
+ /** Геттеры для размеров */
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ /** Завершить все потоки (при остановке программы) */
+ public void shutdown() {
+ pool.shutdown();
+ try {
+ if (!pool.awaitTermination(5, TimeUnit.SECONDS)) {
+ pool.shutdownNow();
+ }
+ } catch (InterruptedException e) {
+ pool.shutdownNow();
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
diff --git a/src/SupportClasses/Statistics.java b/src/SupportClasses/Statistics.java
new file mode 100644
index 0000000..68877f1
--- /dev/null
+++ b/src/SupportClasses/Statistics.java
@@ -0,0 +1,142 @@
+package SupportClasses;
+
+import Fauna.Animal;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class Statistics {
+
+ public static class StatRecord {
+ public int left = 0; // живых животных
+ public int rip = 0; // умерших животных
+ public int reproduced = 0; // потомства
+ public int eaten = 0; // съедено животных
+ public int ateGrass = 0; // съедено травы
+ public int ateAnimals = 0; // сколько животных съел хищник
+ }
+
+ // Основная статистика по видам
+ private static final Map stats = new IdentityHashMap<>();
+ private static int deathsLastStep = 0;
+ private static int previousTotalDeaths = 0;
+
+
+ // Набор уже зарегистрированных объектов (уникальных экземпляров животных)
+ private static final Set registered = Collections.newSetFromMap(new IdentityHashMap<>());
+
+ /** Полный сброс статистики */
+ public static void reset() {
+ stats.clear();
+ registered.clear();
+ }
+
+ /** Регистрируем животное только один раз */
+ public static void registerAnimal(Animal animal) {
+ if (!registered.add(animal)) return; // уже зарегистрировано, пропускаем
+
+ stats.putIfAbsent(animal.getName(), new StatRecord());
+ stats.get(animal.getName()).left++;
+ }
+
+ /** Отмечаем смерть животного */
+ public static void markDeath(Animal animal, boolean eaten) {
+ // удаляем из набора зарегистрированных
+ registered.remove(animal);
+
+ StatRecord record = stats.computeIfAbsent(animal.getName(), n -> new StatRecord());
+ record.left = Math.max(0, record.left - 1);
+ record.rip++;
+ if (eaten && canBeEaten(animal.getName())) {
+ record.eaten++;
+ }
+ }
+
+ /** Отмечаем появление потомка */
+ public static void markReproduce(Animal child) {
+ // потомок — это новый объект, его тоже регистрируем
+ registerAnimal(child);
+
+ StatRecord record = stats.computeIfAbsent(child.getName(), n -> new StatRecord());
+ record.reproduced++;
+ }
+
+ /** Травоядное съело траву */
+ public static void markGrassEaten(Animal animal) {
+ if (!isHerbivore(animal.getName())) return;
+ StatRecord record = stats.computeIfAbsent(animal.getName(), n -> new StatRecord());
+ record.ateGrass++;
+ }
+
+ /** Хищник съел животное */
+ public static void markAnimalEatenByPredator(Animal predator) {
+ if (!predatorEatsAnimals(predator.getName())) return;
+ StatRecord record = stats.computeIfAbsent(predator.getName(), n -> new StatRecord());
+ record.ateAnimals++;
+ }
+
+ /** Обнуляем количество съеденной травы (например, раз в шаг) */
+ public static void resetGrassCounter() {
+ for (StatRecord record : stats.values()) {
+ record.ateGrass = 0;
+ }
+ }
+
+ /** Вывод всей статистики по видам */
+ public static void print() {
+ for (Map.Entry entry : stats.entrySet()) {
+ String name = entry.getKey();
+ StatRecord s = entry.getValue();
+
+ if (isHerbivore(name)) {
+ System.out.printf("%s: left - %d, RIP - %d, reproduced - %d, eaten - %d, ate grass - %d%n",
+ name, s.left, s.rip, s.reproduced, s.eaten, s.ateGrass);
+ } else if (predatorEatsAnimals(name) && !canBeEaten(name)) {
+ System.out.printf("%s: left - %d, RIP - %d, reproduced - %d, ate animals - %d%n",
+ name, s.left, s.rip, s.reproduced, s.ateAnimals);
+ } else {
+ System.out.printf("%s: left - %d, RIP - %d, reproduced - %d, eaten - %d, ate animals - %d%n",
+ name, s.left, s.rip, s.reproduced, s.eaten, s.ateAnimals);
+ }
+ }
+ }
+
+ // --- вспомогательные методы определения типа животного ---
+
+ private static boolean isHerbivore(String name) {
+ return switch (name) {
+ case "Rabbit", "Mouse", "Goat", "Sheep","Horse","Deer","Boar","Buffalo","Duck","Caterpillar" -> true;
+ default -> false;
+ };
+ }
+
+ private static boolean canBeEaten(String name) {
+ return switch (name) {
+ case "Rabbit", "Mouse", "Goat", "Sheep","Horse","Deer","Boar","Buffalo","Duck","Caterpillar", "Fox","Boa" -> true;
+ default -> false;
+ };
+ }
+
+ private static boolean predatorEatsAnimals(String name) {
+ return switch (name) {
+ case "Wolf", "Fox", "Boa","Bear","Eagle" -> true;
+ default -> false;
+ };
+ }
+ public static int getTotalDeaths() {
+ int total = 0;
+ for (StatRecord record : stats.values()) {
+ total += record.rip;
+ }
+ return total;
+ }
+
+ public static int getDeathsThisStep() {
+ int totalNow = getTotalDeaths();
+ deathsLastStep = totalNow - previousTotalDeaths;
+ previousTotalDeaths = totalNow;
+ return Math.max(deathsLastStep, 0);
+ }
+
+}