/** * */ package rampancy.util.weapon; import rampancy.RampantRobot; import rampancy.util.RBattlefield; import rampancy.util.RDrawableObject; import rampancy.util.REnemyRobot; import rampancy.util.RPoint; import rampancy.util.RRobotState; import rampancy.util.RUtil; import rampancy.util.movement.MovSim; import rampancy.util.movement.MovSimStat; import rampancy.util.wave.RBulletWave; import robocode.util.Utils; import java.awt.Color; import java.awt.Graphics2D; import java.util.*; /** * @author Matthew Chun-Lum * */ public class RPMGun extends RGun { public static final String PM_TYPE = "pattern matching gun"; public static final int NUM_MATCH_STATES = 9; public static final int MAX_TICKS = 40; HashMap<String, ArrayList<RRobotState>> robotHistories; private ArrayList<RPoint> examinedLocations; public RPMGun() { super(PM_TYPE); robotHistories = new HashMap<String, ArrayList<RRobotState>>(); examinedLocations = new ArrayList<RPoint>(); this.getStats().minPercent = 25.0; } /* (non-Javadoc) * @see rampancy.util.weapon.RGun#getFiringSolution(rampancy.util.REnemyRobot) */ @Override public RFiringSolution getFiringSolution(REnemyRobot enemy) { ArrayList<RRobotState> states = enemy.getLastNStates(REnemyRobot.MAX_HISTORY_SIZE); if(states.size() < NUM_MATCH_STATES * 2) return null; ArrayList<RRobotState> needle = new ArrayList<RRobotState>(states.subList(0, NUM_MATCH_STATES - 1)); ArrayList<RRobotState> haystack = new ArrayList<RRobotState>(states.subList(NUM_MATCH_STATES, states.size() - 1)); // find the best match for the needle in the haystack int bestMatch = -1; double bestPercentDifference = 100000; for(int i = haystack.size() - 1; i > NUM_MATCH_STATES * 2 - 1; i--) { double cumulativePercentDifference = 0; for(int j = needle.size() - 1; j >= 0; j--) { RRobotState haystackState = haystack.get(i - j); RRobotState needleState = needle.get(j); double deltaHDiff = RUtil.percentDifference(needleState.deltaH, haystackState.deltaH); double deltaTHDiff = RUtil.percentDifference(needleState.heading, haystackState.heading); double deltaVDiff = RUtil.percentDifference(needleState.deltaV, haystackState.deltaV); double deltaTVDiff = RUtil.percentDifference(needleState.velocity, haystackState.velocity); double deltaWall = RUtil.percentDifference(needleState.distanceFromWallCategory, haystackState.distanceFromWallCategory); double deltaDirection = RUtil.percentDifference(needleState.directionTraveling, haystackState.directionTraveling); double deltaTimeSinceStop = RUtil.percentDifference(needleState.timeSinceStop, haystackState.timeSinceStop); double delatTimeDC = RUtil.percentDifference(needleState.timeSinceDirectionChange, haystackState.timeSinceDirectionChange); //cumulativePercentDifference += (deltaHDiff + deltaTVDiff); cumulativePercentDifference += RUtil.square(needleState.deltaH - haystackState.deltaH) + RUtil.square(needleState.velocity -haystackState.velocity); } cumulativePercentDifference /= 1.0;//NUM_MATCH_STATES; if(cumulativePercentDifference < bestPercentDifference) { bestMatch = i - NUM_MATCH_STATES; bestPercentDifference = cumulativePercentDifference; } } if(bestMatch < 0) return null; ArrayList<RRobotState> futureStates = new ArrayList<RRobotState>(states.subList(0, bestMatch)); Collections.reverse(futureStates); ArrayList<PMSolution> solutions = completeSolution(enemy, futureStates, enemy.getReference().getGunHeadingRadians()); if(solutions.isEmpty()) return null; PMSolution bestSolution = solutions.get(0); double bestPower = 0; int i = 0; for(; i < solutions.size(); i++) { if(solutions.get(i).numTicks > MAX_TICKS) break; if(solutions.get(i).power > bestPower) { bestSolution = solutions.get(i); bestPower = bestSolution.power; } } PMDrawableObject drawableObject = new PMDrawableObject(examinedLocations); return new RFiringSolution(bestSolution.predictedHitLocation, bestSolution.power, bestSolution.firingAngle, bestPercentDifference, 0, Color.PINK, this, drawableObject, i + 1); } /* (non-Javadoc) * @see rampancy.util.weapon.RGun#update(rampancy.util.wave.RBulletWave) */ @Override public void update(RBulletWave wave) { super.update(wave); } // Private private ArrayList<PMSolution> completeSolution(REnemyRobot enemy, ArrayList<RRobotState> futureStates, double currentGunHeading) { RRobotState currentState = enemy.getCurrentState(); examinedLocations = new ArrayList<RPoint>(); ArrayList<PMSolution> solutions = new ArrayList<PMSolution>(); double x = currentState.location.x; double y = currentState.location.y; double velocity = currentState.velocity; double heading = currentState.heading; RBattlefield bf = RampantRobot.getGlobalBattlefield(); RPoint fireLocation = enemy.getReference().getCopyOfLocation(); for(int i = 0; i < Math.min(futureStates.size(), MAX_TICKS); i++) { RRobotState state = futureStates.get(i); MovSimStat[] predicted = MovSim.futurePos(1, x, y, velocity, velocity + state.deltaV, heading, (velocity == 0 ? 0 : 1000), state.deltaH, MovSim.defaultMaxTurnRate, bf.width, bf.height); RPoint predictedPoint = new RPoint(predicted[0].x, predicted[0].y); examinedLocations.add(predictedPoint); double distance = fireLocation.distance(predictedPoint); double requiredVelocity = distance / (double) (i + 1); double requiredPower = RUtil.computeBulletPower(requiredVelocity); if(requiredPower >= 0.1 && requiredPower <= 3.0) { // if the required power is valid double firingAngle = Utils.normalAbsoluteAngle(RUtil.computeAbsoluteBearing(fireLocation, predictedPoint)); firingAngle = Utils.normalRelativeAngle(firingAngle - currentGunHeading); if(Math.abs(firingAngle) <= 0.5) { solutions.add(new PMSolution(predictedPoint, requiredPower, firingAngle, i + 1)); } } x = predicted[0].x; y = predicted[0].y; velocity = predicted[0].v; heading = predicted[0].h; } return solutions; } class PMSolution { RPoint predictedHitLocation; double power; double firingAngle; int numTicks; public PMSolution(RPoint predicted, double power, double angle, int ticks) { this.predictedHitLocation = predicted; this.power = power; this.firingAngle = angle; this.numTicks = ticks; } } class PMDrawableObject implements RDrawableObject { private List<RPoint> points; public PMDrawableObject(List<RPoint> points) { this.points = points; } /* (non-Javadoc) * @see rampancy.util.RDrawableObject#draw(java.awt.Graphics2D) */ public void draw(Graphics2D g) { g.setColor(Color.magenta); for(RPoint point : points) { RUtil.drawOval(point, 5, g); } } } }