0

I'm currently working on an AI that will take decisions for every individual of a list. But while iterating over this list, I get ConcurrentModificationException. I verified if my removeDeads function could cause such a thing but as it isn't inside the loop, it shouldn't. Here is the error stack :


Exception in Application start method
java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:473)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:372)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:945)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:973)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:198)
    at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:939)
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:893)
    at IA.RLAI.play(RLAI.java:48)
    at Game.Game.play(Game.java:61)
    at Main.start(Main.java:136)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:919)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$11(PlatformImpl.java:449)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$9(PlatformImpl.java:418)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:417)
    at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
    at javafx.graphics/com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277)
    ... 1 more
Exception running application Main

The problem is that it never happens at the same moment. I could have iterated on 3 individuals as well as 15.

Here is my code :

    public void play(Population population, Game game) throws Exception {
        //looking if some individuals died from the other player
        ArrayList<Individual> deads = new ArrayList<>();
        for(Individual i: population.getIndividuals()){
            if(i.getCurrentHP()==0){
                deads.add(i);
            }
        }
        population.getIndividuals().removeAll(deads);
        for (Individual dead: deads) {
            learn(dead);
        }

    //Normal individuals
    for (Individual me: population.getIndividuals()) {
        int size = Math.max(0, me.getMemory().size()-1);

        if(me.getMemory().isEmpty()){
            System.out.println(me.getMemory().toString());
        }else {
            System.out.println(me.getMemory().get(size).toString());
        }

        double[] inputs = new double[153];
        int i = 0;

        double[] toInsert = getAllyMap(me);
        System.arraycopy(toInsert, 0, inputs, i, 49);
        i+=49;

        toInsert = getEnemyMap(me, game);
        System.arraycopy(toInsert, 0, inputs, i, 49);
        i+=49;

        toInsert = getFoodMap(me);
        System.arraycopy(toInsert, 0, inputs, i, 49);
        i+=49;

        inputs[i++] = me.getPosition().getX()-population.getFoodBase().getX(); //Relative position to foodBase X
        inputs[i++] = me.getPosition().getY()-population.getFoodBase().getY(); //Relative position to foodBase Y
        inputs[i++] = me.getCurrentHP();
        inputs[i++] = me.getCurrentFoodLvl();
        inputs[i++] = me.getCurrentFoodStock();
        inputs[i++] = population.getMapping().getCase(population.getFoodBase()).getFoodQuantity();

        double[] outputs = network.forward(inputs);
        //calculus of the probability of the outputs
        double sumExp = 0;
        for(int j = 0; j<outputs.length; j++){
            sumExp += Math.exp(outputs[j]);
        }
        double[] proba = new double[outputs.length];
        for (int j = 0; j<outputs.length; j++){
            proba[j] = Math.exp(outputs[j])/sumExp;
        }
        //determination of the chosen output
        double[] probCuml = new double[outputs.length+1];
        probCuml[0] = 0;
        for (int j = 0; j < outputs.length; j++){
            probCuml[j+1] = probCuml[j]+proba[j];
        }
        double rand = Math.random();
        int choice = 1;
        for(int j = 1; j<probCuml.length; j++){
            if(rand < probCuml[j]){
                choice = j-1;
                break;
            }
        }
        //Action
        switch(choice){
            case 0:
                me.move(Actions.MoveUp);
                break;
            case 1:
                me.move(Actions.MoveDown);
                break;
            case 2:
                me.move(Actions.MoveRight);
                break;
            case 3:
                me.move(Actions.MoveLeft);
                break;
            case 4:
                Population toAttack = game.getOtherPopulation(population);
                me.attack(toAttack);
                break;
            case 5:
                me.collect();
                break;
            case 6:
                me.store();
                break;
            case 7:
                me.eat();
                break;
            case 8:
                me.copulate();
                break;
            default :
                throw new Exception("Out of Possible Choice");
        }
        me.regen();
        if(me.getCurrentHP()<=0){me.addMemory(Reinforcement.Die);}
        learn(me);
    }
    //looking for individuals that die of hunger but as they already learned, 

    no need to do it again
    population.areDead();
    System.out.println(" ");
}

And here is my areDead function:

public ArrayList<Individual> areDead(){
        ArrayList<Individual> areDead = new ArrayList<>();
        for (Individual individual: individuals) {
            if(individual.getCurrentHP()<=0){
                areDead.add(individual);
            }
        }
        individuals.removeAll(areDead);
        return areDead;
    }

The problem is that I don't know where this exception comes from. It says it comes from the loop on individuals but there is nothing that modifies the list in it.

Kristof U.
  • 1,263
  • 10
  • 17

0 Answers0