Enemy Dodging Movement

From Robowiki
Jump to navigation Jump to search

A method for avoiding collisions with enemies.

How's it work?

General idea is: in Enemy Dodging Movement (EDM), you're generating set of points around your robot, calculate average distance to enemies for each point and go to point with minimal value.

Graphical demonstration

Edm demo.GIF

red squares
known enemies positions
blue circle
danger area (enemies outside this area are ignored)
red/white points around the EDMRobot
destination point set. White points is good, red is bad.
bold white point
destination point, i.e. the point which is the furthest from enemies in the blue circle.

Implementation

/**
 * This class is implement Enemy dodging movement method
 * 
 * @author jdev
 */
public class EnemyDodgingMovement {

    private static final int ACTIVITY_AREA_MARGIN = 20;
    private static final int FIELD_OF_VISION = 50;
    private static final int DANGER_DISTANCE = FIELD_OF_VISION * 3;

    private final Robot robot;
    private final Rectangle activityArea;

    public EnemyDodgingMovement(Robot robot) {
        this.robot = robot;

        activityArea = new Rectangle(ACTIVITY_AREA_MARGIN, ACTIVITY_AREA_MARGIN,
                (int)robot.getBattleFieldWidth() - ACTIVITY_AREA_MARGIN * 2,
                (int)robot.getBattleFieldHeight() - ACTIVITY_AREA_MARGIN * 2);
    }

    /**
     * Method to calculate farest point from enemies
     * @param enemies position of enemies
     * @return farest point from enemies
     */
    public Point2D.Double getDestination(Collection<Point2D.Double> enemies) {
        final Collection<EDMPoint> points = getPoints(FIELD_OF_VISION, enemies);
        
        double maxAvgDist = 0;
        EDMPoint destination = null;
        
        for (EDMPoint p : points) {
            double avgDist = calculateAvgDistance(p, enemies);
            if (avgDist > maxAvgDist) {
                maxAvgDist = avgDist;
                destination = p;
            }
        }

        return destination;
    }

    /**
     * Returns the collection of points, which are located on circle with radius = <code>dist</code> and with center
     * in [<code>robot.getX()</code>, <code>robot.getY()</code>]
     * @param dist distance to probably destination points from robot
     * @param enemies enemies positions
     * @return Returns the collection of points 
     */
    private Collection<EDMPoint> getPoints(double dist, Collection<Point2D.Double> enemies) {
        final Collection<EDMPoint> points = new LinkedList<EDMPoint>();
        final Point2D.Double myPos = new Point2D.Double(robot.getX(), robot.getY());
        for (double angle = 0; angle < PI * 2; angle += PI / 9) {
            final EDMPoint p = new EDMPoint(myPos.x + sin(angle) * dist,
                    myPos.y + cos(angle) * dist);

            if (!activityArea.contains(p)) {
                continue;
            }
            p.avgDistance = calculateAvgDistance(p, enemies);
            points.add(p);
        }

        return points;
    }

    /**
     * Calculates avarenge distance from point <code>point</code> to enemies in <code>enemies</code>
     * @param point point to calculate averenge distance
     * @param enemies enemies positions
     * @return averenge distance
     */
    private double calculateAvgDistance(Point2D.Double point, Collection<Point2D.Double> enemies) {
        double distanceSum = 0;
        int closeEnemyCount = 0;
        for (Point2D.Double p : enemies) {
            final double distance = p.distance(point);
            if (p.distance(robot.getX(), robot.getY()) > DANGER_DISTANCE) {
                continue;
            }
            
            distanceSum += distance;
            closeEnemyCount++;
        }

        return distanceSum / (double)(closeEnemyCount > 0 ? closeEnemyCount : 1);
    }

    /**
     * Paints a EDM's model
     * @param g graphics to paint
     * @param enemies enemies positions
     */
    public void paint(Graphics2D g, Collection<Point2D.Double> enemies) {
        g.setColor(Color.WHITE);
        final Collection<EDMPoint> points = getPoints(FIELD_OF_VISION, enemies);
        double maxAvgDist = 0;
        double minAvgDist = Double.MAX_VALUE;
        for (EDMPoint p : points) {
            if (p.avgDistance < minAvgDist) {
                minAvgDist = p.avgDistance;
            }
            if (p.avgDistance > maxAvgDist) {
                maxAvgDist = p.avgDistance;
            }
        }

        for (EDMPoint rp : points) {

            int radius = 4;
            int gb = (int) (255 * (rp.avgDistance - minAvgDist) / (maxAvgDist - minAvgDist));
            if (gb < 0) {
                gb = 0;
            } else if (gb > 255) {
                gb = 255;
            }
            g.setColor(new Color(255, gb, gb));
            g.fillOval((int) Math.round(rp.x - radius / 2), (int) Math.round(rp.y - radius / 2), radius, radius);
            if (rp.avgDistance == maxAvgDist) {
                radius = 6;
                g.drawOval((int) Math.round(rp.x - radius / 2), (int) Math.round(rp.y - radius / 2), radius, radius);
            }
        }

        g.setColor(Color.BLUE);
        final int fieldOfVisionRadius = DANGER_DISTANCE * 2;
        g.drawOval((int)robot.getX() - fieldOfVisionRadius / 2, (int)robot.getY() - fieldOfVisionRadius / 2,
                fieldOfVisionRadius, fieldOfVisionRadius);
    }

}
/**
 * Class to represent Point in EDM method. Externds Point2D.Double and adds field <code>avgDistance</code>,
 * which means averegene distance to enemies in EDM 
 *
 * @author jdev
 */
public class EDMPoint extends Point2D.Double {
    public double avgDistance;
    public EDMPoint(double x, double y) {
        super(x, y);
    }
}

Advantages

  • Easy to implement.
  • If possible, this method will give you a point where you will not hit any enemies.

Disadvantages

  • It's clear tactical "method", so it must be combined with any "strategical" method, for example Minimum Risk Movement