/** * */ package rampancy.whuphs; import java.awt.Color; import java.awt.Graphics2D; import java.util.*; import rampancy.*; import rampancy.util.*; import rampancy.util.movement.MovSim; import rampancy.util.movement.MovSimStat; import rampancy.util.movement.RMoveChoice; import rampancy.util.wave.REnemyWave; import rampancy.util.wave.REnemyWaveWithStats; import robocode.util.Utils; /** * @author Matthew Chun-Lum * */ public class WhuphsMovementManager implements RMovementManager { public static final double MIN_DISTANCE = 230; public static final double MIN_WAVE_ORBIT_DISTANCE = 230; public static final Color CW_COLOR = new Color(0, 0, 255); public static final Color CCW_COLOR = new Color(0, 200, 200); private ArrayList<RMoveChoice> examinedMovementChoices; private REnemyWave dummyWave; private RampantRobot reference; private int direction; /** * Constructor */ public WhuphsMovementManager(RampantRobot reference) { examinedMovementChoices = new ArrayList<RMoveChoice>(); this.reference = reference; direction = 1; } public void updateReference(RampantRobot reference) { this.reference = reference; if(Math.random() < 0.7) direction = -direction; } /* (non-Javadoc) * @see rampancy_v2.RMovementManager#computeBestMove() */ public RMoveChoice computeBestMove(REnemyRobot enemy) { dummyWave = new REnemyWave(enemy); ArrayList<RMoveChoice> movementChoices = getBestOrbitMove(new MoveStruct(reference, enemy, dummyWave)); if(movementChoices.isEmpty()) return null; examinedMovementChoices = new ArrayList<RMoveChoice>(movementChoices); Collections.sort(movementChoices, new Comparator<RMoveChoice>() { public int compare(RMoveChoice o1, RMoveChoice o2) { return (int) (o1.danger - o2.danger); } }); // if(Math.random() < 0.01) // direction = -direction; int tempDirection = direction == 0 ? 1 : direction; for(RMoveChoice choice : movementChoices) { if(choice.direction == tempDirection) return choice; } return null; } /* (non-Javadoc) * @see rampancy_v2.RMovementManager#draw(java.awt.Graphics2D) */ public void draw(Graphics2D g) { REnemyWave wave = RampantRobot.getWaveManager().getClosestEnemyWave(reference.getLocation()); if(wave == null) wave = dummyWave; if(wave == null) return; double goAngle = RUtil.computeOrbitAngle(reference.getCopyOfLocation(), wave, 0.0, 1); double goAngleCCW = RUtil.computeOrbitAngle(reference.getCopyOfLocation(), wave, 0.0, -1); RPoint cw = RUtil.project(reference.getCopyOfLocation(), goAngle, RUtil.WALL_STICK); RPoint ccw = RUtil.project(reference.getCopyOfLocation(), goAngleCCW, RUtil.WALL_STICK); g.setColor(Color.blue); RUtil.drawOval(reference.getCopyOfLocation(), (int) RUtil.WALL_STICK, g); g.setColor(CW_COLOR); RUtil.drawOval(cw, 25, g); RUtil.fillOval(cw, 3, g); g.setColor(CCW_COLOR); RUtil.drawOval(ccw, 25, g); RUtil.fillOval(ccw, 3, g); g.setColor(Color.blue); for(RMoveChoice choice : examinedMovementChoices) choice.draw(g); } // ---------- Private Helpers ----------- // private ArrayList<RMoveChoice> getBestOrbitMove(MoveStruct solution) { ArrayList<RMoveChoice> choices = new ArrayList<RMoveChoice>(); // simulate clockwise simulateOrbitDirection(solution.reference.getLocation(), solution.reference.getHeadingRadians(), solution.reference.getVelocity(), 1, 0, solution, choices); // simulate counterclockwise simulateOrbitDirection(solution.reference.getLocation(), solution.reference.getHeadingRadians(), solution.reference.getVelocity(), -1, 0, solution, choices); return choices; } private void simulateOrbitDirection(RPoint fromLocation, double fromHeading, double fromVelocity, int direction, int timeOffset, MoveStruct solution, ArrayList<RMoveChoice> movementChoices) { if(solution.wave.intercepted(fromLocation, timeOffset)) return; RPoint orbitLocation = solution.enemy.getCurrentState().location; double orbitDistance = orbitLocation.distance(fromLocation); double goAngle = RUtil.computeAbsoluteBearing(orbitLocation, fromLocation); double attackAngle = computeAttackAngle(goAngle, fromLocation, fromHeading, fromVelocity, direction, timeOffset, solution); goAngle = RUtil.wallSmoothing(fromLocation, goAngle + (Math.PI / 2.0 + attackAngle) * direction, direction, orbitDistance); MovSimStat next = predictPosition(fromLocation, fromHeading, goAngle, fromVelocity, MovSim.defaultMaxVelocity, direction); RPoint nextLocation = new RPoint(next.x, next.y); // log this point as a movement choice logOrbitLocation(nextLocation, timeOffset, direction, solution, movementChoices); simulateOrbitDirection(nextLocation, next.h, next.v, direction, timeOffset + 1, solution, movementChoices); } private void logOrbitLocation(RPoint location, int timeOffset, int direction, MoveStruct solution, ArrayList<RMoveChoice> movementChoices) { double danger = 0; RPoint orbitLocation = solution.enemy.getCurrentState().location; if(location.distance(orbitLocation) < solution.enemy.getPreferredSafeDistance()) danger += 100000.0 / RUtil.square(location.distance(orbitLocation)); double orbitDistance = orbitLocation.distance(location); double goAngle = RUtil.computeAbsoluteBearing(orbitLocation, solution.reference.getLocation()); double attackAngle = computeAttackAngle(goAngle, solution.reference.getLocation(), solution.reference.getHeadingRadians(), solution.reference.getVelocity(), direction, timeOffset, solution); goAngle = RUtil.wallSmoothing(solution.reference.getLocation(), goAngle + (Math.PI / 2.0 + attackAngle) * direction, direction, orbitDistance); RMoveChoice moveChoice = new RMoveChoice(location, goAngle, -1, 8.0, danger, 0, timeOffset, direction); moveChoice.color = direction > 0 ? CW_COLOR : CCW_COLOR; movementChoices.add(moveChoice); } private double computeAttackAngle(double goAngle, RPoint fromLocation, double fromHeading, double fromVelocity, double direction, int timeOffset, MoveStruct solution) { double attackAngle = 0; REnemyRobot enemy = solution.enemy; RRobotState enemyState = enemy.getLastState(); if(enemyState == null) enemyState = enemy.getCurrentState(); if(fromLocation.distance(enemyState.location) < enemy.getPreferredSafeDistance()) { // way too vulnerable int enemyDirection = enemyState.directionTraveling; // if we are traveling in opposite directions we might collide double orbitAngle = goAngle + (Math.PI / 2.0 * direction); if(enemyDirection != direction) { double delta1 = Math.abs(orbitAngle - enemyState.heading); double delta2 = Math.abs(orbitAngle - Utils.normalAbsoluteAngle(enemyState.heading + Math.PI)); attackAngle = -Math.min(delta1, delta2); } else { attackAngle = -0.05; } } return attackAngle; } /* * Accurately predicts the future position * Credit goes to Albert for the MovSim part */ private MovSimStat predictPosition(RPoint fromLocation, double currentHeading, double desiredHeading, double currentVelocity, double maxVelocity, double direction) { double angleToTurn = desiredHeading - currentHeading; int moveDirection = 1; if(Math.cos(angleToTurn) < 0) { angleToTurn += Math.PI; moveDirection = -1; } angleToTurn = Utils.normalRelativeAngle(angleToTurn); RBattlefield bf = RampantRobot.getGlobalBattlefield(); MovSimStat[] nextTick = MovSim.futurePos(1, fromLocation.x, fromLocation.y, currentVelocity, maxVelocity, currentHeading, 1000 * moveDirection, angleToTurn, MovSim.defaultMaxTurnRate, bf.width, bf.height); return nextTick[0]; } } class MoveStruct { public MoveStruct(RampantRobot reference, REnemyRobot enemy, REnemyWave wave) { this.reference = reference; this.enemy = enemy; this.wave = wave; } public RampantRobot reference; public REnemyRobot enemy; public REnemyWave wave; }