ab.demo.AIAssignment2.java Source code

Java tutorial

Introduction

Here is the source code for ab.demo.AIAssignment2.java

Source

/*****************************************************************************
 ** ANGRYBIRDS AI AGENT FRAMEWORK
 ** Copyright (c) 2014, XiaoYu (Gary) Ge, Stephen Gould, Jochen Renz
 **  Sahan Abeyasinghe,Jim Keys,  Andrew Wang, Peng Zhang
 ** All rights reserved.
**This work is licensed under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
**To view a copy of this license, visit http://www.gnu.org/licenses/
 *****************************************************************************/
package ab.demo;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import weka.classifiers.bayes.NaiveBayes;
import weka.core.Instances;
import weka.core.converters.ConverterUtils.DataSource;
import ab.demo.other.ActionRobot;
import ab.demo.other.Shot;
import ab.planner.TrajectoryPlanner;
import ab.utils.StateUtil;
import ab.vision.ABObject;
import ab.vision.ABType;
import ab.vision.GameStateExtractor.GameState;
import ab.vision.Vision;

public class AIAssignment2 implements Runnable {

    private ActionRobot aRobot;
    private Random randomGenerator;
    public int currentLevel = 1;
    public static int time_limit = 12;
    private Map<Integer, Integer> scores = new LinkedHashMap<Integer, Integer>();
    TrajectoryPlanner tp;
    private boolean firstShot;
    private Point prevTarget;

    // a standalone implementation of the Naive Agent
    public AIAssignment2() {

        aRobot = new ActionRobot();
        tp = new TrajectoryPlanner();
        prevTarget = null;
        firstShot = true;
        randomGenerator = new Random();
        // --- go to the Poached Eggs episode level selection page ---
        ActionRobot.GoFromMainMenuToLevelSelection();
    }

    // run the client
    public void run() {

        aRobot.loadLevel(currentLevel);
        while (true) {
            GameState state = solve();
            if (state == GameState.WON) {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int score = StateUtil.getScore(ActionRobot.proxy);
                if (!scores.containsKey(currentLevel))
                    scores.put(currentLevel, score);
                else {
                    if (scores.get(currentLevel) < score)
                        scores.put(currentLevel, score);
                }
                int totalScore = 0;
                for (Integer key : scores.keySet()) {

                    totalScore += scores.get(key);
                    System.out.println(" Level " + key + " Score: " + scores.get(key) + " ");
                }
                System.out.println("Total Score: " + totalScore);
                aRobot.loadLevel(++currentLevel);
                // make a new trajectory planner whenever a new level is entered
                tp = new TrajectoryPlanner();

                // first shot on this level, try high shot first
                firstShot = true;
            } else if (state == GameState.LOST) {
                System.out.println("Restart");
                aRobot.restartLevel();
            } else if (state == GameState.LEVEL_SELECTION) {
                System.out
                        .println("Unexpected level selection page, go to the last current level : " + currentLevel);
                aRobot.loadLevel(currentLevel);
            } else if (state == GameState.MAIN_MENU) {
                System.out.println("Unexpected main menu page, go to the last current level : " + currentLevel);
                ActionRobot.GoFromMainMenuToLevelSelection();
                aRobot.loadLevel(currentLevel);
            } else if (state == GameState.EPISODE_MENU) {
                System.out.println("Unexpected episode menu page, go to the last current level : " + currentLevel);
                ActionRobot.GoFromMainMenuToLevelSelection();
                aRobot.loadLevel(currentLevel);
            }

        }

    }

