Это для личного проекта, я пытаюсь создать симулятор выживания, используя динозавров в качестве агентов.
Основная часть симуляции - это цикл do: while, а внутри него do: while - 3 цикла for: каждый (не вложенный), который проходит через три разных списка ArrayList. Каждый ArrayList представляет собой массив объектов, каждый из которых выполняет разные функции.
Dinosaurs перемещаются по полю и могут съесть другие объекты Dinosaur, объекты Grass или объекты Water. Ни объекты Grass, ни объекты Water не исчезают после поедания, однако объекты Dinosaur удаляются из dinosaursArrayList, если они съедены. Вот мой соответствующий код:
for (Grass grass : grasses) {
System.out.println(grass);
}
//simulation loop
do {
for (Grass grass : grasses) {
grass.timeToGrow();
}
for (Water water : waters) {
}
for (Dinosaur dinosaur : dinosaurs) {
if (simulationLength % 3 == 0) { //dinosaurs lose 1 health and food every 3 turns
dinosaur.addFood(-1);
dinosaur.addWater(-1);
}
dinosaur.loseHealth();
dinosaur.move(dinosaurs, grasses, waters);
dinosaurs.add(dinosaur.timeToReproduce());
}
System.out.println("There are " + dinosaurs.size() + " dinosaurs left.");
simulationLength -= 1;
simulationTime += 1;
//TODO update GUI
} while (simulationLength > 0);
System.out.println("¡THE SIMULATION IS DONE!");
dinosaur.move() перемещает каждого динозавра в ArrayList динозавров. Если динозавр является хищником и приземляется на другого динозавра, он его съедает, а съеденный динозавр удаляется из списка. dinosaur.timeToReproduce() возвращает объект Dinosaur, если динозавр должен использовать пищу и сделать еще один Dinosaur (который добавляется в список Dinosaurs), и ноль, если нет.
public void move(ArrayList<Dinosaur> dinosaurs, ArrayList<Grass> grasses, ArrayList<Water> waters) {
Positioned food = getFoodSource(dinosaurs, grasses, waters);
try { //to find food
if (distanceTo(food) <= getSpeed()) { //can move to food in one turn
moveInRange(dinosaurs, grasses, waters);
} else { //food is out of reach
moveOutRange(dinosaurs, grasses, waters);
}
} catch (NullPointerException e) { //didn't find any food :(
//TODO make the dino search for alternate food source
//TODO make the dino wander if truly no food
//not yet moving
setxLoc(getxLoc());
setyLoc(getyLoc());
}
}
public void moveInRange(ArrayList<Dinosaur> dinosaurs, ArrayList<Grass> grasses, ArrayList<Water> waters) {
Positioned food = getFoodSource(dinosaurs, grasses, waters); //searching out target...
int xLocFood = food.getxLoc();
int yLocFood = food.getyLoc();
this.setxLoc(xLocFood);
this.setyLoc(yLocFood);
switch (food.getName()) {
case "grass":
//grass just got eaten. it now has to regrow
System.out.println(this + " just ate " + food + "!");
grasses.get(grasses.indexOf(food)).setGrowthStage(1);
this.addHealth(4);
this.addFood(4);
break;
case "water":
//you gots to do nothing. water doesn't go away
System.out.println(this + " just drank " + food + "!");
this.addHealth(2);
this.addWater(4);
break;
case "dinosaur":
//remove the dinosaur you just ate
System.out.println(this + " just tried to eat " + food + "!");
battle(dinosaurs.get(dinosaurs.indexOf(food)), dinosaurs);
break;
}
}
public void moveOutRange(ArrayList<Dinosaur> dinosaurs, ArrayList<Grass> grasses, ArrayList<Water> waters) {
Positioned food = getFoodSource(dinosaurs, grasses, waters); //searching out target...
//if there is not a valid food source found then the next 2 lines will throw a NullPointerException
int xLocFood = food.getxLoc();
int yLocFood = food.getyLoc();
int newXLoc;
int newYLoc;
double dx = xLocFood - getxLoc(); //total x distance to food
double dy = yLocFood - getyLoc(); //total y distance to food
double theta = Math.atan(dy / dx); //θ = arctan(dy/dx)
//rounding for accuracy
newXLoc = (int) Math.round(getSpeed() * Math.cos(theta)); //x=speed*cos(θ)
newYLoc = (int) Math.round(getSpeed() * Math.sin(theta)); //y=speed*sin(θ)
setxLoc(getxLoc() + newXLoc); //newX = x+moveX
setyLoc(getyLoc() + newYLoc); //newY = y+moveY
}
public void battle(Dinosaur dino, ArrayList<Dinosaur> dinosaurs) {
//attack vs defense
int dH = this.getAttack() - dino.getDefense();
if (dH > 0) { //dinosaur's attack is greater than the other guy's defense
System.out.println("\t" + dino + " just lost " + dH + " health!");
dino.addHealth(-dH);
this.addFood(dino.curFood / 2);
if (dino.getHealth() <= 0) {
System.out.println("\t" + dino + " was just eaten!");
for (int i = 0; i < dinosaurs.size(); i++) {
if (dinosaurs.get(i).equals(dino)) {
dinosaurs.remove(i);
}
}
}
} else if (dH < 0) { //defender has the big defense
System.out.println("\t" + this + " just lost " + dH + " health!");
this.addHealth(dH);
badDinos.add(dino);
}
}
Я видел кое-что о Iterator, это то, что мне следует использовать? Если да, то как они работают? Должен ли я просто создать новый ArrayList в move() / battle() и timeToReproduce(), а затем удалить материал после завершения цикла dinosaurs? Если да, то как мне убедиться, что динозавры, которых предполагалось съесть, действительно есть?
Я буду продолжать обновлять подробности по мере того, как люди их просят.
Зависит от того, чего вы хотите достичь. Т.е. что должно произойти, если ваш код добавляет динозавра в список динозавров, над которыми он сейчас работает? Должно ли это быть частью итерации или нет? Если предполагается, что модификации будут действовать только в следующем раунде, то итерация по копии текущего состояния
Решение, которое я вижу здесь, заключается в том, что вам нужно подумать о безопасности потоков. Я бы порекомендовал вам использовать объект Concurrent Collection, например: ConcurrentHashMap, потому что это избавит вас от исключения, которое вы получаете. Вам придется заплатить за производительность, но решение остается выполнимым.
Другое решение - использовать массив ([]), если у вас не слишком много динозавров.
Обратите внимание, что даже если это сработает, dinosaurs.add(dinosaur.timeToReproduce()); добавляет еще один элемент к dinosaurs каждый раз, поэтому вы будете повторять бесконечно (или до тех пор, пока он не достигнет нуля и не выдаст NPE). Вам нужно проверить, является ли возвращаемое значение timeToReproduce() нулевым перед его добавлением, иначе вы просто добавляете нули в список.
@MarcosVasconcelos, нельзя ли добавлять элементы в итератор? @zapl Теперь я понимаю, что, вероятно, было бы лучше, чтобы любые добавленные динозавры делали это после того, как исходные динозавры переместились, что решает эту проблему - например, добавьте в ArrayList dinosaurstoAdd, а затем добавьте их после завершения цикла. @NesanMano Я бы предпочел не использовать стандартный массив, поскольку у ArrayLists есть несколько дополнительных опций и немного больше гибкости. @SeanVanGorder, спасибо, что помогли мне это понять. Да я не хочу добавлять нули в список XD




Итератор решает проблему удаления, но для добавления я не уверен