Here you'll learn how to avoid walls and make a multi-mode bot.
A problem with all of the previous robots we've looked at is that they hit the walls a lot, and hitting the walls drains your energy. A better strategy would be to stop before you hit the walls. But how?
The first thing you need to do is decide how close we will allow our robot to get to the walls:
public class WallAvoider extends AdvancedRobot { ... private int wallMargin = 60;Next, we add a custom event that will be fired when a certain condition is true:
// Don't get too close to the walls addCustomEvent(new Condition("too_close_to_walls") { public boolean test() { return ( // we're too close to the left wall (getX() <= wallMargin || // or we're too close to the right wall getX() >= getBattleFieldWidth() - wallMargin || // or we're too close to the bottom wall getY() <= wallMargin || // or we're too close to the top wall getY() >= getBattleFieldHeight() - wallMargin) ); } });Note that we are creating an anonymous inner class with this call. (You guys will do a lot of this sort of thing when we do GUI stuff.) We need to override the test() method to return a boolean when our custom event occurs.
The next thing we need to do is handle the event, which can be done like so:
public void onCustomEvent(CustomEvent e) { if (e.getCondition().getName().equals("too_close_to_walls")) { // switch directions and move away moveDirection *= -1; setAhead(10000 * moveDirection); } }The problem with that approach, though is that this event could get fired over and over, causing us to rappidly switch back and forth, never actually moving away.
Sample robot: JiggleOfDeath demonstrates the flaw in the above approach. Match him up against Walls and watch him go down.
To avoid this "jiggle of death" we should have a variable that indicates that we're handling the event. We can declare another like so:
public class WallAvoider extends AdvancedRobot { ... private int tooCloseToWall = 0;Then handle the event a little smarter:
public void onCustomEvent(CustomEvent e) { if (e.getCondition().getName().equals("too_close_to_walls")) { if (tooCloseToWall <= 0) { // if we weren't already dealing with the walls, // we are now tooCloseToWall += wallMargin; setMaxVelocity(0); // stop!!! } } }
There are two last problems we need to solve. Firstly, we have a doMove() method where we put all our normal movement code. If we're trying to get away from a wall, we don't want our normal movement code to get called, creating (once again) the "jiggle of death". Secondly, we want to eventually return to "normal" movement, so we should have the tooCloseToWall variable "time out" eventually.
We can solve both these problems with the following doMove() implementation:
public void doMove() { // always square off against our enemy, turning slightly toward him setTurnRight(enemy.getBearing() + 90 - (10 * moveDirection)); // if we're close to the wall, eventually, we'll move away if (tooCloseToWall > 0) tooCloseToWall--; // normal movement: switch directions if we've stopped if (getVelocity() == 0) { setMaxVelocity(8); moveDirection *= -1; setAhead(10000 * moveDirection); } }
Sample robot: WallAvoider uses all the above code to avoid running into the walls. Match him up against Walls and note how he gently glides toward the sides but never (well, rarely) hits them. (Perfecting this bot is left (as always) as an exercise for the student.)
Besides the colors you chose, the biggest part of your robot's personality is in his movement code. On the other hand, different situations call for different tactics. Using the wall-avoiding as an example, you may want to code your bot to change "modes" based on certain criteria. Using your PartsBot exercise as a starting point, I can picture a robot with a method like this in it:
public void onRobotDeath(RobotDeathEvent e) { ... if (getOthers() > 10) { // a large group calls for fluid movement parts[TANK] = new CirclingTank(); } else if (getOthers() > 1) { // dodging is the best small-group tactic parts[TANK] = new DodgingTank(); } else if (getOthers() == 1) { // if there's only one bot left, hunt him down parts[TANK] = new SeekAndDestroy(); } ... }The details are left (as always) as an exercise for the student.
For your Robcode lab today, make a bot called PartsBot that uses the RobotPart interface, per the instructions on this page.
Note: The bot you make in this lab (or any previous lab) does not have to be the one you use in the showdown.