    private double distance(Point p1, Point p2) {
        return Math.sqrt((double) ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)));
    }

    public GameState solve() {

        // capture Image
        BufferedImage screenshot = ActionRobot.doScreenShot();

        // process image
        Vision vision = new Vision(screenshot);

        // find the slingshot
        Rectangle sling = vision.findSlingshotMBR();

        // confirm the slingshot
        while (sling == null && aRobot.getState() == GameState.PLAYING) {
            System.out.println("No slingshot detected. Please remove pop up or zoom out");
            ActionRobot.fullyZoomOut();
            screenshot = ActionRobot.doScreenShot();
            vision = new Vision(screenshot);
            sling = vision.findSlingshotMBR();
        }

        // get all the pigs
        List<ABObject> pigs = vision.findPigsMBR();
        List<ABObject> blocks = vision.findBlocksMBR();

        GameState state = aRobot.getState();

        // if there is a sling, then play, otherwise just skip.
        if (sling != null) {

            if (!pigs.isEmpty()) { //if there are pigs in the level

                Point releasePoint = null;
                Shot shot = new Shot();
                int dx, dy;
                {
                    //random pick up a pig
                    ABObject pig = pigs.get(randomGenerator.nextInt(pigs.size()));
                    Point _tpt = pig.getCenter();

                    // estimate the trajectory
                    ArrayList<Point> pts = tp.estimateLaunchPoint(sling, _tpt);

                    //define all of the wood, ice and stone in the stage
                    ArrayList<ABObject> wood = new ArrayList<ABObject>();
                    ArrayList<ABObject> stone = new ArrayList<ABObject>();
                    ArrayList<ABObject> ice = new ArrayList<ABObject>();
                    ArrayList<ABObject> tnt = new ArrayList<ABObject>();

                    //initialise counters to store how many times the trajectory intersects blocks of these types
                    int woodCount = 0;
                    int stoneCount = 0;
                    int iceCount = 0;
                    int pigsCount = 0;
                    int tntCount = 0;

                    //populate the wood, stone and ice ArrayLists with the correct materials
                    for (int i = 0; i < blocks.size(); i++) {
                        if (blocks.get(i).type == ABType.Wood)
                            wood.add(blocks.get(i));
                        if (blocks.get(i).type == ABType.Stone)
                            stone.add(blocks.get(i));
                        if (blocks.get(i).type == ABType.Ice)
                            ice.add(blocks.get(i));
                        if (blocks.get(i).type == ABType.TNT)
                            tnt.add(blocks.get(i));
                    }

                    //check whether the trajectory intersects any wood blocks
                    for (int i = 0; i < wood.size(); i++) {
                        for (int j = 0; j < pts.size(); j++) {
                            if (wood.get(i).contains(pts.get(j))) {
                                System.out.println("Trajectory intersects some wood");
                                woodCount++;
                            }
                            if (pig.contains(pts.get(j))) //if we find the pig on this point
                                j = pts.size() - 1; //stop looking for wood on the trajectory (escape for loop)
                        }
                    }

                    //check whether the trajectory intersects any ice blocks
                    for (int i = 0; i < ice.size(); i++) {
                        for (int j = 0; j < pts.size(); j++) {
                            if (ice.get(i).contains(pts.get(j))) {
                                System.out.println("Trajectory intersects some ice");
                                iceCount++;
                            }
                            if (pig.contains(pts.get(j))) //if we find the pig on this point
                                j = pts.size() - 1; //stop looking for ice on the trajectory (escape for loop)
                        }
                    }

                    //check whether the trajectory intersects any stone blocks            
                    for (int i = 0; i < stone.size(); i++) {
                        for (int j = 0; j < pts.size(); j++) {
                            if (stone.get(i).contains(pts.get(j))) {
                                System.out.println("Trajectory intersects some stone");
                                stoneCount++;
                            }
                            if (pig.contains(pts.get(j))) //if we find the pig on this point
                                j = pts.size() - 1; //stop looking for stone on the trajectory (escape for loop)
                        }
                    }

                    //how many pigs the trajectory intersects (this should always be at least 1)         
                    for (int i = 0; i < pigs.size(); i++) {
                        for (int j = 0; j < pts.size(); j++) {
                            if (pigs.get(i).contains(pts.get(j))) {
                                System.out.println("Trajectory intersects a pig");
                                pigsCount++;
                            }
                        }
                    }

                    //how many tnt blocks the trajectory intersects            
                    for (int i = 0; i < tnt.size(); i++) {
                        for (int j = 0; j < pts.size(); j++) {
                            if (tnt.get(i).contains(pts.get(j))) {
                                System.out.println("Trajectory intersects some tnt");
                                tntCount++;
                            }
                            if (pig.contains(pts.get(j))) //if we find the pig on this point
                                j = pts.size() - 1; //stop looking for tnt on the trajectory
                        }
                    }

                    StringBuilder sb = new StringBuilder();
                    sb.append(pigsCount + "," + woodCount + "," + iceCount + "," + stoneCount + "," + tntCount
                            + ",?");
                    String dataEntry = sb.toString();
                    try (PrintWriter out = new PrintWriter(
                            new BufferedWriter(new FileWriter("dataset/birds.level.arff", true)))) {
                        out.println(dataEntry);
                    } catch (IOException e) {
                        System.out.println("Error - dataset/birds.level.arff file not found or in use!");
                    }

                    //indicator of if the agent should continue this shot or not (used in the bayes classifier)
                    ArrayList<Boolean> takeShot = new ArrayList<Boolean>();

                    try {
                        DataSource source = new DataSource("dataset/birds.data.arff"); //initialise the learning set for the agent
                        Instances data = source.getDataSet();

                        // setting class attribute if the data format does not provide this information
                        // For example, the XRFF format saves the class attribute information as well
                        if (data.classIndex() == -1)
                            data.setClassIndex(data.numAttributes() - 1);

                        DataSource thisLevel = new DataSource("dataset/birds.level.arff"); //initialise the data retrieved from the current level for the agent
                        Instances thisLevelData = thisLevel.getDataSet();
                        if (thisLevelData.classIndex() == -1)
                            thisLevelData.setClassIndex(data.numAttributes() - 1);

                        //build a new NaiveBayes classifier
                        NaiveBayes bayes = new NaiveBayes();
                        bayes.buildClassifier(data);

                        for (int i = 0; i < thisLevelData.numInstances(); i++) { //for all instances in the current level
                            double label = bayes.classifyInstance(thisLevelData.instance(i)); //generate an outcome/classify an instance in the current level
                            thisLevelData.instance(i).setClassValue(label); //store this outcome
                            System.out.println(thisLevelData.instance(i).stringValue(5)); //print it
                            if (thisLevelData.instance(i).stringValue(5) != "?") { //if there is a decisive choice as to whether a shot should be taken
                                data.add(thisLevelData.instance(i)); //store it
                                if (thisLevelData.instance(i).stringValue(5) == "yes") {//if the classifier classifies a good shot, store it
                                    takeShot.add(true);
                                } else { //if no, store this too
                                    takeShot.add(false);
                                }
                            }
                        }

                        //add all non ? entries to the learning set
                        BufferedWriter writer = new BufferedWriter(new FileWriter("dataset/birds.data.arff"));
                        writer.write(data.toString());
                        writer.flush();
                        writer.close();

                    } catch (Exception e) {
                        e.printStackTrace();
                        System.out.println("Exception caught - file handle error");
                    }

                    //TODO: roll a random number to determine whether we take a shot or not.
                    //populated using the bayesian classification above.
                    //if we roll true, continue with the random pig shot as usual.
                    //if not, take a new random pig and try again.
                    //TODO: implement a failsafe so the agent does not get stuck randomly choosing pigs which the bayesian classification does not allow.
                    Random rng = new Random(takeShot.size());
                    if (takeShot.get(rng.nextInt()))
                        System.out.println("Taking this shot.");
                    else {
                        System.out.println("Not taking this shot. Finding another random pig.");
                        return state;
                    }

                    // if the target is very close to before, randomly choose a
                    // point near it
                    if (prevTarget != null && distance(prevTarget, _tpt) < 10) {
                        double _angle = randomGenerator.nextDouble() * Math.PI * 2;
                        _tpt.x = _tpt.x + (int) (Math.cos(_angle) * 10);
                        _tpt.y = _tpt.y + (int) (Math.sin(_angle) * 10);
                        System.out.println("Randomly changing to " + _tpt);
                    }

                    prevTarget = new Point(_tpt.x, _tpt.y);

                    // do a high shot when entering a level to find an accurate velocity
                    if (firstShot && pts.size() > 1) {
                        releasePoint = pts.get(1);
                    } else if (pts.size() == 1)
                        releasePoint = pts.get(0);
                    else if (pts.size() == 2) {
                        // randomly choose between the trajectories, with a 1 in
                        // 6 chance of choosing the high one
                        if (randomGenerator.nextInt(6) == 0)
                            releasePoint = pts.get(1);
                        else
                            releasePoint = pts.get(0);
                    } else if (pts.isEmpty()) {
                        System.out.println("No release point found for the target");
                        System.out.println("Try a shot with 45 degree");
                        releasePoint = tp.findReleasePoint(sling, Math.PI / 4);
                    }

                    // Get the reference point
                    Point refPoint = tp.getReferencePoint(sling);

                    //Calculate the tapping time according the bird type 
                    if (releasePoint != null) {
                        double releaseAngle = tp.getReleaseAngle(sling, releasePoint);
                        System.out.println("Release Point: " + releasePoint);
                        System.out.println("Release Angle: " + Math.toDegrees(releaseAngle));
                        int tapInterval = 0;
                        switch (aRobot.getBirdTypeOnSling()) {

                        case RedBird:
                            tapInterval = 0;
                            break; // start of trajectory
                        case YellowBird:
                            tapInterval = 65 + randomGenerator.nextInt(25);
                            break; // 65-90% of the way
                        case WhiteBird:
                            tapInterval = 70 + randomGenerator.nextInt(20);
                            break; // 70-90% of the way
                        case BlackBird:
                            tapInterval = 70 + randomGenerator.nextInt(20);
                            break; // 70-90% of the way
                        case BlueBird:
                            tapInterval = 65 + randomGenerator.nextInt(20);
                            break; // 65-85% of the way
                        default:
                            tapInterval = 60;
                        }

                        int tapTime = tp.getTapTime(sling, releasePoint, _tpt, tapInterval);
                        dx = (int) releasePoint.getX() - refPoint.x;
                        dy = (int) releasePoint.getY() - refPoint.y;
                        shot = new Shot(refPoint.x, refPoint.y, dx, dy, 0, tapTime);
                    } else {
                        System.err.println("No Release Point Found");
                        return state;
                    }
                }

                // check whether the slingshot is changed. the change of the slingshot indicates a change in the scale.
                {
                    ActionRobot.fullyZoomOut();
                    screenshot = ActionRobot.doScreenShot();
                    vision = new Vision(screenshot);
                    Rectangle _sling = vision.findSlingshotMBR();
                    if (_sling != null) {
                        double scale_diff = Math.pow((sling.width - _sling.width), 2)
                                + Math.pow((sling.height - _sling.height), 2);
                        if (scale_diff < 25) {
                            if (dx < 0) {
                                aRobot.cshoot(shot);
                                state = aRobot.getState();
                                if (state == GameState.PLAYING) {
                                    screenshot = ActionRobot.doScreenShot();
                                    vision = new Vision(screenshot);
                                    List<Point> traj = vision.findTrajPoints();
                                    tp.adjustTrajectory(traj, sling, releasePoint);
                                    firstShot = false;
                                }
                            }
                        } else
                            System.out.println(
                                    "Scale is changed, can not execute the shot, will re-segement the image");
                    } else
                        System.out
                                .println("no sling detected, can not execute the shot, will re-segement the image");
                }

            }

        }
        return state;
    }

    public static void main(String args[]) {

        AIAssignment2 na = new AIAssignment2();
        if (args.length > 0)
            na.currentLevel = Integer.parseInt(args[0]);
        na.run();

    }
}