javaeclipseneural-networkartificial-intelligenceflappy-bird-clone

AI Flappy Bird doesnt mutate corectly


i am new to programming and especialy at programming ai. I am sorry if the problem is stupid or easy to fix. I have created a primitive flappy bird game with neural network to controll it. Every thing goes ok until all the birds die. After all birds die i am choosing the best bird, create Array of new population , set the new birds "brain" to equal to the best bird "brain", and finally i am mutate all the new population birds brain a tiny so they are not be the same. I tried to mutate using probability, mutate all the weights of new birds brain, set if statement so weights doesnt go below 1.0 or under -1.0. The result is the same , all the birds at the next generation (second) act like they are having the same "brain". Here are some code that i think is worth to check. I can place all the code but it is big.

Repopulation

        for (int i = 0; i < population; i++) {
            birds.add(new Bird());
            birds.get(i).brain=lastbird.brain;
            birds.get(i).brain.mutate(0.1);
        }

Mutation function

        public void mutate(double eta) {
        Random dice = new Random();
        for (int layer = 1; layer < NETWORK_SIZE; layer++) {
            for (int neuron = 0; neuron < NETWORK_LAYER_SIZES[layer]; neuron++) {
                if (dice.nextDouble() < eta) {
                    bias[layer][neuron] += dice.nextGaussian()/2;
                }
                for (int prevNeuron = 0; prevNeuron < NETWORK_LAYER_SIZES[layer - 1]; prevNeuron++) {
                    if (dice.nextDouble() < eta) {
                        weights[layer][neuron][prevNeuron] += dice.nextGaussian()/2;
                    }
                }
        }
        }
        }

Network(brain) veriables and constructor

public class Network  {

private double[][] output;
private double[][][] weights;
private double[][] bias;

private double[][] error_signal;
private double[][] output_derivative;

public final int[] NETWORK_LAYER_SIZES;
public final int INPUT_SIZE;
public final int OUTPUT_SIZE;
public final int NETWORK_SIZE;

public Network(int... NETWORK_LAYER_SIZES) {
    this.NETWORK_LAYER_SIZES = NETWORK_LAYER_SIZES;
    this.INPUT_SIZE = NETWORK_LAYER_SIZES[0];
    this.NETWORK_SIZE = NETWORK_LAYER_SIZES.length;
    this.OUTPUT_SIZE = NETWORK_LAYER_SIZES[NETWORK_SIZE - 1];

    this.output = new double[NETWORK_SIZE][];
    this.weights = new double[NETWORK_SIZE][][];
    this.bias = new double[NETWORK_SIZE][];

    this.error_signal = new double[NETWORK_SIZE][];
    this.output_derivative = new double[NETWORK_SIZE][];

    for (int i = 0; i < NETWORK_SIZE; i++) {
        this.output[i] = new double[NETWORK_LAYER_SIZES[i]];
        this.error_signal[i] = new double[NETWORK_LAYER_SIZES[i]];
        this.output_derivative[i] = new double[NETWORK_LAYER_SIZES[i]];

        this.bias[i] = NetworkTools.createRandomArray(NETWORK_LAYER_SIZES[i], -0.5, 0.7);

        if (i > 0) {
            weights[i] = NetworkTools.createRandomArray(NETWORK_LAYER_SIZES[i], NETWORK_LAYER_SIZES[i - 1], -1, 1);
        }
    }
}

Solution

  • When you assign the brain of the i th bird to the lastbird.brain in all of the new generation birds at birds.get(i).brain=lastbird.brain, you are setting the references of all the brains of all the new birds to the same brain object. ie. any bird.brain reference points to the same brain object. So when you change (ie. mutate) one bird's brain, it is done on the common object pointed by all the references and it is reflected on all the birds at the same time.

    You need to copy the contents of the brain object instead of pointing to the same object. You can do this by cloning or using copy constructors. Copy constructors are preferred over cloning. You need to replace birds.get(i).brain=lastbird.brain with

    birds.get(i).brain = new Brain(lastbird.brain);
    

    Since you have not given the code of the Brain object, I cannot give the implementation of the copy constructor. You can assign the primitives (int, String etc) using = sign within the constructor. But for all custom objects, you need to create a copy constructor for those as well.

    You can find more information here How do I copy an object in Java?.

    Edit: Adding the implementation after the Network class was provided.

     public Network( Network other )
        {
            this.output = copy2d( other.output );
            this.weights = copy3d( other.weights );
            this.bias = copy2d( other.bias );
            this.error_signal = copy2d( other.error_signal );
            this.output_derivative = copy2d( other.output_derivative );
    
            this.NETWORK_LAYER_SIZES = copy1dInt(other.NETWORK_LAYER_SIZES);
            this.INPUT_SIZE = other.INPUT_SIZE;
            this.OUTPUT_SIZE = other.OUTPUT_SIZE;
            this.NETWORK_SIZE = other.NETWORK_SIZE;
        }
    
        private static double[][][] copy3d( double[][][] original )
        {
            double[][][] copy = new double[original.length][][];
            for( int i = 0; i < original.length; i++ )
            {
                copy[i] = copy2d( original[i] );
            }
            return copy;
        }
    
        private  static double[][] copy2d( double[][] original )
        {
            double[][] copy = new double[original.length][];
            for( int i = 0; i < original.length; i++ )
            {
                copy[i] = copy1d( original[i] );
            }
            return copy;
        }
    
        private static double[] copy1d( double[] original )
        {
            int length = original.length;
            double[] copy = new double[length];
            System.arraycopy( original, 0, copy, 0, length );
            return copy;
        }
    
        private static int[] copy1dInt( int[] original )
        {
            int length = original.length;
            int[] copy = new int[length];
            System.arraycopy( original, 0, copy, 0, length );
            return copy;
        }