Difference between revisions of "User:Exauge/snippets"

From Robowiki
Jump to navigation Jump to search
m (correction)
m (Using <syntaxhighlight>.)
 
(7 intermediate revisions by one other user not shown)
Line 2: Line 2:
 
These are just a few code snippets to help you get a robot up and going nice and fast :)
 
These are just a few code snippets to help you get a robot up and going nice and fast :)
 
== Movement ==
 
== Movement ==
=== Oscillator Movement ===
+
=== Random Movement ===
This is my strong (at least many small bots have a hard time hitting it), but small and easy to implement [[Oscillator Movement]]. It tries to stay away from walls, but isn't perfect so it can still collide sometimes. It works fairly well in smaller bots (nanos and micros) but a stronger movement will probably be needed for larget bots (minis and megas).
+
This is my strong (at least many small bots have a hard time hitting it), but small and easy to implement [[Random Movement]]. It tries to stay away from walls, but isn't perfect so it can still collide sometimes. It works fairly well in smaller bots (nanos and micros) but a stronger movement will probably be needed for largest bots (minis and megas). My robot [[GateKeeper]] uses an extremely lightweight version of this movement. You can see it from its source. Completely random = travels in random direction, normal random = travels in radians relative to enemy position.
I'll start with '''The Bare Minimum''' *Note the bare minimum is not an oscillator movement*:
+
I'll start with '''The Bare Minimum''' as a completely [[Random Movement]] but you can use it as an normal random movement as well:
<pre>
+
<syntaxhighlight>
 
public class YOURROBOTNAME extends AdvancedRobot {
 
public class YOURROBOTNAME extends AdvancedRobot {
  
Line 12: Line 12:
 
public void onScannedRobot(ScannedRobotEvent e) {
 
public void onScannedRobot(ScannedRobotEvent e) {
 
if(eEner > (eEner = e.getEnergy())) {
 
if(eEner > (eEner = e.getEnergy())) {
setTurnRight((Math.random()*360)-180);
+
setTurnRight(Math.random()*360-180); // Delete this line if you use normal random
 
setAhead(((Math.random()*700)-(350))*1.2);
 
setAhead(((Math.random()*700)-(350))*1.2);
 
}
 
}
 +
//setTurnRightRadians(Math.cos(e.getBearingRadians())); // Uncomment this line for normal random movement
 
}
 
}
 
}
 
}
</pre>
+
</syntaxhighlight>
Next let's add a few more advanced features like changing velocity and oscillation:
+
Next let's add a few more advanced features like changing velocity:
<pre>
+
<syntaxhighlight>
 
public class YOURROBOTNAME extends AdvancedRobot {
 
public class YOURROBOTNAME extends AdvancedRobot {
  
Line 28: Line 29:
 
if(eEner > (eEner = e.getEnergy())) {
 
if(eEner > (eEner = e.getEnergy())) {
 
setMaxVelocity(16*Math.random()+5);
 
setMaxVelocity(16*Math.random()+5);
setTurnRight((Math.random()*360)-180);
+
setTurnRight(Math.random()*360-180); // Delete this line if you use normal random
 
setAhead(((Math.random()*700)-(350))*1.2);
 
setAhead(((Math.random()*700)-(350))*1.2);
setTurnRightRadians((Math.cos(absB = e.getBearingRadians())));
 
 
}
 
}
 +
//setTurnRightRadians(Math.cos(e.getBearingRadians())); // Uncomment this line for normal random
 
}
 
}
 
}
 
}
</pre>
+
</syntaxhighlight>
 
Unfortunately, something like the above two will probably bump into walls a lot so lets add a little wall avoidance:
 
Unfortunately, something like the above two will probably bump into walls a lot so lets add a little wall avoidance:
<pre>
+
<syntaxhighlight>
 
public class YOURROBOTNAME extends AdvancedRobot {
 
public class YOURROBOTNAME extends AdvancedRobot {
  
 
static double eEner;
 
static double eEner;
 +
int backDir = 1;
  
 
public void onHitWall(HitWallEvent event){
 
public void onHitWall(HitWallEvent event){
Line 49: Line 51:
 
if(eEner > (eEner = e.getEnergy())) {
 
if(eEner > (eEner = e.getEnergy())) {
 
setMaxVelocity(16*Math.random()+5);
 
setMaxVelocity(16*Math.random()+5);
setTurnRight((Math.random()*360)-180);
+
setTurnRight(Math.random()*360-180); // Delete this line if you use normal random
 
setAhead(((Math.random()*700)-(350))*1.2);
 
setAhead(((Math.random()*700)-(350))*1.2);
setTurnRightRadians((Math.cos(absB = e.getBearingRadians())));
 
 
}
 
}
 
if (mxMv < 30) {
 
if (mxMv < 30) {
 
setBack(75 * backDir);
 
setBack(75 * backDir);
 
}
 
}
 +
//setTurnRightRadians(Math.cos(e.getBearingRadians())); // Uncomment this line for normal random
 
}
 
}
 
}
 
}
</pre>
+
</syntaxhighlight>
Now for the entire thing. Any of these can be used but obviously the fewer features the smaller the code size.
+
Now for the entire thing with changed move amount on bullet hit.. Any of these can be used but obviously the fewer features the smaller the code size.
<pre>
+
<syntaxhighlight>
 
public class YOURROBOTNAME extends AdvancedRobot {
 
public class YOURROBOTNAME extends AdvancedRobot {
  
 +
int moveNeg = 1;
 +
double randInt = 1;
 +
double randHit = 1;
 
static double eEner;
 
static double eEner;
  
 
public void onHitWall(HitWallEvent event){
 
public void onHitWall(HitWallEvent event){
 
backDir = backDir * -1;
 
backDir = backDir * -1;
 +
}
 +
 +
public void onHitByBullet(HitByBulletEvent e) {
 +
moveNeg = moveNeg * -1;
 +
randInt = Math.random() + .5;
 +
randHit = randInt * randInt * moveNeg;
 
}
 
}
  
Line 75: Line 86:
 
if(eEner > (eEner = e.getEnergy())) {
 
if(eEner > (eEner = e.getEnergy())) {
 
setMaxVelocity(16*Math.random()+5);
 
setMaxVelocity(16*Math.random()+5);
setTurnRight((Math.random()*360)-180);
+
setTurnRight(Math.random()*360-180); // Delete this line if you use normal random
 
setAhead(((Math.random()*mvAvg)-(mvAvg*.5))*.8*randHit);
 
setAhead(((Math.random()*mvAvg)-(mvAvg*.5))*.8*randHit);
setTurnRightRadians((Math.cos(absB = e.getBearingRadians())));
 
 
}
 
}
if (mxMv < 30) {
+
if (mxMv < 45) {
 
setBack(75 * backDir);
 
setBack(75 * backDir);
 
}
 
}
 +
//setTurnRightRadians(Math.cos(e.getBearingRadians())); // Uncomment this line for normal random
 
}
 
}
 
}
 
}
</pre>
+
</syntaxhighlight>
  
 
== Targeting ==
 
== Targeting ==
 
=== Pattern Matching Gun ===
 
=== Pattern Matching Gun ===
 +
<code>Approx Codesize: 130 bytes</code>
 +
 
This is a pattern matching gun which is a slightly altered version of the one on Robar's Blackwidow. Again, it is a small pattern matcher intended for small bots. A more advanced code will probably work better with larger bots. If you look at the source code of almost every nano and micro, their pattern matcher will look almost exactly like this. That is because the lighter version of the pattern matchers were all for the most part based on FunkyChicken's pattern matcher.
 
This is a pattern matching gun which is a slightly altered version of the one on Robar's Blackwidow. Again, it is a small pattern matcher intended for small bots. A more advanced code will probably work better with larger bots. If you look at the source code of almost every nano and micro, their pattern matcher will look almost exactly like this. That is because the lighter version of the pattern matchers were all for the most part based on FunkyChicken's pattern matcher.
<pre>
+
<syntaxhighlight>
 
public class YOURROBOTNAME extends AdvancedRobot {
 
public class YOURROBOTNAME extends AdvancedRobot {
  
Line 113: Line 126:
 
}
 
}
 
}
 
}
</pre>
+
</syntaxhighlight>
 +
=== Linear Targeting ===
 +
<code>Approx Codesize: 50 bytes</code>
 +
 
 +
[[Linear targeting]] is just a step ahead of [[Head-On Targeting]]. Most robots don't just stay in one point - they move. The idea behind linear targeting is that you can easily predict exactly where the enemy will be if they continue to move in a strait line at the same velocity. It is fairly effective against some robots with basic movement but usually isn't used outside the [[NanoBots|nanobot]] class and it is less-commonly used in the [[MicroBots|microbot]] class. The major advantage of [[Linear Targeting]] is that is has a small code size and it is better than [[Head-On Targeting]]. Think of linear targeting as a triangle with one vertex being your robot, another being the enemy robot, and the third being the point that the bullet will hit them. The angle at your robot's vertex will be the angle that your radar would need to turn if it were going to hit the enemy.
 +
 
 +
[[File:Lin_targ.jpg]]
 +
 
 +
So how can that angle be found? It's quite simple.
 +
<syntaxhighlight>
 +
public class YOURROBOTNAME extends AdvancedRobot {
 +
 
 +
public void onScannedRobot(ScannedRobotEvent e) {
 +
int bPow = 2; // change as you wish, but note if you use a decimal you must change int to double, which takes more codesize
 +
int bVel = 20 - (bPow * 3); // again, if you make bPow a decimal (such as 2.5) you much change this from int to double as well
 +
double absoluteBearing = getHeadingRadians() + e.getBearingRadians();
 +
setTurnGunRightRadians(Utils.normalRelativeAngle(absoluteBearing -
 +
    getGunHeadingRadians() + (e.getVelocity() * Math.sin(e.getHeadingRadians() -
 +
    absoluteBearing) / bVel)));
 +
setFire(bPow);
 +
}
 +
}
 +
</syntaxhighlight>
 +
Basically, this tells your robot to fire at the spot where the enemy will be when your bullet hits if it continues to move at the same heading and velocity. More thorough explanation / better code for it can be found in the [[Linear Targeting]] article.
  
 
== Radar ==
 
== Radar ==
 
=== Infinity Radar Lock ===
 
=== Infinity Radar Lock ===
 +
<code>Approx Codesize: 15 bytes</code>
 +
 
The [[Radar#The_infinity_lock|infinity lock]] is very easy to implement and has a very small code size which is why I chose it for my first robot. For other radar locks see [[radar]]. **NOTE TO MAKE THE PATTERN MATCHER OR RANDOM MOVEMENT ABOVE WORK PROPERLY, YOU WILL NEED A RADAR LOCK OF SOME TYPE**
 
The [[Radar#The_infinity_lock|infinity lock]] is very easy to implement and has a very small code size which is why I chose it for my first robot. For other radar locks see [[radar]]. **NOTE TO MAKE THE PATTERN MATCHER OR RANDOM MOVEMENT ABOVE WORK PROPERLY, YOU WILL NEED A RADAR LOCK OF SOME TYPE**
  
 
It's really quite simple to use. In your public void run, insert setTurnRadarRight(Double.POSITIVE_INFINITY); so that it will look something like this:
 
It's really quite simple to use. In your public void run, insert setTurnRadarRight(Double.POSITIVE_INFINITY); so that it will look something like this:
<pre>
+
<syntaxhighlight>
 
public void run() {
 
public void run() {
 
setTurnRadarRight(Double.POSITIVE_INFINITY); // Radar Lock
 
setTurnRadarRight(Double.POSITIVE_INFINITY); // Radar Lock
 
}
 
}
</pre>
+
</syntaxhighlight>
 
Then at the end of your onscannedrobot, insert setTurnRadarLeft(getRadarTurnRemaining()); so that it will look something like this:
 
Then at the end of your onscannedrobot, insert setTurnRadarLeft(getRadarTurnRemaining()); so that it will look something like this:
<pre>
+
<syntaxhighlight>
 
public void onScannedRobot(ScannedRobotEvent e) {
 
public void onScannedRobot(ScannedRobotEvent e) {
 
// Your movement code... bla bla
 
// Your movement code... bla bla
Line 132: Line 170:
 
setTurnRadarLeft(getRadarTurnRemaining());
 
setTurnRadarLeft(getRadarTurnRemaining());
 
}
 
}
</pre>
+
</syntaxhighlight>
 
And there you have it. You have just implemented a radar lock.
 
And there you have it. You have just implemented a radar lock.
 +
 +
== Other ==
 +
=== Multi-mode Movement ===
 +
<code>Approx Codesize: 65 bytes for each movement used</code>
 +
 +
Multi-mode movement is very common in most bots in the micro class or above. There are several ways to implement it.
 +
<syntaxhighlight>
 +
public class YOURROBOTNAME extends AdvancedRobot {
 +
 +
static double bestMove1 = 0;
 +
static double bestMove2 = 0;
 +
static int moveNum = 0;
 +
static int changeMove = 1;
 +
static int winCount = 0;
 +
double eEner;
 +
 +
// some other code...
 +
 +
public void onScannedRobot(ScannedRobotEvent e) {
 +
eEner = e.getEnergy(); // store the enemy energy
 +
if(moveNum < 1){ // if we use movement 1,
 +
// Movement code for first movement
 +
}
 +
if(moveNum >= 1 && moveNum < 2){ // if we use movement 2,
 +
// Movement code for second movement
 +
}
 +
if(moveNum >= 2){ // if we've used both movements, let's decide which worked better
 +
changeMove = 0; // don't change our movement any more.
 +
if(bestMove2 > bestMove1){ // if movement 2 did better than movement 1,
 +
moveNum = 1; // use movement 2
 +
}
 +
if(bestMove1 >= bestMove2){ // else if movement 1 did better than movement 2,
 +
moveNum = 0; // use movement 1
 +
}
 +
}
 +
// gun code might go here...
 +
// radar code...
 +
}
 +
 +
public void onDeath(DeathEvent event){ // if we die
 +
if (winCount < 4){ // if we've 4 battles with the movement before it changes, we'll assume it's working
 +
moveNum += ((1/4)*changeMove); // every 4 losses, switch movements
 +
}
 +
if(moveNum < 1){ // if we used movement 1 when we died
 +
bestMove1 -= eEner; // subtract how much energy the enemy had left
 +
}
 +
if(moveNum >= 1){ // if we used movement 2 when we died
 +
bestMove2 -= eEner; // subtract how much energy the enemy had left
 +
}
 +
}
 +
 +
public void onWin(WinEvent event){ // if we win and
 +
if(moveNum < 1){ // if we are using movement 1
 +
bestMove1 += getEnergy(); // add the energy we had left to the movement 1 counter
 +
winCount += 1; // add a win to the victory counter
 +
}
 +
if(moveNum >= 1){ // and if we are using movement 2
 +
bestMove2 += getEnergy(); // add the energy we had left to the movement 2 counter
 +
}
 +
}
 +
}
 +
</syntaxhighlight>
  
 
[[Category:Code Snippets]]
 
[[Category:Code Snippets]]

Latest revision as of 09:36, 1 July 2010

About

These are just a few code snippets to help you get a robot up and going nice and fast :)

Movement

Random Movement

This is my strong (at least many small bots have a hard time hitting it), but small and easy to implement Random Movement. It tries to stay away from walls, but isn't perfect so it can still collide sometimes. It works fairly well in smaller bots (nanos and micros) but a stronger movement will probably be needed for largest bots (minis and megas). My robot GateKeeper uses an extremely lightweight version of this movement. You can see it from its source. Completely random = travels in random direction, normal random = travels in radians relative to enemy position. I'll start with The Bare Minimum as a completely Random Movement but you can use it as an normal random movement as well:

public class YOURROBOTNAME extends AdvancedRobot {

static double eEner;

public void onScannedRobot(ScannedRobotEvent e) {
	if(eEner > (eEner = e.getEnergy())) {
		setTurnRight(Math.random()*360-180); // Delete this line if you use normal random
		setAhead(((Math.random()*700)-(350))*1.2);
	}
		//setTurnRightRadians(Math.cos(e.getBearingRadians())); // Uncomment this line for normal random movement
}
}

Next let's add a few more advanced features like changing velocity:

public class YOURROBOTNAME extends AdvancedRobot {

static double eEner;
double backDir = 1;

public void onScannedRobot(ScannedRobotEvent e) {
	if(eEner > (eEner = e.getEnergy())) {
		setMaxVelocity(16*Math.random()+5);
		setTurnRight(Math.random()*360-180); // Delete this line if you use normal random
		setAhead(((Math.random()*700)-(350))*1.2);
	}
		//setTurnRightRadians(Math.cos(e.getBearingRadians())); // Uncomment this line for normal random
}
}

Unfortunately, something like the above two will probably bump into walls a lot so lets add a little wall avoidance:

public class YOURROBOTNAME extends AdvancedRobot {

static double eEner;
int backDir = 1;

public void onHitWall(HitWallEvent event){
	backDir = backDir * -1;
}

public void onScannedRobot(ScannedRobotEvent e) {
	double mxMv = Math.min(Math.min((getBattleFieldWidth()-getX()), (getBattleFieldHeight()-getY())), Math.min(getX(), getY()));
	if(eEner > (eEner = e.getEnergy())) {
		setMaxVelocity(16*Math.random()+5);
		setTurnRight(Math.random()*360-180); // Delete this line if you use normal random
		setAhead(((Math.random()*700)-(350))*1.2);
	}
	if (mxMv < 30) {
		setBack(75 * backDir);
	}
		//setTurnRightRadians(Math.cos(e.getBearingRadians())); // Uncomment this line for normal random
}
}

Now for the entire thing with changed move amount on bullet hit.. Any of these can be used but obviously the fewer features the smaller the code size.

public class YOURROBOTNAME extends AdvancedRobot {

int moveNeg = 1;
double randInt = 1;
double randHit = 1;
static double eEner;

public void onHitWall(HitWallEvent event){
	backDir = backDir * -1;
}

public void onHitByBullet(HitByBulletEvent e) {
	moveNeg = moveNeg * -1;
	randInt = Math.random() + .5;
	randHit = randInt * randInt * moveNeg;
}

public void onScannedRobot(ScannedRobotEvent e) {
	double mxMv = Math.min(Math.min((getBattleFieldWidth()-getX()), (getBattleFieldHeight()-getY())), Math.min(getX(), getY()));
	double mvAmount = Math.max(getBattleFieldWidth(), getBattleFieldHeight());
	double mvAvg = (mxMv+mvAmount)/2;
	if(eEner > (eEner = e.getEnergy())) {
		setMaxVelocity(16*Math.random()+5);
		setTurnRight(Math.random()*360-180); // Delete this line if you use normal random
		setAhead(((Math.random()*mvAvg)-(mvAvg*.5))*.8*randHit);
	}
	if (mxMv < 45) {
		setBack(75 * backDir);
	}
		//setTurnRightRadians(Math.cos(e.getBearingRadians())); // Uncomment this line for normal random
}
}

Targeting

Pattern Matching Gun

Approx Codesize: 130 bytes

This is a pattern matching gun which is a slightly altered version of the one on Robar's Blackwidow. Again, it is a small pattern matcher intended for small bots. A more advanced code will probably work better with larger bots. If you look at the source code of almost every nano and micro, their pattern matcher will look almost exactly like this. That is because the lighter version of the pattern matchers were all for the most part based on FunkyChicken's pattern matcher.

public class YOURROBOTNAME extends AdvancedRobot {

static final double bPow = 2.2; // Bullet Power - change it to whatever you like.
static final double bVel = 20-3*bPow;
static final int patDep = 30;
static String eLog = "00000000000000000000000000888888";

public void onScannedRobot(ScannedRobotEvent e) {
	int i;
	double absB;
	int mLen = patDep;
	int indX;
	setTurnRightRadians((Math.cos(absB = e.getBearingRadians())));
	eLog = String.valueOf( (char)Math.round(e.getVelocity() * Math.sin(e.getHeadingRadians() - ( absB+=getHeadingRadians() )))).concat(eLog);
	while((indX = eLog.indexOf(eLog.substring(0, mLen--), (i = (int)((e.getDistance())/bVel)))) < 0);
	do{
		absB += Math.asin(((byte)eLog.charAt(indX--))/e.getDistance());
	}
	while(--i > 0);
	setTurnGunRightRadians(Utils.normalRelativeAngle(absB-getGunHeadingRadians()));
	setFire(bPow);
	}
}

Linear Targeting

Approx Codesize: 50 bytes

Linear targeting is just a step ahead of Head-On Targeting. Most robots don't just stay in one point - they move. The idea behind linear targeting is that you can easily predict exactly where the enemy will be if they continue to move in a strait line at the same velocity. It is fairly effective against some robots with basic movement but usually isn't used outside the nanobot class and it is less-commonly used in the microbot class. The major advantage of Linear Targeting is that is has a small code size and it is better than Head-On Targeting. Think of linear targeting as a triangle with one vertex being your robot, another being the enemy robot, and the third being the point that the bullet will hit them. The angle at your robot's vertex will be the angle that your radar would need to turn if it were going to hit the enemy.

Lin targ.jpg

So how can that angle be found? It's quite simple.

public class YOURROBOTNAME extends AdvancedRobot {

	public void onScannedRobot(ScannedRobotEvent e) {
		int bPow = 2; // change as you wish, but note if you use a decimal you must change int to double, which takes more codesize
		int bVel = 20 - (bPow * 3); // again, if you make bPow a decimal (such as 2.5) you much change this from int to double as well
		double absoluteBearing = getHeadingRadians() + e.getBearingRadians();
		setTurnGunRightRadians(Utils.normalRelativeAngle(absoluteBearing - 
		    getGunHeadingRadians() + (e.getVelocity() * Math.sin(e.getHeadingRadians() - 
		    absoluteBearing) / bVel)));
		setFire(bPow);
	}
}

Basically, this tells your robot to fire at the spot where the enemy will be when your bullet hits if it continues to move at the same heading and velocity. More thorough explanation / better code for it can be found in the Linear Targeting article.

Radar

Infinity Radar Lock

Approx Codesize: 15 bytes

The infinity lock is very easy to implement and has a very small code size which is why I chose it for my first robot. For other radar locks see radar. **NOTE TO MAKE THE PATTERN MATCHER OR RANDOM MOVEMENT ABOVE WORK PROPERLY, YOU WILL NEED A RADAR LOCK OF SOME TYPE**

It's really quite simple to use. In your public void run, insert setTurnRadarRight(Double.POSITIVE_INFINITY); so that it will look something like this:

public void run() {
	setTurnRadarRight(Double.POSITIVE_INFINITY); // Radar Lock
}

Then at the end of your onscannedrobot, insert setTurnRadarLeft(getRadarTurnRemaining()); so that it will look something like this:

public void onScannedRobot(ScannedRobotEvent e) {
	// Your movement code... bla bla
	// Your gun code... bla bla
	setTurnRadarLeft(getRadarTurnRemaining());
}

And there you have it. You have just implemented a radar lock.

Other

Multi-mode Movement

Approx Codesize: 65 bytes for each movement used

Multi-mode movement is very common in most bots in the micro class or above. There are several ways to implement it.

public class YOURROBOTNAME extends AdvancedRobot {

static double bestMove1 = 0;
static double bestMove2 = 0;
static int moveNum = 0;
static int changeMove = 1;
static int winCount = 0;
double eEner;

// some other code...

	public void onScannedRobot(ScannedRobotEvent e) {
		eEner = e.getEnergy(); // store the enemy energy
		if(moveNum < 1){ // if we use movement 1,
			// Movement code for first movement
		}
		if(moveNum >= 1 && moveNum < 2){ // if we use movement 2,
			// Movement code for second movement
		}
		if(moveNum >= 2){ // if we've used both movements, let's decide which worked better
			changeMove = 0; // don't change our movement any more.
			if(bestMove2 > bestMove1){ // if movement 2 did better than movement 1,
				moveNum = 1; // use movement 2
			}
			if(bestMove1 >= bestMove2){ // else if movement 1 did better than movement 2,
				moveNum = 0; // use movement 1
			}
		}
		// gun code might go here...
		// radar code...
	}
	
	public void onDeath(DeathEvent event){ // if we die
		if (winCount < 4){ // if we've 4 battles with the movement before it changes, we'll assume it's working
			moveNum += ((1/4)*changeMove); // every 4 losses, switch movements
		}
		if(moveNum < 1){ // if we used movement 1 when we died
			bestMove1 -= eEner; // subtract how much energy the enemy had left
		}
		if(moveNum >= 1){ // if we used movement 2 when we died
			bestMove2 -= eEner; // subtract how much energy the enemy had left
		}
	}
	
	public void onWin(WinEvent event){ // if we win and
		if(moveNum < 1){ // if we are using movement 1
			bestMove1 += getEnergy(); // add the energy we had left to the movement 1 counter
			winCount += 1; // add a win to the victory counter
		}
		if(moveNum >= 1){ // and if we are using movement 2
			bestMove2 += getEnergy(); // add the energy we had left to the movement 2 counter
		}
	}
}