-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added: Animal, AnimalTree, Genotype, GenotypeTests. #3
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package agh.ics.oop.model; | ||
|
||
import agh.ics.oop.model.movement.MapDirection; | ||
import agh.ics.oop.model.movement.Vector2d; | ||
import agh.ics.oop.model.util.AnimalTree; | ||
import agh.ics.oop.model.util.RandomInteger; | ||
import agh.ics.oop.model.worldMaps.Globe; | ||
|
||
public class Animal { | ||
private final Globe globe; | ||
private Vector2d position; | ||
private MapDirection direction; | ||
private Genotype genotype; | ||
private int energy; | ||
private int daysLived=0; | ||
private int eatenPlants=0; | ||
private int dayOfDeath; | ||
private int currentGeneIndex; | ||
private AnimalTree animalTree; | ||
|
||
|
||
public Animal(Globe globe) { | ||
this.globe=globe; | ||
this.position=new Vector2d(RandomInteger.getRandomInt(globe.getWidth()) + 1, RandomInteger.getRandomInt(globe.getHeight()) + 1); | ||
this.direction=MapDirection.values()[RandomInteger.getRandomInt(8)]; | ||
this.energy=globe.getAnimalsStartingEnergy(); | ||
this.genotype=new Genotype(globe.getGenotypeLength()); | ||
this.currentGeneIndex=RandomInteger.getRandomInt(globe.getGenotypeLength()); | ||
this.animalTree=new AnimalTree(this); | ||
} | ||
|
||
public Animal(Animal mother, Animal father) { | ||
this.globe=mother.getGlobe(); | ||
this.position=mother.getPosition(); | ||
this.direction=MapDirection.values()[RandomInteger.getRandomInt(8)]; | ||
this.energy=globe.getEnergyUsedToReproduce()*2; | ||
this.genotype=new Genotype(mother,father); | ||
this.currentGeneIndex=RandomInteger.getRandomInt(globe.getGenotypeLength()); | ||
mother.useEnergy(globe.getEnergyUsedToReproduce()); | ||
mother.animalTree.addChild(this.animalTree); | ||
father.useEnergy(globe.getEnergyUsedToReproduce()); | ||
father.animalTree.addChild(this.animalTree); | ||
} | ||
|
||
private void useEnergy(int energyUsedToReproduce) { | ||
this.energy-=energyUsedToReproduce; | ||
} | ||
public void nextGene(){ | ||
this.currentGeneIndex=(this.currentGeneIndex+1)%globe.getGenotypeLength(); | ||
} | ||
|
||
public boolean canReproduce(){ | ||
return this.energy>=globe.getMinEnergyToReproduce(); | ||
} | ||
|
||
public int getNumberOfChildren(){ | ||
return animalTree.getChildrenCount(); | ||
} | ||
public int getNumberOfDescendants(){ | ||
return animalTree.getDescendantsCount(); | ||
} | ||
|
||
public Vector2d getPosition() { | ||
return position; | ||
} | ||
|
||
public int getEnergy() { | ||
return energy; | ||
} | ||
|
||
public Genotype getGenotype() { | ||
return genotype; | ||
} | ||
|
||
public Globe getGlobe() { | ||
return globe; | ||
} | ||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package agh.ics.oop.model; | ||
|
||
import agh.ics.oop.model.util.RandomInteger; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class Genotype { | ||
private List<Integer> genes = new ArrayList<>(); | ||
|
||
public Genotype(int genotypeLength) { | ||
for (int i = 0; i < genotypeLength; i++) { | ||
this.genes.add(RandomInteger.getRandomInt(8)); | ||
} | ||
} | ||
|
||
public Genotype(Animal mother, Animal father) { | ||
validateParents(mother, father); | ||
|
||
int divisionPoint = (int) (((double) mother.getEnergy() / (father.getEnergy() + mother.getEnergy())) * mother.getGenotype().genes.size()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You access genotype length twice in this constructor. Imo it would be better to either add an attribute for it or create a local variable to make code more readable. |
||
boolean chooseLeftSide = RandomInteger.getRandomBoolean(); | ||
|
||
for (int i = 0; i < mother.getGlobe().getGenotypeLength(); i++) { | ||
if ((chooseLeftSide && i < divisionPoint) || (!chooseLeftSide && i >= divisionPoint)) { | ||
genes.add(mother.getGenotype().genes.get(i)); | ||
} else { | ||
genes.add(father.getGenotype().genes.get(i)); | ||
} | ||
} | ||
|
||
mutate(mother.getGlobe().getMinNumberOfMutations(), mother.getGlobe().getMaxNumberOfMutations()); | ||
} | ||
|
||
private void validateParents(Animal mother, Animal father) { | ||
if (mother == null || father == null) { | ||
throw new IllegalArgumentException("Both mother and father must not be null"); | ||
} | ||
} | ||
|
||
private void mutate(int minNumberOfMutation, int maxNumberOfMutation) { | ||
int numberOfMutations = RandomInteger.getRandomInt(minNumberOfMutation, maxNumberOfMutation); | ||
|
||
for (int i = 0; i < numberOfMutations; i++) { | ||
switchGenes(RandomInteger.getRandomInt(genes.size()), RandomInteger.getRandomInt(genes.size())); //opcja symulacji z podmianką | ||
randomGene(RandomInteger.getRandomInt(genes.size())); //opcja symulacji pełna losowość | ||
} | ||
} | ||
|
||
public void switchGenes(int firstGeneIndex, int secondGeneIndex) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can use Collections.swap() to do this. |
||
int temp = this.genes.get(firstGeneIndex); | ||
this.genes.set(firstGeneIndex, this.genes.get(secondGeneIndex)); | ||
this.genes.set(secondGeneIndex, temp); | ||
} | ||
public void randomGene(int geneIndex) { | ||
genes.set(geneIndex, RandomInteger.getRandomInt(8)); | ||
} | ||
|
||
public String toString() { | ||
StringBuilder genotypeToString = new StringBuilder(); | ||
for (Integer gene : genes) { | ||
genotypeToString.append(gene.toString()); | ||
} | ||
return genotypeToString.toString(); | ||
} | ||
|
||
public Integer getCurrentGene(int currentGeneIndex) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case it is better to use int instead of Integer. |
||
return genes.get(currentGeneIndex); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package agh.ics.oop.model.util; | ||
|
||
import agh.ics.oop.model.Animal; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class AnimalTree { | ||
private final List<AnimalTree> children; | ||
private final Animal animal; | ||
|
||
public AnimalTree(Animal animal) { | ||
this.animal = animal; | ||
this.children = new ArrayList<>(); | ||
} | ||
|
||
public void addChild(AnimalTree child) { | ||
children.add(child); | ||
} | ||
public int getChildrenCount() { | ||
return children.size(); | ||
} | ||
|
||
public int getDescendantsCount() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My idea is that once you choose a specific animal, you get all of its children and add an observer to them that updates out count whenever a new animal is born (from an observed animal). Not sure if it is the correct solution. |
||
int descendantsCount = children.size(); // liczba bezpośrednich potomków | ||
|
||
for (AnimalTree child : children) { | ||
descendantsCount += child.getDescendantsCount(); // rekurencyjnie dodaj potomków potomka | ||
} | ||
|
||
return descendantsCount; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package agh.ics.oop.model.util; | ||
|
||
import java.util.Random; | ||
|
||
public class RandomInteger { | ||
private static final Random random = new Random(); | ||
|
||
public static int getRandomInt(int bound) { | ||
return random.nextInt(bound); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is exclusive but it's overload with 2 variables is inclusive. It will lead to confusion and mistakes in the future. |
||
} | ||
|
||
public static int getRandomInt(int min, int max) { | ||
if (min >= max) { | ||
throw new IllegalArgumentException("Min value must be less than max value"); | ||
} | ||
return random.nextInt(max - min + 1) + min; | ||
} | ||
|
||
public static boolean getRandomBoolean() { | ||
return random.nextBoolean(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,17 @@ | ||
package agh.ics.oop.model.worldMaps; | ||
|
||
import agh.ics.oop.model.Animal; | ||
import agh.ics.oop.model.Genotype; | ||
import agh.ics.oop.model.movement.Vector2d; | ||
|
||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Random; | ||
import java.util.*; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would keep the named imports because they are more readable. |
||
|
||
public class Globe { | ||
private final int width; | ||
private final int height; | ||
|
||
private static final Vector2d lowerLeftBoundary = new Vector2d(0, 0); | ||
|
||
private final Vector2d upperRightBoundary;; | ||
private int plantCount; | ||
private final int energyPerPlant; | ||
|
@@ -21,7 +22,9 @@ public class Globe { | |
private final int energyUsedToReproduce; | ||
private final int minNumberOfMutations; | ||
private final int maxNumberOfMutations; | ||
private final int genomeLength; | ||
private final int genotypeLength; | ||
private final Map<Genotype, Integer> genotypeCount = new HashMap<>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are using a map but there is neither equals nor hash in Genotype |
||
|
||
|
||
private Map<Vector2d, List<Animal>> animals = new HashMap<Vector2d, ArrayList<Animal>>(); | ||
private Map<Vector2d, Plant> plants = new HashMap<Vector2d, Plant>(); | ||
|
@@ -34,7 +37,7 @@ private void checkIfNotNegative(int value) throws IllegalArgumentException{ | |
|
||
public Globe(int width, int height, int energyPerPlant, int plantsPerDay, int numberOfStartingAnimals, | ||
int animalsStartingEnergy,int minEnergyToReproduce, int energyUsedToReproduce, | ||
int minNumberOfMutations, int maxNumberOfMutations, int genomeLength) throws IllegalArgumentException { | ||
int minNumberOfMutations, int maxNumberOfMutations, int genotypeLength) throws IllegalArgumentException { | ||
|
||
if (width < 1) throw new IllegalArgumentException("Map's width must be positive"); | ||
this.width = width; | ||
|
@@ -51,12 +54,6 @@ public Globe(int width, int height, int energyPerPlant, int plantsPerDay, int nu | |
checkIfNotNegative(plantsPerDay); | ||
this.plantsPerDay = plantsPerDay; | ||
|
||
checkIfNotNegative(numberOfStartingAnimals); | ||
Random random = new Random(); | ||
for (int i = 0; i < numberOfStartingAnimals; i++) { | ||
place(new Animal(new Vector2d(random.nextInt(width) + 1, random.nextInt(height) + 1))); | ||
} | ||
|
||
checkIfNotNegative(animalsStartingEnergy); | ||
this.animalsStartingEnergy = animalsStartingEnergy; | ||
|
||
|
@@ -72,14 +69,21 @@ public Globe(int width, int height, int energyPerPlant, int plantsPerDay, int nu | |
throw new IllegalArgumentException("Maximum number of mutations must be greater than minimal"); | ||
} | ||
this.maxNumberOfMutations = maxNumberOfMutations; | ||
if (genomeLength < 1) throw new IllegalArgumentException("Genome length must be positive"); | ||
this.genomeLength = genomeLength; | ||
if (genotypeLength < 1) throw new IllegalArgumentException("Genotype length must be positive"); | ||
this.genotypeLength = genotypeLength; | ||
|
||
checkIfNotNegative(numberOfStartingAnimals); | ||
for (int i = 0; i < numberOfStartingAnimals; i++) { | ||
place(new Animal(this)); | ||
} | ||
} | ||
|
||
public boolean canMoveTo(Vector2d position) { | ||
return position.precedes(upperRightBoundary) && position.follows(lowerLeftBoundary); | ||
} | ||
|
||
|
||
|
||
public void place(Animal animal) { | ||
Vector2d position = animal.getPosition(); | ||
numberOfAnimals++; | ||
|
@@ -88,5 +92,78 @@ public void place(Animal animal) { | |
animalsAtThisPosition.add(animal); | ||
|
||
animals.put(animalsAtThisPosition); | ||
updateGenotypeCount(animal); | ||
} | ||
public void remove(Animal animal) { | ||
Vector2d position = animal.getPosition(); | ||
List<Animal> animalsAtThisPosition = animals.get(position); | ||
|
||
if (animalsAtThisPosition != null) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would replace it with try catch because we expect that position in animal is kept correctly. |
||
animalsAtThisPosition.remove(animal); | ||
if (animalsAtThisPosition.isEmpty()) { | ||
animals.remove(position); | ||
} | ||
} | ||
|
||
removeGenotype(animal); | ||
} | ||
private void updateGenotypeCount(Animal animal) { | ||
Genotype animalGenotype = animal.getGenotype(); | ||
|
||
genotypeCount.put(animalGenotype, genotypeCount.getOrDefault(animalGenotype, 0) + 1); | ||
} | ||
private void removeGenotype(Animal animal) { | ||
Genotype animalGenotype = animal.getGenotype(); | ||
|
||
genotypeCount.put(animalGenotype, genotypeCount.getOrDefault(animalGenotype, 1) - 1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above we expect that genotypes are also tracked correctly so no need for getOrDefault(). |
||
|
||
if (genotypeCount.get(animalGenotype) == 0) { | ||
genotypeCount.remove(animalGenotype); | ||
} | ||
} | ||
public Genotype findMostCommonGenotype() { //niedynamiczne- do poprawy | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To me it sounds like heap. |
||
Genotype mostCommonGenotype = null; | ||
int maxCount = 0; | ||
|
||
for (Map.Entry<Genotype, Integer> entry : genotypeCount.entrySet()) { | ||
if (entry.getValue() > maxCount) { | ||
mostCommonGenotype = entry.getKey(); | ||
maxCount = entry.getValue(); | ||
} | ||
} | ||
|
||
return mostCommonGenotype; | ||
} | ||
public int getAnimalsStartingEnergy() { | ||
return animalsStartingEnergy; | ||
} | ||
|
||
public int getGenotypeLength() { | ||
return genotypeLength; | ||
} | ||
|
||
public int getEnergyUsedToReproduce() { | ||
return energyUsedToReproduce; | ||
} | ||
|
||
public int getMinNumberOfMutations() { | ||
return minNumberOfMutations; | ||
} | ||
|
||
public int getMaxNumberOfMutations() { | ||
return maxNumberOfMutations; | ||
} | ||
|
||
public int getMinEnergyToReproduce() { | ||
return minimalEnergyToReproduce; | ||
} | ||
|
||
public int getWidth() { | ||
return width; | ||
} | ||
|
||
public int getHeight() { | ||
return height; | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no reason to keep Globe in Animal.
You use it to: