[Home]Chase-san/PatternBot

Robo Home | Chase-san | Changes | Preferences | AllPages

Showing revision 1
package chase.pm;

import robocode.*;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;

import static java.lang.Math.*;
import static robocode.util.Utils.*;

/**
 * A simple demonstration of a one on one
 * pattern matching robot.
 * 
 * @author Chase
 */
public class PatternBot extends AdvancedRobot {
	//the deepest backward trace to determine the best match
	private static final int MAX_MATCH_LENGTH = 50;
	//Limit the size of the pattern, so that it doesn't get to slow
	private static final int MAX_PATTERN_LENGTH = 10000;
	//the maximum angle difference between heading delta's to be considered a match
	private static final double MAX_ANGLE_DIFF = PI/64; //== 2.8125 degrees
	//the maximum velocity difference between velocity delta's to be considered a match
	private static final double MAX_VELOCITY_DIFF = 1;
	private static int patternLength = 0;
	private static Rectangle2D.Double battlefield; 
	private static State head, tail;
	private double lastHeading, lastVelocity;
	private Point2D.Double nextPredicted;
	private ArrayList<Point2D.Double> bestList;
	
	public void run() {
		//adjust for all the turns, by way of hierarchy
		//this adjusts the radar for robot turn aswell
		setAdjustGunForRobotTurn(true);
		setAdjustRadarForGunTurn(true);
		
		//some nice flamy red colors
		setColors(Color.red, Color.red, Color.red);
		
		//Add a break to the pattern, so that we don't
		//track patterns that never happened
		addBreak();
		lastVelocity = 100;
		
		//setup the battlefield for later
		if(battlefield == null) {
			battlefield = new Rectangle2D.Double(18,18,
					getBattleFieldWidth()-36,
					getBattleFieldHeight()-36);
		}
		
		//spin the radar
		do {
			turnRadarRightRadians(Double.POSITIVE_INFINITY);
		} while(true);
	}
	
	public void onScannedRobot(ScannedRobotEvent e) {
		//This is the direct angle to the enemy
		double eAbsoluteBearing = getHeadingRadians() + e.getBearingRadians();
		
		//since this is one on one robot, reset the radar
		//we do this first as the lag may cause the robot
		//to skip a turn, this way we don't have broken scans
		
		//this is an advanced no-slip one on one radar
		double radar = normalRelativeAngle(eAbsoluteBearing - getRadarHeadingRadians());
		double arcToScan = atan(36.0 / e.getDistance());
		radar += (radar < 0) ? -arcToScan : arcToScan;
		setTurnRadarRightRadians(radar);
		
		//Initialize some more variable
		double eHeading = e.getHeadingRadians();
		double eVelocity = e.getVelocity();
		double eDistance = e.getDistance();
		Point2D.Double myLocation = new Point2D.Double(getX(), getY());
		Point2D.Double eLocation = project(myLocation,eAbsoluteBearing,eDistance);
		
		if(lastVelocity == 100) {
			lastHeading = eHeading;
			lastVelocity = eVelocity;
			return;
		}
		
		//record the latest data into the pattern
		addState(eHeading, eVelocity);
		
		//check and eliminate extra states over the limit
		while(patternLength > MAX_PATTERN_LENGTH) pruneTail();
		
		//update the last heading and velocity
		lastHeading = eHeading;
		lastVelocity = eVelocity;
		
		//check to see if we need to fire this tick
		if (getGunHeat()/getGunCoolingRate() < 5) {
			//determine the bullet power and speed
			double bulletPower = 3;
			double bulletSpeed = 20-3*bulletPower;
			
			//now for the meat of the gun, we scan from the head
			State state = head.reverse.reverse; //atleast 2 back
			int deepestDepth = 2; //to save time
			Point2D.Double predictedLocation = null;
			while(state.reverse != null) {
				if(!state.isBreak && matchState(state, head)) {
					State state2 = state.reverse;
					State recent = head.reverse;
					int depth = 1; //could be 0 aswell
					//check to see if the state is a match for the current state
					while(matchState(state2,recent) && depth <= MAX_MATCH_LENGTH) {
						depth++;
						state2 = state2.reverse;
						recent = recent.reverse;
					}
					//now play it forward to determine where to aim, but
					//only if the depth was deeper then the deepest
					if(depth > deepestDepth) {
						state2 = state.forward;
						Point2D.Double predict = eLocation;
						double bulletDistance = eDistance;
						double heading = eHeading;
						double velocity = eVelocity;
						ArrayList<Point2D.Double> list = new ArrayList<Point2D.Double>();
						while(battlefield.contains(predict) && bulletDistance > 0
								&& state2 != null && !state2.isBreak) {
							bulletDistance -= bulletSpeed;
							heading = normalRelativeAngle(heading + state2.headingDelta);
							velocity = max(-8, min(8,velocity + state2.velocityDelta));
							predict = project(predict,heading,velocity);
							list.add(predict);
							state2 = state2.forward;
						}
						if(battlefield.contains(predict) && bulletDistance < 1) {
							bestList = list;
							deepestDepth = depth;
							predictedLocation = predict;
						}
					}
				}
				state = state.reverse;
			}
			
			nextPredicted = predictedLocation;
			double aimAngle = eAbsoluteBearing;
			if(predictedLocation != null)
				aimAngle = absoluteAngle(myLocation, predictedLocation);
			double angle = normalRelativeAngle(aimAngle-getGunHeadingRadians());
			setTurnGunRightRadians(angle);
			setFire(bulletPower);
		}
	}
	
	public void onPaint(Graphics2D g) {
		g.setColor(Color.white);
		if(bestList != null) {
			Point2D.Double last = null;
			for(int i=0;i<bestList.size();i++) {
				Point2D.Double c = bestList.get(i);
				if(last == null) {
					g.setColor(Color.red);
					g.fillOval((int)c.x-4, (int)c.y-4, 8, 8);
					g.setColor(Color.white);
				} else {
					g.drawLine((int)last.x, (int)last.y, (int)c.x, (int)c.y);
				}
				last = c;
			}
		}
		if(nextPredicted != null) {
			g.drawString("Next Point: ("+(float)nextPredicted.x
				+","+(float)nextPredicted.y+")", 0, 5);
			g.setColor(Color.GREEN);
			g.fillOval((int)nextPredicted.x-4,
					(int)nextPredicted.y-4, 8, 8);
		} else {
			g.drawString("Next Point: null", 0, 5);
		}
		g.setColor(Color.white);
		g.drawString("Pattern Length: " + patternLength, 0, 20);
	}
	
	public boolean matchState(State s1, State s2) {
		if(s1 != null && s2 != null &&
			abs(s1.headingDelta - s2.headingDelta) <= MAX_ANGLE_DIFF
		&& abs(s1.velocityDelta - s2.velocityDelta) <= MAX_VELOCITY_DIFF) {
			return true;
		}
		return false;
	}
	
	public void pruneTail() {
		State temp = tail;
		tail = tail.forward;
		tail.reverse = null;
		temp.forward = null;
		patternLength--;
	}
	
	public void addState(double heading, double velocity) {
		patternLength++;
		State s = new State();
		s.headingDelta = normalRelativeAngle(heading - lastHeading);
		s.velocityDelta = velocity - lastVelocity;

		_addState(s);
	}
	
	public void addBreak() {
		patternLength++;
		State s = new State();
		s.isBreak = true;
		
		if(head == null) {
			head = s;
		} else {
			_addState(s);
		}
	}
	
	private void _addState(State s) {
		State tmp = head;
		head = s;
		head.reverse = tmp;
		tmp.forward = head;
		if(tail == null) {
			tail = tmp;
		}
	}
	
	public static final Point2D.Double project(Point2D.Double l, double a, double d){
		return new Point2D.Double(l.x + d*Math.sin(a), l.y + d*Math.cos(a));
	}
	public static final double absoluteAngle(Point2D source, Point2D target) {
		return Math.atan2(target.getX() - source.getX(), target.getY() - source.getY());
	}
}

//only in the same file for completeness
class State {
	double headingDelta, velocityDelta;
	boolean isBreak;
	State reverse, forward;
	
	public State() {
		isBreak = false;
	}
}

Comments

I made this bot for three reasons, one to jumpstart my creation of a decent pattern matching robot (probably folding), to gain more advanced knowledge of the nicities surrounding pattern matcher design, and to make a very well commented base bot for people who do not yet understand pattern-matchers (like me) and wanna try to learn. While the comments are a little flaky (as am I), I post it here for the world (or atleast the other members of the wiki) to edit and allow correct all my horrible programming mistakes and shabby commenting (even though CommentsAreBad?).

I'll post some challenge scores for it in a bit. --Chase-san


Robo Home | Chase-san | Changes | Preferences | AllPages
Edit revision 1 of this page | View other revisions | View current revision
Edited January 5, 2008 8:27 EST by Chase-san (diff)
Search: