Active Games

Slot Players

About

Tanks is a simple game of stealth and tactics. You move around the map trying to find the enemy tanks and shoot them while avoiding getting shot. You are only sent the positions of tanks that you can see but are sent bullets at all times. This means that shooting will let the other tanks know where you are.

This game is classed as easy because there is not much data to handle and an effective bot can be made with less than one hundred lines of code. This does not mean that complex bots are not possible. There are many tricks and strategies that a tank can use to outsmart their opponent.


Rules

Each tick every tank can move in any direction or fire a shot in any direction. The tank moves 1 space per turn and each shot moves 3 spaces per turn.

You can see an enemy if there is a rectangle that can be drawn that contains both you and the other tanks that contains no walls.

Tanks have an ammo restraint to make trigger happy tanks less effective. Each tank has a maximum of 10 ammo.

Ammo pickups, shown as a pile of shots on the game viewer, fully restores a tank's ammo reserve. Furthermore, any kill will fully restore your ammo.

On death, you will be sent one set of data with the isAlive byte set to zero. There will be no other data in this dataset so do not attempt to read any, seriously, we are NOT hiding anything in this data.

One point is gained for every kill and one point is lost for every death, it is not possible to have negative points.

The coordinate system is defined so that (0, 0) is at the top left of the game board. So to move UP subtract one from your y coordinate.


On Each Game Tick

1.

The tanks are moved by each player. If two tanks try to move into the same spot they neither of them will move. Keep a look out for this to avoid getting your tank stuck in loops.

2.

Shots move X spaces. Any tanks on spaces that the shot moves into will be destroyed and lose a point while the tank than shot the shot will gain a point. Shots cannot pass through each other and cannot share the same space. If this does occur all colliding shots will be destroyed.

3.

Tanks fire shots. The shot appears in the space in-front of the tank killing any tank that is in that spot instantly. All tanks fire at the same time, so two tanks next to each other shooting will both die instantly.


Example Situations

Example 1

If both tanks fire towards each other they will both be killed as all tanks fire instantly. If one tank shoots and one tank moves up no tank will die as the tanks are moved each tick before shots are fired.

Example 2

The bullet is moving upwards. If the tank moves onto the bullet it will not be killed and the bullet will continue on its way. This is because tanks are only killed if a bullet moves onto the space that they occupy.

Example 3

Both tanks try to move into the central space at the same time. They are both cannot move as two tanks cannot occupy the same space. Neither tank can see the other one so make sure to check if your moves succeed to avoid getting stuck.

Example 4

The bullet is moving upwards. If the tank moves to the side it will not be killed as tanks are moved before shots are moved in each game tick.


Data protocol

There are two different ways to connect to the game. The first is to use the custom made Java API kindly written by Liam to connect to the game. The second is to use the Network class to connect to the game, this is the lower level API that all games use and allows access to the raw data sent from the server. The first option is more recommended for general use, but if you are using a language other than Java or know what you are doing the second approach may be more applicable.

High Level API

This is an API written by Liam to make the creation of Tank bots quicker. It handles the reading of the data and several common tasks for you to allow you to concentrate more on the more important things such as beating your opponents into submission.

There are several classes that you will need to pay attention to in order to use this API:

The brownshome.scriptwars.game.tanks.Direction class contains constants for each direction and methods for manipulating coordinates and directions.
The brownshome.scriptwars.game.tanks.Shot class describes a shot on the game grid.
The brownshome.scriptwars.game.tanks.Tank class describes a tank in the game world.
The brownshome.scriptwars.game.tanks.World class contains most of the methods that can be used to interact with the map, such as the position of ammo pickups, shots and walls.
The brownshome.scriptwars.game.tanks.Action class contains constants for all the actions a tank can make.
The brownshome.scriptwars.game.tanks.Coordinates class is an immutable (it cannot be edited) object containing the methods int getX() and int getY() that represents a position on the game world.
Finally, the brownshome.scriptwars.game.tanks.TankAPI contains all the functions needed to interact with the game server.

First call the constructor TankAPI(int id, String ip, String name) to create a TankAPI object to communicate with. At the start of each loop call nextTick(). This method gets data from the server and sends any actions you have set, it returns a boolean which will be false if the connection has failed for some reason. The get data from the server using the functions in the API and use it to decide what to do. Some useful functions are getVisibleShots(), getVisibleTanks(), getMap().isWall(Coordinates coordinate) and getMap().getTank(Coordinates coordinate). There are other functions that may be useful; a link to the documentation is provided at the bottom of this section.
Once you have decided what action to take call either move(Direction direction), shoot(Direction direction) or doNothing(). This will set what action your tank will do when nextTick() is called.

