diff --git a/README.md b/README.md index cac387b..c2a8ab2 100644 --- a/README.md +++ b/README.md @@ -1 +1,23 @@ # Island +## Description + +This program is a model of an island with variable parameters, consisting of an array of locations (for example, 100x20 cells). The locations will be filled with plants and animals. Animals can: + +- There are plants and/or other animals (if there is suitable food in their location), + +- Move around (to neighboring locations), + +- Reproduce (if there is a pair in their location), + +- Starve to death or be eaten. + +## Menu +![Снимок экрана 2023-12-13 в 22.41.16.png](https://sun9-75.userapi.com/impf/flMZlZqf4zfSCd7SJ1_XWsEHyqQmOkGPdNni2Q/Wn8UBRtEaWU.jpg?size=1280x610&quality=96&sign=6abd3609550508af513ed3c207f6b1eb&type=album) +You can very flexibly change the simulation settings in the provided menu + +![Снимок экрана 2023-12-13 в 22.41.51.png](https://sun9-10.userapi.com/impf/4MxG-31Cp3Hy0Fvf-gsVYJ2YH-fogGtDgL4nSg/DBnGgaffTfc.jpg?size=1280x734&quality=96&sign=6e817330e3712257a28aa9753b3fb3eb&type=album) + +Also you can watch the simulation in the console + +![Снимок экрана 2023-12-13 в 22.42.32.png](https://sun9-61.userapi.com/impf/jKHTOeM3SLnVWsS_oQKfMWBzJ2JLzp42fa0HVA/K3o5_Qn_a94.jpg?size=570x844&quality=96&sign=b8d9b0ad383710e33a61ba243b08d232&type=album) +![Снимок экрана 2023-12-13 в 22.42.46.png](https://sun9-19.userapi.com/impf/Hz_evDk2xinAjGoaViAjm-LG39Efdz0ioA45cw/M8qe7KBb0js.jpg?size=570x844&quality=96&sign=f13bd777dcde69fed47b86f4edaacf17&type=album) diff --git a/src/efanov/Application.java b/src/efanov/Application.java new file mode 100644 index 0000000..25d9054 --- /dev/null +++ b/src/efanov/Application.java @@ -0,0 +1,11 @@ +package efanov; + + +import efanov.simulation.SimulationStarter; + +public class Application { + public static void main(String[] args) { + SimulationStarter simulationStarter = new SimulationStarter(); + simulationStarter.start(); + } +} diff --git a/src/efanov/constants/Constant.java b/src/efanov/constants/Constant.java new file mode 100644 index 0000000..2c51c27 --- /dev/null +++ b/src/efanov/constants/Constant.java @@ -0,0 +1,42 @@ +package efanov.constants; + +import efanov.simulation.SimulationSettings; + +public class Constant { + public static final String RESOURCES_PROBABILITY_YAML = "/Users/effgang/dev/Island/src/efanov/resources/probability.yaml"; + public static final String ANIMAL_SETTINGS_PROPERTIES = "/Users/effgang/dev/Island/src/efanov/resources/animal-settings.properties"; + public static final int PLANT_COUNT_TO_ADD = 10; + public static final String END = "***Simulation is over***"; + public static final String DELIMITER = "-".repeat(20); + public static final String SIMULATION_STARTING = "Simulation starting..."; + public static final String CHANGE_DATA = """ + Would you like to change another parameter ? + 1 - Yes + 2 - No"""; + public static final String INVALID_ANSWER = "Invalid answer "; + public static final String WRITE_1_OR_2 = " please write 1 or 2"; + public static final String IT_S_NOT_A_NUMBER = "It's not a number: "; + public static final String ENTER_THE_PARAMETER_VALUE_FOR = "Please re-enter the parameter value for "; + public static final String WRITE_THE_PARAMETER_VALUE = "Write the parameter value: "; + public static final String WRITE_NUMBER_OF_PARAMETER = "Write parameter number: "; + public static final String GREETING = "We are glad to welcome you in our simulation, choose the parameters that you want to change"; + public static final String INVALID_NUMBER_PLEASE_TRY_AGAIN = "Invalid number, please try again"; + public static final String MAP_WIDTH = "Map width"; + public static final String MAP_HEIGHT = "Map height"; + public static final String COUNT_ENTITIES_IN_ONE_LOCATION = "Count entities in one location"; + public static final String LIFE_CYCLE_OF_SIMULATION = "Life cycle of simulation (tact)"; + public static final SimulationSettings settings = new SimulationSettings(); + public static final String START_MENU = """ + 1. Map width + 2. Map height + 3. Count entities in one location + 4. Life cycle of simulation (tact) + 5. Continue + """; + public static final String DATA = "Initial data:\n" + + "width -> " + settings.getWidth() + "\n" + + "height -> " + settings.getHeight() + "\n" + + "Count entities in one location -> " + settings.getEntityCountOnCage() + "\n" + + "Life cycle of simulation (tact) -> " + settings.getLifeCycleTact() + "\n"; + +} diff --git a/src/efanov/dialog/UserDialog.java b/src/efanov/dialog/UserDialog.java new file mode 100644 index 0000000..b73d1b2 --- /dev/null +++ b/src/efanov/dialog/UserDialog.java @@ -0,0 +1,103 @@ +package efanov.dialog; + +import efanov.constants.Constant; +import efanov.simulation.SimulationSettings; + +import java.util.InputMismatchException; +import java.util.Scanner; + +public class UserDialog { + public boolean exit = false; + public String paramType = null; + + public void showMenuAndChangeParameters(SimulationSettings settings) { + try (Scanner scanner = new Scanner(System.in)) { + System.out.println(Constant.DATA); + System.out.println(Constant.GREETING); + while (true) { + System.out.println(Constant.DELIMITER); + System.out.print(Constant.START_MENU); + System.out.println(Constant.DELIMITER); + System.out.print(Constant.WRITE_NUMBER_OF_PARAMETER); + int ans; + try { + ans = scanner.nextInt(); + if (ans < 0 || ans > 5) { + System.out.println(Constant.INVALID_NUMBER_PLEASE_TRY_AGAIN); + continue; + } + } catch (InputMismatchException e) { + String badValue = scanner.nextLine(); + System.out.println(Constant.IT_S_NOT_A_NUMBER + badValue); + continue; + } + + if (ans != 5) { + System.out.print(Constant.WRITE_THE_PARAMETER_VALUE); + } + if (ans == 1) { + paramType = Constant.MAP_WIDTH; + } + if (ans == 2) { + paramType = Constant.MAP_HEIGHT; + } + if (ans == 3) { + paramType = Constant.COUNT_ENTITIES_IN_ONE_LOCATION; + } + if (ans == 4) { + paramType = Constant.LIFE_CYCLE_OF_SIMULATION; + } + if (ans == 5) { + break; + } + + int param; + while (true) { + try { + param = scanner.nextInt(); + } catch (InputMismatchException e) { + String badValue = scanner.nextLine(); + System.out.println(Constant.IT_S_NOT_A_NUMBER + badValue + "\n" + Constant.ENTER_THE_PARAMETER_VALUE_FOR + paramType); + continue; + } + break; + } + + + switch (ans) { + case 1 -> settings.setWidth(param); + case 2 -> settings.setHeight(param); + case 3 -> settings.setEntityCountOnCage(param); + case 4 -> settings.setLifeCycleTact(param); + default -> System.out.println(Constant.INVALID_NUMBER_PLEASE_TRY_AGAIN); + } + + System.out.println(Constant.CHANGE_DATA); + + while (true) { + try { + int solution = scanner.nextInt(); + if (solution == 1) { + break; + } else if (solution == 2) { + exit = true; + break; + } else { + System.out.println(Constant.INVALID_NUMBER_PLEASE_TRY_AGAIN); + } + + } catch (InputMismatchException e) { + String badValue = scanner.nextLine(); + System.out.println(Constant.INVALID_ANSWER + badValue + Constant.WRITE_1_OR_2); + } + + } + if (exit) { + break; + } + } + } finally { + System.out.println(Constant.SIMULATION_STARTING); + } + } +} diff --git a/src/efanov/entities/EntitiesFactory.java b/src/efanov/entities/EntitiesFactory.java new file mode 100644 index 0000000..2bb8f32 --- /dev/null +++ b/src/efanov/entities/EntitiesFactory.java @@ -0,0 +1,55 @@ +package efanov.entities; + +import efanov.entities.animals.Animal; +import efanov.entities.animals.herbivores.*; +import efanov.entities.animals.predators.*; +import efanov.entities.plants.Herb; +import efanov.properties.processing.Processor; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +public class EntitiesFactory { + Processor processor = new Processor(); + + public Entity createEntity(EntityType entityType) { + return switch (entityType) { + case WOLF -> getAnimal(entityType, Wolf.class); + case BEAR -> getAnimal(entityType, Bear.class); + case BOA -> getAnimal(entityType, Boa.class); + case FOX -> getAnimal(entityType, Fox.class); + case EAGLE -> getAnimal(entityType, Eagle.class); + case HORSE -> getAnimal(entityType, Horse.class); + case GOAT -> getAnimal(entityType, Goat.class); + case SHEEP -> getAnimal(entityType, Sheep.class); + case MOUSE -> getAnimal(entityType, Mouse.class); + case RABBIT -> getAnimal(entityType, Rabbit.class); + case DEER -> getAnimal(entityType, Deer.class); + case WILDBOAR -> getAnimal(entityType, WildBoar.class); + case DUCK -> getAnimal(entityType, Duck.class); + case BUFFALO -> getAnimal(entityType, Buffalo.class); + case CATERPILLAR -> getAnimal(entityType, Caterpillar.class); + case PLANT -> new Herb(processor.getEmojiFromFile(entityType.name()), + processor.getWeightFromFile(entityType.name()), + processor.getMaxCountFromFile(entityType.name())); + }; + } + + private Animal getAnimal(EntityType entityType, Class clazz) { + Constructor constructor; + try { + + constructor = clazz.getDeclaredConstructor(String.class, Double.class, Integer.class, Integer.class, Double.class); + + return constructor.newInstance(processor.getEmojiFromFile(entityType.name()), + processor.getWeightFromFile(entityType.name()), + processor.getMaxCountFromFile(entityType.name()), + processor.getSpeedFromFile(entityType.name()), + processor.getSaturationFromFile(entityType.name())); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { + throw new RuntimeException(e); + } + + } +} diff --git a/src/efanov/entities/Entity.java b/src/efanov/entities/Entity.java new file mode 100644 index 0000000..1c31cf7 --- /dev/null +++ b/src/efanov/entities/Entity.java @@ -0,0 +1,14 @@ +package efanov.entities; + +import lombok.*; + +@Getter +@Setter +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@EqualsAndHashCode +public abstract class Entity { + private String emoji; + private double weight; + private int maxCountOnLocation; + +} diff --git a/src/efanov/entities/EntityType.java b/src/efanov/entities/EntityType.java new file mode 100644 index 0000000..ab99d95 --- /dev/null +++ b/src/efanov/entities/EntityType.java @@ -0,0 +1,26 @@ +package efanov.entities; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum EntityType { + WOLF, + BOA, + FOX, + BEAR, + EAGLE, + HORSE, + DEER, + RABBIT, + MOUSE, + GOAT, + SHEEP, + WILDBOAR, + BUFFALO, + DUCK, + CATERPILLAR, + PLANT + +} diff --git a/src/efanov/entities/animals/Action.java b/src/efanov/entities/animals/Action.java new file mode 100644 index 0000000..0337f5d --- /dev/null +++ b/src/efanov/entities/animals/Action.java @@ -0,0 +1,15 @@ +package efanov.entities.animals; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum Action { + EAT(30), + REPRODUCE(30), + MOVE(30), + SLEEP(10); + + private final int actionChance; +} diff --git a/src/efanov/entities/animals/Animal.java b/src/efanov/entities/animals/Animal.java new file mode 100644 index 0000000..cc8f4fb --- /dev/null +++ b/src/efanov/entities/animals/Animal.java @@ -0,0 +1,45 @@ +package efanov.entities.animals; + +import efanov.entities.Entity; +import lombok.Getter; +import lombok.Setter; + +import java.util.concurrent.ThreadLocalRandom; + +@Getter +@Setter +public abstract class Animal extends Entity { + private static final int BOUND = 100; + protected int speed; + protected double saturation; + protected double health; + + public Animal(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation); + this.speed = speed; + this.saturation = saturation; + this.health = saturation; + } + + public abstract Animal reproduce(); + + public void eat(Entity food) { + if (food.getWeight() + this.getHealth() >= this.getSaturation()) { + this.setHealth(this.getSaturation()); + } else { + double saturationAfterEating = this.getHealth() + food.getWeight(); + this.setHealth(saturationAfterEating); + } + } + + public Direction chooseDirection() { + return Direction.values()[ThreadLocalRandom.current().nextInt(Direction.values().length)]; + } + + public Action chooseAction() { + var action = Action.values()[ThreadLocalRandom.current().nextInt(Action.values().length)]; + var isActiveAction = ThreadLocalRandom.current().nextInt(BOUND) < action.getActionChance(); + return isActiveAction ? action : Action.SLEEP; + } + +} diff --git a/src/efanov/entities/animals/Direction.java b/src/efanov/entities/animals/Direction.java new file mode 100644 index 0000000..982be73 --- /dev/null +++ b/src/efanov/entities/animals/Direction.java @@ -0,0 +1,13 @@ +package efanov.entities.animals; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum Direction { + LEFT, + RIGHT, + UP, + DOWN +} diff --git a/src/efanov/entities/animals/EatingMap.java b/src/efanov/entities/animals/EatingMap.java new file mode 100644 index 0000000..a3d48f4 --- /dev/null +++ b/src/efanov/entities/animals/EatingMap.java @@ -0,0 +1,27 @@ +package efanov.entities.animals; + +import efanov.constants.Constant; +import lombok.Getter; +import org.yaml.snakeyaml.Yaml; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.Map; + +@Getter +public class EatingMap { + InputStream inputStream; + + { + try { + inputStream = new FileInputStream(Constant.RESOURCES_PROBABILITY_YAML); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + Yaml yaml = new Yaml(); + Map> eatingProbability = yaml.load(inputStream); + +} diff --git a/src/efanov/entities/animals/herbivores/Buffalo.java b/src/efanov/entities/animals/herbivores/Buffalo.java new file mode 100644 index 0000000..83d32aa --- /dev/null +++ b/src/efanov/entities/animals/herbivores/Buffalo.java @@ -0,0 +1,13 @@ +package efanov.entities.animals.herbivores; + +public class Buffalo extends Herbivore { + + public Buffalo(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + } + + @Override + public Buffalo reproduce() { + return new Buffalo(getEmoji(), getWeight(), getMaxCountOnLocation(), speed, saturation); + } +} diff --git a/src/efanov/entities/animals/herbivores/Caterpillar.java b/src/efanov/entities/animals/herbivores/Caterpillar.java new file mode 100644 index 0000000..e4a06db --- /dev/null +++ b/src/efanov/entities/animals/herbivores/Caterpillar.java @@ -0,0 +1,14 @@ +package efanov.entities.animals.herbivores; + +public class Caterpillar extends Herbivore { + + public Caterpillar(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + this.health = maxCountOnLocation; + } + + @Override + public Caterpillar reproduce() { + return new Caterpillar(getEmoji(), getWeight(), getMaxCountOnLocation(), speed, saturation); + } +} diff --git a/src/efanov/entities/animals/herbivores/Deer.java b/src/efanov/entities/animals/herbivores/Deer.java new file mode 100644 index 0000000..8893bbb --- /dev/null +++ b/src/efanov/entities/animals/herbivores/Deer.java @@ -0,0 +1,13 @@ +package efanov.entities.animals.herbivores; + +public class Deer extends Herbivore { + + public Deer(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + } + + @Override + public Deer reproduce() { + return new Deer(getEmoji(), getWeight(), getMaxCountOnLocation(), speed, saturation); + } +} diff --git a/src/efanov/entities/animals/herbivores/Duck.java b/src/efanov/entities/animals/herbivores/Duck.java new file mode 100644 index 0000000..6787528 --- /dev/null +++ b/src/efanov/entities/animals/herbivores/Duck.java @@ -0,0 +1,13 @@ +package efanov.entities.animals.herbivores; + +public class Duck extends Herbivore { + public Duck(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + this.health = speed; + } + + @Override + public Duck reproduce() { + return new Duck(getEmoji(), getWeight(), getMaxCountOnLocation(), speed, saturation); + } +} diff --git a/src/efanov/entities/animals/herbivores/Goat.java b/src/efanov/entities/animals/herbivores/Goat.java new file mode 100644 index 0000000..0697282 --- /dev/null +++ b/src/efanov/entities/animals/herbivores/Goat.java @@ -0,0 +1,13 @@ +package efanov.entities.animals.herbivores; + +public class Goat extends Herbivore { + + public Goat(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + } + + @Override + public Goat reproduce() { + return new Goat(getEmoji(), getWeight(), getMaxCountOnLocation(), speed, saturation); + } +} diff --git a/src/efanov/entities/animals/herbivores/Herbivore.java b/src/efanov/entities/animals/herbivores/Herbivore.java new file mode 100644 index 0000000..816d3c5 --- /dev/null +++ b/src/efanov/entities/animals/herbivores/Herbivore.java @@ -0,0 +1,16 @@ +package efanov.entities.animals.herbivores; + +import efanov.entities.Entity; +import efanov.entities.animals.Animal; + +public abstract class Herbivore extends Animal { + + public Herbivore(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + } + + @Override + public void eat(Entity entity) { + + } +} diff --git a/src/efanov/entities/animals/herbivores/Horse.java b/src/efanov/entities/animals/herbivores/Horse.java new file mode 100644 index 0000000..99f688a --- /dev/null +++ b/src/efanov/entities/animals/herbivores/Horse.java @@ -0,0 +1,14 @@ +package efanov.entities.animals.herbivores; + +public class Horse extends Herbivore { + + + public Horse(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + } + + @Override + public Horse reproduce() { + return new Horse(getEmoji(), getWeight(), getMaxCountOnLocation(), speed, saturation); + } +} diff --git a/src/efanov/entities/animals/herbivores/Mouse.java b/src/efanov/entities/animals/herbivores/Mouse.java new file mode 100644 index 0000000..e754ed4 --- /dev/null +++ b/src/efanov/entities/animals/herbivores/Mouse.java @@ -0,0 +1,13 @@ +package efanov.entities.animals.herbivores; + +public class Mouse extends Herbivore { + + public Mouse(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + } + + @Override + public Mouse reproduce() { + return new Mouse(getEmoji(), getWeight(), getMaxCountOnLocation(), speed, saturation); + } +} diff --git a/src/efanov/entities/animals/herbivores/Rabbit.java b/src/efanov/entities/animals/herbivores/Rabbit.java new file mode 100644 index 0000000..6f3cba6 --- /dev/null +++ b/src/efanov/entities/animals/herbivores/Rabbit.java @@ -0,0 +1,13 @@ +package efanov.entities.animals.herbivores; + +public class Rabbit extends Herbivore { + + public Rabbit(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + } + + @Override + public Rabbit reproduce() { + return new Rabbit(getEmoji(), getWeight(), getMaxCountOnLocation(), speed, saturation); + } +} diff --git a/src/efanov/entities/animals/herbivores/Sheep.java b/src/efanov/entities/animals/herbivores/Sheep.java new file mode 100644 index 0000000..0f8c04e --- /dev/null +++ b/src/efanov/entities/animals/herbivores/Sheep.java @@ -0,0 +1,13 @@ +package efanov.entities.animals.herbivores; + +public class Sheep extends Herbivore { + + public Sheep(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + } + + @Override + public Sheep reproduce() { + return new Sheep(getEmoji(), getWeight(), getMaxCountOnLocation(), speed, saturation); + } +} diff --git a/src/efanov/entities/animals/herbivores/WildBoar.java b/src/efanov/entities/animals/herbivores/WildBoar.java new file mode 100644 index 0000000..381e2aa --- /dev/null +++ b/src/efanov/entities/animals/herbivores/WildBoar.java @@ -0,0 +1,13 @@ +package efanov.entities.animals.herbivores; + +public class WildBoar extends Herbivore { + + public WildBoar(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + } + + @Override + public WildBoar reproduce() { + return new WildBoar(getEmoji(), getWeight(), getMaxCountOnLocation(), speed, saturation); + } +} diff --git a/src/efanov/entities/animals/predators/Bear.java b/src/efanov/entities/animals/predators/Bear.java new file mode 100644 index 0000000..85cedb4 --- /dev/null +++ b/src/efanov/entities/animals/predators/Bear.java @@ -0,0 +1,13 @@ +package efanov.entities.animals.predators; + +public class Bear extends Predator { + + public Bear(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + } + + @Override + public Bear reproduce() { + return new Bear(getEmoji(), getWeight(), getMaxCountOnLocation(), speed, saturation); + } +} diff --git a/src/efanov/entities/animals/predators/Boa.java b/src/efanov/entities/animals/predators/Boa.java new file mode 100644 index 0000000..78e31f0 --- /dev/null +++ b/src/efanov/entities/animals/predators/Boa.java @@ -0,0 +1,13 @@ +package efanov.entities.animals.predators; + +public class Boa extends Predator { + + public Boa(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + } + + @Override + public Boa reproduce() { + return new Boa(getEmoji(), getWeight(), getMaxCountOnLocation(), speed, saturation); + } +} diff --git a/src/efanov/entities/animals/predators/Eagle.java b/src/efanov/entities/animals/predators/Eagle.java new file mode 100644 index 0000000..6d90b38 --- /dev/null +++ b/src/efanov/entities/animals/predators/Eagle.java @@ -0,0 +1,13 @@ +package efanov.entities.animals.predators; + +public class Eagle extends Predator { + + public Eagle(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + } + + @Override + public Eagle reproduce() { + return new Eagle(getEmoji(), getWeight(), getMaxCountOnLocation(), speed, saturation); + } +} diff --git a/src/efanov/entities/animals/predators/Fox.java b/src/efanov/entities/animals/predators/Fox.java new file mode 100644 index 0000000..9decb01 --- /dev/null +++ b/src/efanov/entities/animals/predators/Fox.java @@ -0,0 +1,13 @@ +package efanov.entities.animals.predators; + +public class Fox extends Predator { + + public Fox(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + } + + @Override + public Fox reproduce() { + return new Fox(getEmoji(), getWeight(), getMaxCountOnLocation(), speed, saturation); + } +} diff --git a/src/efanov/entities/animals/predators/Predator.java b/src/efanov/entities/animals/predators/Predator.java new file mode 100644 index 0000000..d4d6b15 --- /dev/null +++ b/src/efanov/entities/animals/predators/Predator.java @@ -0,0 +1,10 @@ +package efanov.entities.animals.predators; + +import efanov.entities.animals.Animal; + +public abstract class Predator extends Animal { + + public Predator(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + } +} diff --git a/src/efanov/entities/animals/predators/Wolf.java b/src/efanov/entities/animals/predators/Wolf.java new file mode 100644 index 0000000..60952e5 --- /dev/null +++ b/src/efanov/entities/animals/predators/Wolf.java @@ -0,0 +1,13 @@ +package efanov.entities.animals.predators; + +public class Wolf extends Predator { + + public Wolf(String emoji, Double weight, Integer maxCountOnLocation, Integer speed, Double saturation) { + super(emoji, weight, maxCountOnLocation, speed, saturation); + } + + @Override + public Wolf reproduce() { + return new Wolf(getEmoji(), getWeight(), getMaxCountOnLocation(), speed, saturation); + } +} diff --git a/src/efanov/entities/plants/Herb.java b/src/efanov/entities/plants/Herb.java new file mode 100644 index 0000000..77d7bd8 --- /dev/null +++ b/src/efanov/entities/plants/Herb.java @@ -0,0 +1,13 @@ +package efanov.entities.plants; + +import efanov.entities.Entity; +import lombok.Getter; + +@Getter +public class Herb extends Entity { + + public Herb(String emoji, double weight, int maxCountOnLocation) { + super(emoji, weight, maxCountOnLocation); + } + +} diff --git a/src/efanov/island/IslandController.java b/src/efanov/island/IslandController.java new file mode 100644 index 0000000..7739157 --- /dev/null +++ b/src/efanov/island/IslandController.java @@ -0,0 +1,77 @@ +package efanov.island; + +import efanov.constants.Constant; +import efanov.entities.animals.Action; +import efanov.entities.animals.Animal; +import efanov.entities.animals.EatingMap; +import efanov.island.service.impl.ActionServiceImpl; +import efanov.island.service.impl.StepServiceImpl; +import efanov.simulation.SimulationSettings; +import efanov.simulation.SimulationStarter; +import efanov.statistic.Statistic; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +public class IslandController { + private final IslandMap map; + private final EatingMap eatingMap; + private final SimulationSettings settings; + private final ActionServiceImpl actionService; + private final Statistic stat; + + public IslandController(IslandMap map, EatingMap eatingMap, SimulationSettings simulationSettings) { + this.map = map; + this.eatingMap = eatingMap; + this.settings = simulationSettings; + this.actionService = new ActionServiceImpl(new StepServiceImpl(), eatingMap, simulationSettings); + this.stat = new Statistic(map); + } + + public void doAction(Action action, Animal animal, Location location) { + switch (action) { + case MOVE -> actionService.doMove(animal, location, map); + case EAT -> actionService.doEat(animal, location); + case REPRODUCE -> actionService.doReproduce(animal, location); + case SLEEP -> actionService.doSleep(animal); + } + actionService.reduceHealth(animal); + } + + public void startLife() { + for (int count = 1; count <= settings.getLifeCycleTact(); count++) { + for (int y = 0; y < settings.getHeight(); y++) { + for (int x = 0; x < settings.getWidth(); x++) { + Location location = map.getLocations()[y][x]; + List animals = new ArrayList<>(location.getAnimals()); + for (Animal animal : animals) { + if (isDead(animal)) { + location.removeEntity(animal); + continue; + } + Action action = animal.chooseAction(); + doAction(action, animal, location); + } + } + } + System.out.println("***" + " Tact - " + count + "***"); + stat.printStatistic(stat.getAllEntitiesStatistic()); + } + stopSimulation(); + System.out.println(Constant.END); + } + + private void stopSimulation() { + SimulationStarter.executorService.shutdown(); + } + + private boolean isDead(Animal animal) { + return animal.getHealth() <= 0; + } + + +} diff --git a/src/efanov/island/IslandMap.java b/src/efanov/island/IslandMap.java new file mode 100644 index 0000000..ec729c6 --- /dev/null +++ b/src/efanov/island/IslandMap.java @@ -0,0 +1,84 @@ +package efanov.island; + +import efanov.constants.Constant; +import efanov.entities.EntitiesFactory; +import efanov.entities.Entity; +import efanov.entities.EntityType; +import efanov.simulation.SimulationSettings; +import lombok.Getter; +import lombok.Setter; + +import java.util.concurrent.ThreadLocalRandom; + +@Getter +@Setter +public class IslandMap { + private final EntitiesFactory entitiesFactory; + private final SimulationSettings settings; + private Location[][] locations; + + public IslandMap(SimulationSettings settings) { + this.entitiesFactory = new EntitiesFactory(); + this.settings = settings; + } + + public void init() { + this.locations = new Location[settings.getHeight()][settings.getWidth()]; + for (int coordY = 0; coordY < settings.getHeight(); coordY++) { + for (int coordX = 0; coordX < settings.getWidth(); coordX++) { + locations[coordY][coordX] = new Location(coordY, coordX); + } + } + } + + public void fillIsland(int maxCount) { + for (int coordY = 0; coordY < settings.getHeight(); coordY++) { + for (int coordX = 0; coordX < settings.getWidth(); coordX++) { + for (int i = 0; i < maxCount; i++) { + while (true) { + Entity entity = getRandom(); + var count = locations[coordY][coordX].getEntities().stream() + .filter(entityOnLocation -> entityOnLocation.equals(entity)) + .count(); + if (count < entity.getMaxCountOnLocation()) { + locations[coordY][coordX].addEntity(entity); + break; + } + } + } + } + } + } + + private Entity getRandom() { + var entityTypes = EntityType.values(); + var entityType = entityTypes[ThreadLocalRandom.current().nextInt(entityTypes.length)]; + return entitiesFactory.createEntity(entityType); + } + + public Runnable plantGrow() { + return () -> { + while (true) { + var coordY = ThreadLocalRandom.current().nextInt(getHeight()); + var coordX = ThreadLocalRandom.current().nextInt(getWidth()); + var herb = entitiesFactory.createEntity(EntityType.PLANT); + Location location = locations[coordY][coordX]; + if (location.getPlants().size() + Constant.PLANT_COUNT_TO_ADD >= herb.getMaxCountOnLocation()) { + continue; + } + for (int i = 0; i <= Constant.PLANT_COUNT_TO_ADD; i++) { + location.addEntity(herb); + } + break; + } + }; + } + + public int getHeight() { + return settings.getHeight(); + } + + public int getWidth() { + return settings.getWidth(); + } +} diff --git a/src/efanov/island/Location.java b/src/efanov/island/Location.java new file mode 100644 index 0000000..9b90527 --- /dev/null +++ b/src/efanov/island/Location.java @@ -0,0 +1,70 @@ +package efanov.island; + +import efanov.entities.Entity; +import efanov.entities.animals.Animal; +import efanov.entities.plants.Herb; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Getter +public class Location { + private final int x; + private final int y; + private final List entities; + private final Map entitiesCountStatistic; + + public Location(int y, int x) { + this.x = x; + this.y = y; + this.entities = new ArrayList<>(); + this.entitiesCountStatistic = new HashMap<>(); + } + + public void addEntity(Entity entity) { + entities.add(entity); + addToStatistic(entity); + } + + public void removeEntity(Entity entity) { + entities.remove(entity); + removeFromStatistic(entity); + } + + public List getAnimals() { + return entities.stream() + .filter(e -> e instanceof Animal) + .map(e -> (Animal) e) + .toList(); + } + + public List getPlants() { + return entities.stream() + .filter(e -> e instanceof Herb) + .map(e -> (Herb) e) + .toList(); + } + + private void addToStatistic(Entity entity) { + var entityAsString = getSimpleName(entity); + entitiesCountStatistic.merge(entityAsString, 1, (oldValue, newValue) -> oldValue + 1); + } + + private void removeFromStatistic(Entity entity) { + var entityAsString = getSimpleName(entity); + entitiesCountStatistic.merge(entityAsString, 1, (oldValue, newValue) -> { + int tempCount = oldValue - 1; + if (tempCount <= 0) { + return null; + } + return tempCount; + }); + } + + private String getSimpleName(Entity entity) { + return entity.getClass().getSimpleName(); + } +} diff --git a/src/efanov/island/service/ActionService.java b/src/efanov/island/service/ActionService.java new file mode 100644 index 0000000..f7ade38 --- /dev/null +++ b/src/efanov/island/service/ActionService.java @@ -0,0 +1,16 @@ +package efanov.island.service; + +import efanov.entities.animals.Animal; +import efanov.island.IslandMap; +import efanov.island.Location; + +public interface ActionService { + void doMove(Animal animal, Location location, IslandMap map); + + void doEat(Animal animal, Location location); + + void doReproduce(Animal animal, Location location); + + void doSleep(Animal animal); + +} diff --git a/src/efanov/island/service/StepService.java b/src/efanov/island/service/StepService.java new file mode 100644 index 0000000..96aaea2 --- /dev/null +++ b/src/efanov/island/service/StepService.java @@ -0,0 +1,15 @@ +package efanov.island.service; + +import efanov.entities.animals.Animal; +import efanov.island.IslandMap; +import efanov.island.Location; + +public interface StepService { + Location stepUp(Animal animal, Location location, IslandMap map); + + Location stepDown(Animal animal, Location location, IslandMap map); + + Location stepLeft(Animal animal, Location location, IslandMap map); + + Location stepRight(Animal animal, Location location, IslandMap map); +} diff --git a/src/efanov/island/service/impl/ActionServiceImpl.java b/src/efanov/island/service/impl/ActionServiceImpl.java new file mode 100644 index 0000000..f0be39a --- /dev/null +++ b/src/efanov/island/service/impl/ActionServiceImpl.java @@ -0,0 +1,120 @@ +package efanov.island.service.impl; + +import efanov.entities.animals.Animal; +import efanov.entities.animals.Direction; +import efanov.entities.animals.EatingMap; +import efanov.island.IslandMap; +import efanov.island.Location; +import efanov.island.service.ActionService; +import efanov.simulation.SimulationSettings; + +import java.util.Comparator; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Function; + +public class ActionServiceImpl implements ActionService { + private final StepServiceImpl stepService; + private final EatingMap eatingMap; + private final SimulationSettings settings; + + public ActionServiceImpl(StepServiceImpl stepService, EatingMap eatingMap, SimulationSettings settings) { + this.stepService = stepService; + this.eatingMap = eatingMap; + this.settings = settings; + + } + + @Override + public void doMove(Animal animal, Location location, IslandMap map) { + var stepsCount = ThreadLocalRandom.current().nextInt(animal.getSpeed() + 1); + while (stepsCount > 0) { + Direction direction = animal.chooseDirection(); + switch (direction) { + case UP -> location = stepService.stepUp(animal, location, map); + case DOWN -> location = stepService.stepDown(animal, location, map); + case LEFT -> location = stepService.stepLeft(animal, location, map); + case RIGHT -> location = stepService.stepRight(animal, location, map); + } + stepsCount--; + } + } + + @Override + public void doEat(Animal animal, Location location) { + var entities = location.getEntities() + .stream() + .map(e -> e.getClass().getSimpleName()) + .toList(); + var eatingProbability = eatingMap.getEatingProbability(); + + Map personalProbability = eatingProbability.get(animal.getClass().getSimpleName()); + + var sumOfProbability = personalProbability.entrySet() + .stream() + .filter(animalProbability -> animalProbability.getValue() > 0) + .filter(animalProbability -> entities.contains(animalProbability.getKey())) + .mapToInt(Map.Entry::getValue) + .sum(); + if (sumOfProbability == 0) { + doSleep(animal); + return; + } + + var filteredPersonalProbability = personalProbability.entrySet() + .stream() + .filter(animalProbability -> animalProbability.getValue() > 0).map(Map.Entry::getKey) + .sorted(Comparator.comparing(Function.identity())) + .toList(); + + var temp = ThreadLocalRandom.current().nextInt(1, sumOfProbability); + + for (String name : filteredPersonalProbability) { + if (entities.contains(name)) { + temp -= personalProbability.get(name); + if (temp <= 0) { + var food = location.getEntities().stream() + .filter(e -> e.getClass().getSimpleName().equals(name)).findAny(); + animal.eat(food.get()); + location.removeEntity(food.get()); + + } + } + } + } + + @Override + public void doReproduce(Animal animal, Location location) { + var getAnimalCountOnCage = Optional.ofNullable(location.getEntitiesCountStatistic().get(animal.getClass().getSimpleName())).orElse(0); + if (getAnimalCountOnCage >= animal.getMaxCountOnLocation()) { + return; + } + if (checkReproducePartner(animal, location)) { + location.addEntity(animal.reproduce()); + } + } + + @Override + public void doSleep(Animal animal) { + increaseHealth(animal); + } + + public void reduceHealth(Animal animal) { + double healthScale = animal.getHealth() - ((animal.getSaturation() * settings.getReduceHealth()) / 100); + animal.setHealth(healthScale); + } + + private boolean checkReproducePartner(Animal animal, Location location) { + var duplicate = location.getAnimals().stream() + .map(el -> el.getClass().getSimpleName()) + .filter(el -> el.equals(animal.getClass().getSimpleName())) + .count(); + return duplicate > 1; + } + + private void increaseHealth(Animal animal) { + double healthScale = animal.getHealth() + ((animal.getSaturation() * settings.getIncreaseHealth()) / 100); + animal.setHealth(healthScale); + } +} diff --git a/src/efanov/island/service/impl/StepServiceImpl.java b/src/efanov/island/service/impl/StepServiceImpl.java new file mode 100644 index 0000000..3e91936 --- /dev/null +++ b/src/efanov/island/service/impl/StepServiceImpl.java @@ -0,0 +1,76 @@ +package efanov.island.service.impl; + +import efanov.entities.animals.Animal; +import efanov.island.IslandMap; +import efanov.island.Location; +import efanov.island.service.StepService; + +import java.util.Optional; + +public class StepServiceImpl implements StepService { + public Location stepUp(Animal animal, Location location, IslandMap map) { + int currentX = location.getX(); + int currentY = location.getY(); + if (currentY > 0) { + Location newLocation = map.getLocations()[currentY - 1][currentX]; + if (cantStep(animal, newLocation)) { + return location; + } + newLocation.addEntity(animal); + location.removeEntity(animal); + return newLocation; + } + return location; + } + + public Location stepDown(Animal animal, Location location, IslandMap map) { + int currentX = location.getX(); + int currentY = location.getY(); + if (currentY < map.getHeight() - 1) { + Location newLocation = map.getLocations()[currentY + 1][currentX]; + if (cantStep(animal, newLocation)) { + return location; + } + newLocation.addEntity(animal); + location.removeEntity(animal); + return newLocation; + } + return location; + } + + public Location stepLeft(Animal animal, Location location, IslandMap map) { + int currentX = location.getX(); + int currentY = location.getY(); + if (currentX > 0) { + Location newLocation = map.getLocations()[currentY][currentX - 1]; + if (cantStep(animal, newLocation)) { + return location; + } + newLocation.addEntity(animal); + location.removeEntity(animal); + return newLocation; + } + return location; + } + + public Location stepRight(Animal animal, Location location, IslandMap map) { + int currentX = location.getX(); + int currentY = location.getY(); + if (currentX < map.getWidth() - 1) { + Location newLocation = map.getLocations()[currentY][currentX + 1]; + if (cantStep(animal, newLocation)) { + return location; + } + newLocation.addEntity(animal); + location.removeEntity(animal); + return newLocation; + } + return location; + } + + public boolean cantStep(Animal animal, Location newLocation) { + var animalCount = Optional.ofNullable(newLocation.getEntitiesCountStatistic().get(animal.getClass().getSimpleName())) + .orElse(0); + return animalCount >= animal.getMaxCountOnLocation(); + } +} diff --git a/src/efanov/properties/processing/Processor.java b/src/efanov/properties/processing/Processor.java new file mode 100644 index 0000000..1cae66a --- /dev/null +++ b/src/efanov/properties/processing/Processor.java @@ -0,0 +1,63 @@ +package efanov.properties.processing; + +import efanov.constants.Constant; + +import java.io.FileReader; +import java.io.IOException; +import java.util.Properties; + +public class Processor { + + public String getEmojiFromFile(String param) { + try (FileReader reader = new FileReader(Constant.ANIMAL_SETTINGS_PROPERTIES)) { + Properties properties = new Properties(); + properties.load(reader); + return properties.getProperty(param.toLowerCase() + ".emoji"); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + + public double getWeightFromFile(String param) { + try (FileReader reader = new FileReader(Constant.ANIMAL_SETTINGS_PROPERTIES)) { + Properties properties = new Properties(); + properties.load(reader); + return Double.parseDouble(properties.getProperty(param.toLowerCase() + ".weight")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public int getMaxCountFromFile(String param) { + try (FileReader reader = new FileReader(Constant.ANIMAL_SETTINGS_PROPERTIES)) { + Properties properties = new Properties(); + properties.load(reader); + return Integer.parseInt(properties.getProperty(param.toLowerCase() + ".maxCount")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public int getSpeedFromFile(String param) { + try (FileReader reader = new FileReader(Constant.ANIMAL_SETTINGS_PROPERTIES)) { + Properties properties = new Properties(); + properties.load(reader); + return Integer.parseInt(properties.getProperty(param.toLowerCase() + ".speed")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public double getSaturationFromFile(String param) { + try (FileReader reader = new FileReader(Constant.ANIMAL_SETTINGS_PROPERTIES)) { + Properties properties = new Properties(); + properties.load(reader); + return Double.parseDouble(properties.getProperty(param.toLowerCase() + ".saturation")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + +} diff --git a/src/efanov/resources/animal-settings.properties b/src/efanov/resources/animal-settings.properties new file mode 100644 index 0000000..4f0082b --- /dev/null +++ b/src/efanov/resources/animal-settings.properties @@ -0,0 +1,94 @@ +bear.emoji=\ud83d\udc3b +bear.weight=500 +bear.maxCount=5 +bear.speed=2 +bear.saturation=80 + +wolf.emoji=\ud83d\udc3a +wolf.weight=50 +wolf.maxCount=30 +wolf.speed=3 +wolf.saturation=8 + +boa.emoji=\ud83d\udc0d +boa.weight=15 +boa.maxCount=30 +boa.speed=1 +boa.saturation=3 + +fox.emoji=\uD83E\uDD8A +fox.weight=8 +fox.maxCount=30 +fox.speed=2 +fox.saturation=2 + +eagle.emoji=\uD83E\uDD85 +eagle.weight=6 +eagle.maxCount=20 +eagle.speed=3 +eagle.saturation=1 + +horse.emoji=\uD83D\uDC34 +horse.weight=400 +horse.maxCount=20 +horse.speed=4 +horse.saturation=60 + +deer.emoji=\uD83E\uDD8C +deer.weight=300 +deer.maxCount=20 +deer.speed=4 +deer.saturation=50 + +rabbit.emoji=\uD83D\uDC30 +rabbit.weight=2 +rabbit.maxCount=150 +rabbit.speed=2 +rabbit.saturation=0.45 + +mouse.emoji=\uD83D\uDC2D +mouse.weight=0.05 +mouse.maxCount=500 +mouse.speed=1 +mouse.saturation=0.01 + +goat.emoji=\uD83D\uDC10 +goat.weight=60 +goat.maxCount=140 +goat.speed=3 +goat.saturation=10 + +sheep.emoji=\uD83D\uDC11 +sheep.weight=70 +sheep.maxCount=140 +sheep.speed=3 +sheep.saturation=15 + +wildboar.emoji=\uD83D\uDC17 +wildboar.weight=400 +wildboar.maxCount=50 +wildboar.speed=2 +wildboar.saturation=50 + +buffalo.emoji=\uD83D\uDC02 +buffalo.weight=700 +buffalo.maxCount=10 +buffalo.speed=3 +buffalo.saturation=100 + +duck.emoji=\uD83E\uDD86 +duck.weight=1 +duck.maxCount=200 +duck.speed=4 +duck.saturation=0.15 + +caterpillar.emoji=\uD83D\uDC1B +caterpillar.weight=0.01 +caterpillar.maxCount=1000 +caterpillar.speed=0 +caterpillar.saturation=0 + +plant.emoji=\uD83C\uDF3F +plant.weight=1 +plant.maxCount=200 + diff --git a/src/efanov/resources/probability.yaml b/src/efanov/resources/probability.yaml new file mode 100644 index 0000000..4c3e1df --- /dev/null +++ b/src/efanov/resources/probability.yaml @@ -0,0 +1,254 @@ +Wolf: + Boa: 0 + Fox: 0 + Bear: 0 + Eagle: 0 + Horse: 10 + Deer: 15 + Rabbit: 60 + Mouse: 80 + Goat: 60 + Sheep: 70 + WildBoar: 15 + Buffalo: 10 + Duck: 40 + Caterpillar: 0 + Herb: 0 + +Boa: + Wolf: 0 + Fox: 15 + Bear: 0 + Eagle: 0 + Horse: 0 + Deer: 0 + Rabbit: 20 + Mouse: 40 + Goat: 0 + Sheep: 0 + WildBoar: 0 + Buffalo: 0 + Duck: 10 + Caterpillar: 0 + Herb: 0 + +Fox: + Wolf: 0 + Boa: 0 + Bear: 0 + Eagle: 0 + Horse: 0 + Deer: 0 + Rabbit: 70 + Mouse: 90 + Goat: 0 + Sheep: 0 + WildBoar: 0 + Buffalo: 0 + Duck: 60 + Caterpillar: 40 + Herb: 0 + +Bear: + Wolf: 0 + Boa: 80 + Fox: 0 + Eagle: 0 + Horse: 40 + Deer: 80 + Rabbit: 80 + Mouse: 90 + Goat: 70 + Sheep: 70 + WildBoar: 50 + Buffalo: 20 + Duck: 10 + Caterpillar: 0 + Herb: 0 + +Eagle: + Wolf: 0 + Boa: 0 + Fox: 10 + Bear: 0 + Horse: 0 + Deer: 0 + Rabbit: 90 + Mouse: 90 + Goat: 0 + Sheep: 0 + WildBoar: 0 + Buffalo: 0 + Duck: 80 + Caterpillar: 0 + Herb: 0 + +Horse: + Wolf: 0 + Boa: 0 + Fox: 0 + Bear: 0 + Eagle: 0 + Deer: 0 + Rabbit: 0 + Mouse: 0 + Goat: 0 + Sheep: 0 + WildBoar: 0 + Buffalo: 0 + Duck: 0 + Caterpillar: 0 + Herb: 100 + +Deer: + Wolf: 0 + Boa: 0 + Fox: 0 + Bear: 0 + Eagle: 0 + Horse: 0 + Rabbit: 0 + Mouse: 0 + Goat: 0 + Sheep: 0 + WildBoar: 0 + Buffalo: 0 + Duck: 0 + Caterpillar: 0 + Herb: 100 + +Rabbit: + Wolf: 0 + Boa: 0 + Fox: 0 + Bear: 0 + Eagle: 0 + Horse: 0 + Deer: 0 + Mouse: 0 + Goat: 0 + Sheep: 0 + WildBoar: 0 + Buffalo: 0 + Duck: 0 + Caterpillar: 0 + Herb: 100 + +Mouse: + Wolf: 0 + Boa: 0 + Fox: 0 + Bear: 0 + Eagle: 0 + Horse: 0 + Deer: 0 + Rabbit: 0 + Goat: 0 + Sheep: 0 + WildBoar: 0 + Buffalo: 0 + Duck: 0 + Caterpillar: 90 + Herb: 100 + +Goat: + Wolf: 0 + Boa: 0 + Fox: 0 + Bear: 0 + Eagle: 0 + Horse: 0 + Deer: 0 + Rabbit: 0 + Mouse: 0 + Sheep: 0 + WildBoar: 0 + Buffalo: 0 + Duck: 0 + Caterpillar: 0 + Herb: 100 + +Sheep: + Wolf: 0 + Boa: 0 + Fox: 0 + Bear: 0 + Eagle: 0 + Horse: 0 + Deer: 0 + Rabbit: 0 + Mouse: 0 + Goat: 0 + WildBoar: 0 + Buffalo: 0 + Duck: 0 + Caterpillar: 0 + Herb: 100 + +WildBoar: + Wolf: 0 + Boa: 0 + Fox: 0 + Bear: 0 + Eagle: 0 + Horse: 0 + Deer: 0 + Rabbit: 0 + Mouse: 50 + Goat: 0 + Sheep: 0 + Buffalo: 0 + Duck: 0 + Caterpillar: 90 + Herb: 100 + +Buffalo: + Wolf: 0 + Boa: 0 + Fox: 0 + Bear: 0 + Eagle: 0 + Horse: 0 + Deer: 0 + Rabbit: 0 + Mouse: 0 + Goat: 0 + WildBoar: 0 + Sheep: 0 + Duck: 0 + Caterpillar: 0 + Herb: 100 + +Duck: + Wolf: 0 + Boa: 0 + Fox: 0 + Bear: 0 + Eagle: 0 + Horse: 0 + Deer: 0 + Rabbit: 0 + Mouse: 0 + Goat: 0 + WildBoar: 0 + Sheep: 0 + Buffalo: 0 + Caterpillar: 90 + Herb: 100 + +Caterpillar: + Wolf: 0 + Boa: 0 + Fox: 0 + Bear: 0 + Eagle: 0 + Horse: 0 + Deer: 0 + Rabbit: 0 + Mouse: 0 + Goat: 0 + WildBoar: 0 + Sheep: 0 + Buffalo: 0 + Duck: 0 + Herb: 100 \ No newline at end of file diff --git a/src/efanov/simulation/SimulationSettings.java b/src/efanov/simulation/SimulationSettings.java new file mode 100644 index 0000000..a578140 --- /dev/null +++ b/src/efanov/simulation/SimulationSettings.java @@ -0,0 +1,19 @@ +package efanov.simulation; + +import lombok.Getter; +import lombok.Setter; + + +@Setter +@Getter +public class SimulationSettings { + public static final int INITIAL_GROW_TIME = 100; + private int width = 100; + private int height = 20; + private int entityCountOnCage = 10; + private double reduceHealth = 15; + private double increaseHealth = 15; + private int plantGrowTime = 200; + private int lifeCycleTact = 200; + +} diff --git a/src/efanov/simulation/SimulationStarter.java b/src/efanov/simulation/SimulationStarter.java new file mode 100644 index 0000000..0afbf23 --- /dev/null +++ b/src/efanov/simulation/SimulationStarter.java @@ -0,0 +1,41 @@ +package efanov.simulation; + + +import efanov.dialog.UserDialog; +import efanov.entities.animals.EatingMap; +import efanov.island.IslandController; +import efanov.island.IslandMap; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class SimulationStarter { + public static ScheduledExecutorService executorService; + private final SimulationSettings simulationSettings; + private final IslandController controller; + private final IslandMap islandMap; + private final UserDialog dialog; + + public SimulationStarter() { + this.dialog = new UserDialog(); + this.simulationSettings = new SimulationSettings(); + EatingMap eatingMap = new EatingMap(); + this.islandMap = new IslandMap(simulationSettings); + this.controller = new IslandController(islandMap, eatingMap, simulationSettings); + executorService = Executors.newSingleThreadScheduledExecutor(); + } + + public void start() { + dialog.showMenuAndChangeParameters(simulationSettings); + controller.getMap().init(); + controller.getMap().fillIsland(simulationSettings.getEntityCountOnCage()); + executorService.scheduleWithFixedDelay(islandMap.plantGrow(), + SimulationSettings.INITIAL_GROW_TIME, + simulationSettings.getPlantGrowTime(), + TimeUnit.MILLISECONDS); + controller.startLife(); + + } + +} diff --git a/src/efanov/statistic/Statistic.java b/src/efanov/statistic/Statistic.java new file mode 100644 index 0000000..c06c6dd --- /dev/null +++ b/src/efanov/statistic/Statistic.java @@ -0,0 +1,40 @@ +package efanov.statistic; + +import efanov.entities.Entity; +import efanov.island.IslandMap; +import efanov.island.Location; + +import java.text.MessageFormat; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class Statistic { + public static final String EMOJI_KEY_VALUE = "{0} - {1}"; + private final IslandMap islandMap; + + public Statistic(IslandMap islandMap) { + this.islandMap = islandMap; + } + + public Map getAllEntitiesStatistic() { + Map allEntitiesStatistic = new ConcurrentHashMap<>(); + for (int y = 0; y < islandMap.getHeight(); y++) { + for (int x = 0; x < islandMap.getWidth(); x++) { + Location location = islandMap.getLocations()[y][x]; + List entities = location.getEntities(); + for (Entity entity : entities) { + String emoji = entity.getEmoji(); + allEntitiesStatistic.merge(emoji, 1, (oldValue, newValue) -> oldValue + 1); + } + } + } + return allEntitiesStatistic; + } + + public void printStatistic(Map allEntitiesStatistic) { + allEntitiesStatistic.forEach((key, value) -> System.out.println(MessageFormat.format(EMOJI_KEY_VALUE, key, value))); + System.out.println("\n"); + } + +}