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);
}
}
- Sign in with a name, then write or load a sample in the editor.
- Give it a name and press Compile & upload. Errors show with line numbers.
- Tick two or more robots in the list (yours or anyone else's), choose how many rounds, and press Fight!.
- 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.
| Property | Meaning |
|---|---|
Tick | Tick number, starting at 0. |
ArenaWidth, ArenaHeight | Size of the arena. |
X, Y | Your position (centre of the robot). |
Energy | Your remaining energy. This is also your health. See Energy. |
Heading | The direction your body faces, in degrees (0–360). |
TurretHeading | The direction your gun points, in degrees (0–360). Bullets leave in this direction. |
Speed | Your current speed in units per tick (negative when reversing). |
LastScan | The result of the scan you requested last tick, or null if you did not request one (or could not afford it). |
DamageTaken | Energy lost to explosions since your previous tick. |
HitWall | True 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.
| Call | What it does | Limit |
|---|---|---|
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
Radiusto check whether an obstacle is on your line of fire before you waste a shot. See theBlockedhelper 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:
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 = power × 10 × (1 − distanceFromBlast / 60) - Damage lands at the end of the tick the bullet explodes, and shows up in
DamageTakenon the victim's next tick. - After firing, the gun needs 10 ticks to cool down. A
Shootduring 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.
| Action | Cost |
|---|---|
| Move | 0.01 × |speed| every tick (full speed: 0.04 per tick) |
| Turn body | 0.01 per degree |
| Turn turret | 0.005 per degree |
| Scan | 0.001 per degree of arc (a 60° scan: 0.06) |
| Shoot | 1 × power + 0.002 × distance (power 2 at 300 units: 2.6) |
| Collision | 0.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
Tickmust 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
Tickthrows, 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
Robotsubclass 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.
| Sample | What it shows |
|---|---|
| Sitting Duck | The minimum valid robot. It does nothing: a handy practice target. |
| Spinner | The crudest useful robot: circle, spin the turret, fire when something is seen. Does not aim, so it is easy to beat. |
| Hunter | The starter code. Sweep to find an enemy, aim the turret, fire when lined up, check line of sight and sidestep if blocked. |
| Sentry | Holds position and remembers its target between ticks. Only moves when an obstacle blocks its shot. |
| Dodger | Strafes sideways so bullets miss, using HitWall and DamageTaken to change direction. |
| Sniper | Estimates 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.