Documentation

Example Code

This is a basic AI showing how to use the classes. This AI will move randomly while shooting at any enemies it can see. The command to compile this AI is javac -cp "script-wars-client.jar" ExampleTankAI.java and the command to run this AI is java -cp ".;script-wars-client.jar" ExampleTankAI INSERT-ID-HERE


import java.io.IOException;

import brownshome.scriptwars.game.tanks.*;

public class ExampleTankAI {
	/**
	 * The main method to start the AI and connect to the server.
	 * 
	 * args[0] should contain the game id.
	 * You can request one from: http://script-wars.com/games/Tanks
	 * by clicking the 'Join' button.
	 * 
	 * @param args The input arguments containing the ID allocated by the server
	 * @throws IOException If we failed to connect to the server
	 */
	public static void main(String[] args) throws IOException {
		// args[0] should contain the game id.
		// You can request one from: http://script-wars.com/games/Tanks
		// by clicking the 'Join' button
		
		int id;
		if(args.length > 0){
			id = Integer.valueOf(args[0]);
		} else {
			System.out.println("Usage: JAVACOMMAND serverid");
			System.exit(1);
			return;
		}

		TankAPI api = new TankAPI(id, "www.script-wars.com", "Example AI 1");

		while(api.nextTick()) {
			if(!api.isAlive()) {
				continue;
			}

			// Move randomly, this will be overwritten if we can see someone.
			int direction = (int) (Math.random() * 4);
			api.move(Direction.values()[direction]);
	
			// See if there is a tank in our field of view,
			// and if there is select it.
			Tank targetTank = null;
			for(Tank tank : api.getVisibleTanks()){
				targetTank = tank;
			}
	
			// If we can see a tank, lets shoot it.
			if(targetTank != null){
				Coordinates targetPosition = targetTank.getPosition();
				Coordinates myPosition = api.getCurrentPosition();
		
				Direction targetDirection = Direction.getDirection(targetPosition, myPosition);
				if(targetDirection != null) {
					//We have a clear shot on the target
					api.shoot(targetDirection);
				}
			}
	
			System.out.println("Position: " + api.getCurrentPosition());
		}
		
		System.out.println("Disconnected from server:\n\t" + api.getConnectionStatus());
	}
}

Low Level API

This is a simpler API that is used internally by all games and all high level APIs. Due to it's simplicity it is sometimes the only API that can be used if the game is new or you are not writing your AI in Java.

To use this API you create a brownshome.scriptwars.connection.Network object using the Network(int ID, String ip, String name) constructor. Then, in your loop, call nextTick() to get a new set of data from the server and send queued data. This function will return false if there is a problem with the connection. Then extract data from the server using this getX() family of functions, where X is the data type you want. And send data using the sendX() family of functions.

Documentation

For the tank game in particular the data received from the server is laid out in the table below.

Name Amount Type Meaning
isAlive 1 Byte 0 if the player is dead, 1 if the player is dead. There will be no more data to follow if this value is 0.
ammoRemaining 1 Byte The amount of ammunition you have remaining.
xPos 1 Byte The x coordinate of the player.
yPos 1 Byte The y coordinate of the player.
gridWidth 1 Byte The width of the game grid.
gridHeight 1 Byte The height of the game grid.
isWall gridWidth * gridHeight Boolean true if there is a wall, false otherwise. The values are from top to bottom, left to right, row by row. e.g. (0, 0) to (gridWidth - 1, 0) then (1, 0) ...
numberOfTanks 1 Byte The number of tanks that can be seen by the player.
tankData numberOfTanks (Byte, Byte, Byte) The x and y coordinates and a unique ID of a tank that can be seen. No guarantees are made about the order of the tanks. If a tank disconnects it's ID may be re-used
numberOfShots 1 Byte The number of shots in the game
shotData numberOfShots (Byte, Byte, Byte) The x and y coordinates followed by the direction of the shot. The values for specific directions are shown below.
numberOfAmmoPickups 1 Byte The number of ammo pickups in the game
ammoPickupData numberOfAmmoPickups (Byte, Byte) The x and y coordinates of the pickup

Each turn two bytes must be sent to the server.

Name Amount Type Meaning
action 1 Byte The value given in the action table. If the action is nothing then there doesn't need to be a direction byte send.
direction 1 Byte The direction that the action is to take place in.

The values of the direction and action bytes are given below.

