AI How to model genetic programming for Battleships

前端 未结 4 1305
-上瘾入骨i
-上瘾入骨i 2021-01-01 05:46

I have a question regarding Genetic Programming. I am going to work on a genetic algorithm for a game called Battleships.

My question is: How would I decide upon a \

4条回答
  •  囚心锁ツ
    2021-01-01 06:30

    ANSWER PART I: The basis for a genetic algorithm is a having a group of actors, some of which reproduce. The fittest are chosen for reproduction and the offspring are copies of the parents that are slightly mutated. It's a pretty simple concept, but to program it you have to have actions that can be randomly chosen and dynamically modified. For the battleship simulation I created a class called a Shooter because it 'shoots' at a position. The assumption here is that the first position has been hit, and the shooter is now trying to sink the battleship.

    public class Shooter implements Comparable {
        private static final int NUM_SHOTS = 100;
        private List shots;
        private int score;
    
        // Make a new set of random shots.
        public Shooter newShots() {
            shots = new ArrayList(NUM_SHOTS);
            for (int i = 0; i < NUM_SHOTS; ++i) {
                shots.add(newShot());
            }
            return this;
        }
        // Test this shooter against a ship
        public void testShooter(Ship ship) {
            score = shots.size();
            int hits = 0;
            for (Position shot : shots) {
                if (ship.madeHit(shot)) {
                    if (++hits >= ship.getSize())
                        return;
                } else {
                    score = score - 1;
                }
            }
        }
    
        // get the score of the testShotr operation
        public int getScore() {
            return score;
        }
        // compare this shooter to other shooters.
        @Override
        public int compareTo(Shooter o) {
            return score - o.score;
        }
        // getter
        public List getShots() {
            return shots;
        }
        // reproduce this shooter
        public Shooter reproduce() {
            Shooter offspring = new Shooter();
            offspring.mutate(shots);
            return offspring;
        }
        // mutate this shooter's offspring
        private void mutate(List pShots) {
            // copy parent's shots (okay for shallow)
            shots = new ArrayList(pShots);
            // 10% new mutations, in random locations
            for (int i = 0; i < NUM_SHOTS / 10; i++) {
                int loc = (int) (Math.random() * 100);
                shots.set(loc, newShot());
            }
        }
        // make a new random move
        private Position newShot() {
            return new Position(((int) (Math.random() * 6)) - 3, ((int) (Math.random() * 6)) - 3);
        }
    }
    

    The idea here is that a Shooter has up to 100 shots, randomly chosen between +-3 in the X and +- 3 in the Y. Yea, 100 shots is overkill, but hey, whatever. Pass a Ship to this Shooter.testShooter and it will score itself, 100 being the best score, 0 being the worst.

    This Shooter actor has reproduce and mutate methods that will return an offspring that has 10% of its shots randomly mutated. The general idea is that the best Shooters have 'learned' to shoot their shots in a cross pattern ('+') as quickly as possible, since a ship is oriented in one of four ways (North, South, East, West).

    The program that runs the simulation, ShooterSimulation, is pretty simple:

    public class ShooterSimulation {
        private int NUM_GENERATIONS = 1000;
        private int NUM_SHOOTERS = 20;
        private int NUM_SHOOTERS_NEXT_GENERATION = NUM_SHOOTERS / 10;
    
        List shooters = new ArrayList(NUM_SHOOTERS);
        Ship ship;
    
        public static void main(String... args) {
            new ShooterSimulation().run();
        }
    
        // do the work
        private void run() {
            firstGeneration();
            ship = new Ship();
            for (int gen = 0; gen < NUM_GENERATIONS; ++gen) {
                ship.newOrientation();
                testShooters();
                Collections.sort(shooters);
                printAverageScore(gen, shooters);
                nextGeneration();
            }
        }
    
        // make the first generation
        private void firstGeneration() {
            for (int i = 0; i < NUM_SHOOTERS; ++i) {
                shooters.add(new Shooter().newShots());
            }
        }
    
        // test all the shooters
        private void testShooters() {
            for (int mIdx = 0; mIdx < NUM_SHOOTERS; ++mIdx) {
                shooters.get(mIdx).testShooter(ship);
            }
        }
    
        // print the average score of all the shooters
        private void printAverageScore(int gen, List shooters) {
            int total = 0;
            for (int i = 0, j = shooters.size(); i < j; ++i) {
                total = total + shooters.get(i).getScore();
            }
            System.out.println(gen + " " + total / shooters.size());
        }
    
        // throw away the a tenth of old generation
        // replace with offspring of the best fit
        private void nextGeneration() {
            for (int l = 0; l < NUM_SHOOTERS_NEXT_GENERATION; ++l) {
                shooters.set(l, shooters.get(NUM_SHOOTERS - l - 1).reproduce());
            }
        }
    }
    

    The code reads as pseudo-code from the run method: make a firstGeneration then iterate for a number of generations. For each generation, set a newOrientation for the ship, then do testShooters, and sort the results of the test with Collections.sort. printAverageScore of the test, then build the nextGeneration. With the list of average scores you can, cough cough, do an 'analysis'.

    A graph of the results looks like this:

    As you can see it starts out with pretty low average scores, but learns pretty quickly. However, the orientation of the ship keeps changing, causing some noise in addition to the random component. Every now and again a mutation messes up the group a bit, but less and less as the group improves overall.

    Challenges, and the reason for many papers to be sure, is to make more things mutable, especially in a constructive way. For example, the number of shots could be mutable. Or, replacing the list of shots with a tree that branches depending on whether the last shot was a hit or miss might improve things, but it's difficult to say. That's where the 'decision' logic considerations come in. Is it better to have a list of random shots or a tree that decides which branch to take depending on the prior shot? Higher level challenges include predicting what changes will make the group learn faster and be less susceptible to bad mutations.

    Finally, consider that there could be multiple groups, one group a battleship hunter and one group a submarine hunter for example. Each group, though made of the same code, could 'evolve' different internal 'genetics' that allow them to specialize for their task.

    Anyway, as always, start somewhere simple and learn as you go until you get good enough to go back to reading the papers.

    PS> Need this too:

    public class Position {
        int x;
        int y;
        Position(int x, int y ) {this.x=x; this.y=y;}
    
        @Override
        public boolean equals(Object m) {
            return (((Position)m).x==x && ((Position)m).y==y);
        }
    }
    

    UDATE: Added Ship class, fixed a few bugs:

    public class Ship {
        List positions;
    
        // test if a hit was made
        public boolean madeHit(Position shot) {
            for (Position p: positions) {
                if ( p.equals(shot)) return true;
            }
            return false;
        }
    
        // make a new orientation
        public int newOrientation() {
            positions = new ArrayList(3);
            // make a random ship direction.
            int shipInX=0, oShipInX=0 , shipInY=0, oShipInY=0;
    
            int orient = (int) (Math.random() * 4);
            if( orient == 0 ) {
                oShipInX = 1;
                shipInX = (int)(Math.random()*3)-3;
            }
            else if ( orient == 1 ) {
                oShipInX = -1;
                shipInX = (int)(Math.random()*3);
            }
            else if ( orient == 2 ) {
                oShipInY = 1;
                shipInY = (int)(Math.random()*3)-3;
            }
            else if ( orient == 3 ) {
                oShipInY = -1;
                shipInY = (int)(Math.random()*3);
            }
    
            // make the positions of the ship
            for (int i = 0; i < 3; ++i) {
                positions.add(new Position(shipInX, shipInY));
                if (orient == 2 || orient == 3)
                    shipInY = shipInY + oShipInY;
                else
                    shipInX = shipInX + oShipInX;
            }
            return orient;
        }
    
        public int getSize() {
            return positions.size();
        }
    }
    

提交回复
热议问题