Robot Battle: how to play

Quick start

You write one C# class. Every tick of the battle (30 per second, at most 5000 ticks) the arena calls your Tick method. Look at your state, queue some actions, and return. That is the whole interface.

using System;
using System.Linq;
using RobotBattle.Api;

public class MyRobot : Robot
{
    public override string Name => "My Robot";

    public override void Tick(RobotState s, Actions a)
    {
        a.Move(2);                 // drive forwards
        a.Turn(3);                 // ...turning gently
        a.Scan(s.TurretHeading, 40);   // look along the gun

        var enemy = s.LastScan?.Hits.FirstOrDefault(h => h.Type == HitType.Robot);
        if (enemy != null)
            a.Shoot(enemy.Distance, 1);
    }
}
  1. Sign in with a name, then write or load a sample in the editor.
  2. Give it a name and press Compile & upload. Errors show with line numbers.
  3. Tick two or more robots in the list (yours or anyone else's), choose how many rounds, and press Fight!.
  4. Watch the replay round by round (rounds play one after another, or click a round number). You can pause, scrub and change speed, and show or hide scan arcs. Your robot's rating shows on the leaderboard.

Your class must be public, derive from Robot and have a public parameterless constructor. Only one such class per submission, but you can add as many helper classes as you like. A fresh instance is created for every battle, so fields keep their values across ticks and reset between battles.

The arena

  • The arena is 800 × 600 units. (0, 0) is the top-left corner; x grows to the right and y grows downwards.
  • Angles are in degrees. 0 is east (right), 90 is south (down), 180 is west, 270 is north. Angles increase clockwise as you see them on screen. Bearings and headings are absolute angles in the arena; nothing is relative to your robot.
  • Robots are discs of radius 18. Five round obstacles (radius 20–40) are scattered about at random. Robots and obstacles are solid.
  • Robots start at random positions, at least 150 apart where there is room, with a random heading and the turret pointing the same way.
  • You know the arena size (ArenaWidth, ArenaHeight) and where you are. You do not know where the enemies or the obstacles are until you scan.
  • Battles are deterministic given a seed, apart from anything your own code does with System.Random.

Reading your state

Tick(RobotState s, Actions a) gets a fresh RobotState every tick.

PropertyMeaning
TickTick number, starting at 0.
ArenaWidth, ArenaHeightSize of the arena.
X, YYour position (centre of the robot).
EnergyYour remaining energy. This is also your health. See Energy.
HeadingThe direction your body faces, in degrees (0–360).
TurretHeadingThe direction your gun points, in degrees (0–360). Bullets leave in this direction.
SpeedYour current speed in units per tick (negative when reversing).
LastScanThe result of the scan you requested last tick, or null if you did not request one (or could not afford it).
DamageTakenEnergy lost to explosions since your previous tick.
HitWallTrue if you were blocked last tick: you ran into the arena edge, an obstacle or another robot.

Actions

Call these on the Actions object. A tick starts with nothing queued. If you call the same action twice in one tick, the last call wins. Values outside the limits are silently clamped; NaN is ignored.

CallWhat it doesLimit
Move(speed)Sets your speed. Positive is forwards, negative is backwards. The speed stays set until you change it, so you do not need to call it every tick. Hitting something stops you (speed becomes 0).−4 to +4 units per tick
Turn(degrees)Turns the body by this many degrees this tick. Positive is clockwise. Not persistent.±8° per tick
TurnTurret(degrees)Turns the gun by this many degrees this tick, independently of the body. Not persistent.±12° per tick
Shoot(distance, power)Fires a bullet that explodes distance units from your centre, in the direction the turret points. Power defaults to 1.distance 1–600, power 0.1–3, one shot every 10 ticks
Scan(direction, arc)Looks in an arc centred on the absolute angle direction, arc degrees wide. Results arrive next tick in LastScan. The scan direction is independent of the turret.arc 0–60°
Recharge()Spends the whole tick idle to regain energy. You stop moving and every other action queued this tick is ignored. The gun still cools down.+10% of starting energy per tick, never above the starting energy

Within a tick, actions are applied in this order: body turn, turret turn, move, scan, shoot. Each costs energy; if you cannot afford one it is skipped.

The shortest way to turn from one angle to another is Math.IEEERemainder(target - current, 360), which returns a value between −180 and 180. Pass that straight to Turn or TurnTurret and the engine clamps it for you.

Scanning

A scan covers an arc anywhere in the arena (range 1000, longer than the arena diagonal). Anything whose body overlaps the arc is reported, sorted nearest first. A scan is a request, and the answer comes next tick, describing what was there when you asked.

foreach (var hit in s.LastScan?.Hits ?? Enumerable.Empty<ScanHit>())
{
    // hit.Type      HitType.Robot or HitType.Obstacle
    // hit.Bearing   absolute angle from you to its centre, degrees
    // hit.Distance  distance from you to its centre
    // hit.Radius    its size (robots are 18; obstacles 20-40)
}
  • A narrow arc costs less energy than a wide one, and tells you more precisely where a robot is. A 60° scan covers a sixth of the arena.
  • Scans see through obstacles, but bullets do not pass through them. Use Radius to check whether an obstacle is on your line of fire before you waste a shot. See the Blocked helper in the Hunter sample.
  • The arena does not remember anything for you: if you stop scanning a robot, you no longer know where it is. Store what you saw in fields.
  • A scan tells you where a robot was. To hit a moving robot, estimate its velocity from two sightings and aim ahead (see the Sniper sample).

Shooting and damage

  • The bullet starts 18 units in front of you along the turret direction and flies at 12 units per tick. It explodes at exactly the distance you asked for, whether or not anything is there. You choose where the blast happens.
  • Bullets pass over robots. Damage comes only from the explosion.
  • Bullets explode early if they hit an obstacle or the arena edge.
  • Every robot within 60 units of the explosion centre takes damage, including you:
    damage = power × 10 × (1 − distanceFromBlast / 60)
    A dead-centre hit at power 3 does 30. A hit 30 units off-centre does half that. Touching the edge of the blast does nothing.
  • Damage lands at the end of the tick the bullet explodes, and shows up in DamageTaken on the victim's next tick.
  • After firing, the gun needs 10 ticks to cool down. A Shoot during the cooldown is ignored and costs nothing.

Energy

Every robot starts with 100 energy. It is both your fuel and your health: actions spend it and explosions drain it. At 0 you are destroyed. There is no regeneration.

ActionCost
Move0.01 × |speed| every tick (full speed: 0.04 per tick)
Turn body0.01 per degree
Turn turret0.005 per degree
Scan0.001 per degree of arc (a 60° scan: 0.06)
Shoot1 × power + 0.002 × distance (power 2 at 300 units: 2.6)
Collision0.5 each time you are blocked by an edge, obstacle or robot

Standing still costs nothing, but scanning every tick for a whole 5000-tick battle at 60° would cost 300, which is more than you have. Be economical: scan narrow once you have found a target, and stay quiet when there is nothing to do.

Rounds, winning and the leaderboard

A battle is a match of several rounds (5 by default; the person starting the battle can choose 1 to 10). Every round has a new random arena layout and starting positions, and every robot starts it fresh: new instance, 100 energy, nothing remembered from the last round.

Winning a round

  • The last robot still alive wins the round. Robots are ranked by how long they survived, so with several robots you still get 1st, 2nd, 3rd and so on.
  • Robots destroyed on the same tick share a place.
  • If 5000 ticks pass with more than one robot alive, the survivors are ranked by energy; equal energy shares the place. A passive robot that keeps its energy can therefore beat an aggressive one that wasted its own.

Winning the match

Each round gives every robot a score from 0 to 1: its average result against each other robot (win = 1, tie = 0.5, loss = 0). In a two-robot match that is simply 1, 0.5 or 0 per round. Scores add up over the rounds, and the robot with the most points wins the match. Equal points is a draw. A battle can have between 2 and 6 robots.

The leaderboard

Every robot that has fought is ranked by an Elo-style rating. New robots start at 1000.

  • Each round, every pair of robots counts as one game, so ratings move for beating (or losing to) each opponent individually.
  • Beating a higher-rated robot earns more than beating a lower-rated one, and losing to a weaker robot costs more. Farming wins against a Sitting Duck soon stops paying.
  • The columns show rounds won, drawn and lost, and battles (matches) won out of battles fought.
  • Uploading a robot creates a new entry that starts again at 1000, so improving a robot means uploading it again under a new name. Delete the old one if you want it off the board.

Limits and what is allowed

Your code is run in a sandbox

Each robot runs in its own locked-down container with no network, no files, a hard memory limit of 192 MB and limited CPU. It cannot see other robots or other players' code.

  • Time: your Tick must return within about 200 ms (the very first tick gets longer for start-up). If it takes longer, or crashes the process, you are disqualified. Keep each tick quick.
  • Memory: running out of memory disqualifies you.
  • Exceptions: if Tick throws, you lose that tick's actions. Thirty in a row and you are disqualified.
  • Overflow checking is on: integer arithmetic that overflows throws instead of wrapping. Floating point (double) does not.

Allowed

The namespaces System, System.Collections, System.Collections.Generic, System.Linq, System.Text and RobotBattle.Api. That includes Math, List<T>, Dictionary<K,V>, StringBuilder and Random.

Not allowed

Anything else is rejected at upload with a message pointing at the offending line, including: files and networking (System.IO, System.Net), threads and tasks, reflection, Console, Environment, Process, Type/GetType(), Activator, GC, DateTime, interop/P/Invoke, and unsafe code.

Submission limits

  • Source up to 64 KB; at most 20 robots per player.
  • One public Robot subclass per submission.
  • Robot names are 2–24 letters, numbers, spaces, _ . -.

Sample robots

Use Load a sample in the editor to start from one of these. They are well commented.

SampleWhat it shows
Sitting DuckThe minimum valid robot. It does nothing: a handy practice target.
SpinnerThe crudest useful robot: circle, spin the turret, fire when something is seen. Does not aim, so it is easy to beat.
HunterThe starter code. Sweep to find an enemy, aim the turret, fire when lined up, check line of sight and sidestep if blocked.
SentryHolds position and remembers its target between ticks. Only moves when an obstacle blocks its shot.
DodgerStrafes sideways so bullets miss, using HitWall and DamageTaken to change direction.
SniperEstimates the enemy's velocity from two sightings and leads its shots.

Tips

  • Start with Hunter. Beat it, then beat Dodger.
  • Keep your own state in fields. Last tick's scan is the only memory the arena gives you.
  • Do not shoot at point-blank range. The blast is 60 units wide and hurts you too.
  • An explosion needs to land near the enemy, not on it. Aim for where the robot will be.
  • Watch your energy. Hitting walls, scanning wide forever and spraying shots all drain it, and the robot with the most energy wins a stalemate.
  • Replays show your robot's scan arcs. Switch them on to see what your robot is actually looking at.