Action Byte

Value Meaning
0No action (No direction required)
1Move
2Shoot

Direction Byte

Value Meaning
0Up
1Left
2Down
3Right

Example Code


import java.io.IOException;

import brownshome.scriptwars.connection.Network;

/**
 * This is an example of reading data from the server. We avoid using the pre-built Tank and
 * Shot classes as they would not be available in languages other than Java. The actual processing
 * of the data is left to the reader. But structs or classes would be useful.
 * 
 * @author James
 */
public class ExampleTankAIBasic {
	private static Network network;
	
	/**
	 * The main method to start the AI and connect to the server.
	 * 
	 * args[0] should contain the game id.
	 * You can request one from: http://script-wars.com/games/Tanks
	 * by clicking the 'Join' button.
	 * 
	 * @param args The input arguments containing the ID allocated by the server
	 * @throws IOException If we failed to connect to the server
	 */
	public static void main(String[] args) throws IOException {
		// args[0] should contain the game id.
		// You can request one from: http://script-wars.com/games/Tanks
		// by clicking the 'Join' button

		int id;
		if(args.length > 0){
			id = Integer.valueOf(args[0]);
		} else {
			System.out.println("Usage: JAVACOMMAND serverid");
			System.exit(1);
			return;
		}

		network = new Network(id, "www.script-wars.com", "John Smith Low Level");
		int direction = 0; //The initial direction, UP
		boolean hasHitWall = false;

		while(network.nextTick()) {
			boolean isAlive = network.getByte() == 1;
			if(!isAlive) {
				network.sendByte(0);
				System.out.println("We are dead, not big surprise");
				continue;
			}
			
			int ammo = network.getByte();
			
			int xPos = network.getByte();
			int yPos = network.getByte();
			
			int width = network.getByte();
			int height = network.getByte();
			
			boolean[][] map = new boolean[width][height];
			for(int y = 0; y < height; y++) {
				for(int x = 0; x < width; x++) {
					map[y][x] = network.getBoolean();
				}
			}
			
			int noTanks = network.getByte();
			
			int[][] tanks = new int[noTanks][3]; //An array holding the X, Y and ID of each tank
			for(int i = 0; i < noTanks; i++) {
				tanks[i][0] = network.getByte();
				tanks[i][1] = network.getByte();
				tanks[i][2] = network.getByte();
			}
			
			int noShots = network.getByte();
			
			int[][] shots = new int[noShots][3]; //An array holding the X, Y and direction of each shot
			for(int i = 0; i < noShots; i++) {
				shots[i][0] = network.getByte();
				shots[i][1] = network.getByte();
				shots[i][2] = network.getByte();
			}
			
			int noAmmo = network.getByte();
			int[][] ammoPickups = new int[noAmmo][2];
			for(int i = 0; i < noAmmo; i++) {
				ammoPickups[i][0] = network.getByte();
				ammoPickups[i][1] = network.getByte();
			}
			
			//For the actual AI, we shall hug the left wall and keep moving around, should keep things interesting
			//Check the anti-clockwise direction, then the straight, then the clockwise direction, then the reverse
			int antiClock = (direction + 1) % 4; //Cool right : D
			int clockwise = (direction + 3) % 4;
			int reverse = (direction + 2) % 4;
			
			if(isWall(direction, xPos, yPos, map)) {
				hasHitWall = true;
			}
			
			if(hasHitWall) {
				if(!isWall(antiClock, xPos, yPos, map)) {
					direction = antiClock;
				} else if(!isWall(direction, xPos, yPos, map)){
					//Don't change the direction
				} else if(!isWall(clockwise, xPos, yPos, map)) {
					direction = clockwise;
				} else if(!isWall(reverse, xPos, yPos, map)) {
					direction = reverse;
				} else {
					//We are trapped in a box, time to panic, and panic hard
				}
			}
			
			network.sendByte(1); //Move
			network.sendByte(direction);
		}
		
		System.out.println(network.getConnectionStatus()); //This function may not be available on some languages.
	}
	
	/**
	 * Check if there is a wall in the direction we are trying to go.
	 */
	private static boolean isWall(int direction, int xPos, int yPos, boolean[][] map) {
		switch(direction) {
			case 0: //UP
				yPos--;
				break;
			case 1: //LEFT
				xPos--;
				break;
			case 2: //DOWN
				yPos++;
				break;
			case 3: //RIGHT
				xPos++;
				break;
		}
		
		//x and yPos are now the next space.
		return map[yPos][xPos];
	}
}