Skip to content

Performance Optimization ​

Optimizing your bot's performance ensures it executes quickly, avoids timeouts, and can implement sophisticated strategies without slowdowns. This guide covers techniques for writing efficient bot code.

Understanding Turn Time Limits ​

Each bot has a limited time budget per turn (default: 30,000 microseconds or 30 milliseconds).

Turn Timeout Consequences ​

If your bot exceeds the turn timeout:

  • The bot skips that turn (no commands executed)
  • Repeated timeouts can result in penalties
  • In extreme cases, the bot may be disqualified

Typical Execution Times ​

Bot ComplexityTypical Execution Time
Simple bot (basic movement, linear targeting)< 1 ms
Intermediate bot (pattern detection, statistical targeting)2-10 ms
Advanced bot (wave surfing, guess-factor targeting)10-25 ms
Complex statistical bot (large data structures, clustering)15-30 ms

The goal: Stay well under 30ms to maintain consistent performance.

TPS vs. Computation Time ​

TPS (Turns Per Second) controls visualization speed, not computation limits:

  • Visual speed: How fast you see the battle (adjustable in GUI)
  • Computation time: The actual limit per bot per turn (fixed at 30ms default)

See TPS for visualization speed control.

Important: Even at 60 TPS visual speed, your bot still has 30ms per turn. TPS affects display only, not bot execution.

Optimization Techniques ​

Algorithm Efficiency ​

Choose appropriate algorithms:

  • Linear targeting - O(1), very fast
  • Circular targeting - O(1) with trigonometry, still fast
  • Statistical targeting - O(n) where n = data points, potentially slow
  • Pattern matching - O(n×m) where n = history length, m = pattern length, can be very slow

Optimize loops:

java
// Inefficient - creates ArrayList every turn
for (Wave wave : new ArrayList<>(waves)) {
    // Process wave
}

// Better - iterate directly
Iterator<Wave> it = waves.iterator();
while (it.hasNext()) {
    Wave wave = it.next();
    if (wave.hasHit()) {
        it.remove();
    }
}

Data Structure Selection ​

Choose efficient data structures:

For frequent insertions/removals:

  • LinkedList (Java)
  • deque (Python)
  • LinkedList<T> (C#)

For quick lookups:

  • HashMap (Java)
  • dict (Python)
  • Dictionary<TKey, TValue> (C#)

For sorted data:

  • TreeMap (Java)
  • SortedDict (Python's sortedcontainers)
  • SortedDictionary<TKey, TValue> (C#)

Caching Calculations ​

Avoid recalculating values that don't change:

java
// Bad - calculates every turn
double enemyDistance = Math.hypot(
    enemy.getX() - getX(),
    enemy.getY() - getY()
);

// Better - cache until enemy moves
private double cachedEnemyDistance;
private long lastEnemyUpdate;

void onScannedBot(ScannedBotEvent e) {
    cachedEnemyDistance = e.getDistance();
    lastEnemyUpdate = getTime();
}

Trigonometry Optimization ​

Trigonometric functions are relatively expensive:

Pre-compute where possible:

java
// Pre-compute common angles
private static final double[] SIN_TABLE = new double[360];
private static final double[] COS_TABLE = new double[360];

static {
    for (int i = 0; i < 360; i++) {
        double radians = Math.toRadians(i);
        SIN_TABLE[i] = Math.sin(radians);
        COS_TABLE[i] = Math.cos(radians);
    }
}

// Fast lookup (approximate)
public double fastSin(double degrees) {
    return SIN_TABLE[(int) degrees % 360];
}

Use built-in optimizations:

java
// Slower
double distance = Math.sqrt(dx * dx + dy * dy);

// Faster (when available)
double distance = Math.hypot(dx, dy);

Limiting Data Storage ​

Large data structures slow down iteration:

Limit history size:

java
private static final int MAX_HISTORY = 1000;

void recordData(ScanData data) {
    history.add(data);
    if (history.size() > MAX_HISTORY) {
        history.remove(0); // Remove oldest
    }
}

Use circular buffers:

java
private ScanData[] history = new ScanData[MAX_HISTORY];
private int historyIndex = 0;

void recordData(ScanData data) {
    history[historyIndex++ % MAX_HISTORY] = data;
}

Lazy Evaluation ​

Defer expensive calculations until necessary:

java
// Bad - calculates even if not needed
double angle = calculateBestAngle();
if (shouldFire()) {
    setFire(angle);
}

// Better - only calculate when needed
if (shouldFire()) {
    double angle = calculateBestAngle();
    setFire(angle);
}

Profiling Your Bot ​

Identifying Bottlenecks ​

Measure execution time of different sections:

java
long start = System.nanoTime();
performTargeting();
long targetingTime = System.nanoTime() - start;

start = System.nanoTime();
performMovement();
long movementTime = System.nanoTime() - start;

if (getTime() % 100 == 0) {
    System.out.println("Targeting: " + targetingTime/1000 + "µs");
    System.out.println("Movement: " + movementTime/1000 + "µs");
}

Finding Performance Issues ​

Common performance bottlenecks:

  • Excessive object creation - Creates garbage collection pressure
  • Nested loops - O(n²) or worse complexity
  • Large data structure iteration - Processing thousands of entries per turn
  • Unnecessary calculations - Computing values that won't be used

Memory Management ​

Garbage Collection ​

Minimize object creation in the main loop:

java
// Bad - creates new objects every turn
Point2D.Double myPos = new Point2D.Double(getX(), getY());

// Better - reuse objects
private Point2D.Double myPos = new Point2D.Double();

public void run() {
    while (true) {
        myPos.setLocation(getX(), getY());
        // Use myPos
    }
}

Memory Leaks ​

Remove unused data:

java
// Don't forget to clean up old waves
waves.removeIf(Wave::hasPassedTarget);

// Don't store references to destroyed bots
if (enemy.getEnergy() <= 0) {
    enemyData.remove(enemy.getName());
}

Platform-Specific Considerations ​

Java/Kotlin ​

  • JIT compilation warms up over time (first few rounds may be slower)
  • Use ArrayList for fast iteration, HashMap for lookups
  • Avoid Stream API in hot loops (adds overhead)

Python ​

  • Use numpy for numerical calculations (much faster than pure Python)
  • List comprehensions faster than manual loops
  • Consider __slots__ for data classes to reduce memory

C# ​

  • Structs faster than classes for small data (no heap allocation)
  • Use List<T> and Dictionary<TKey, TValue> for performance
  • LINQ convenient but adds overhead in tight loops

TypeScript / JavaScript ​

  • V8 JIT is very fast; avoid eval() and dynamic property access in hot loops
  • Use Map for lookups (faster than plain object property access for frequent updates)
  • Avoid Array spread ([...arr]) and rest parameters in tight loops (heap pressure)

Testing Performance ​

Benchmarking Improvements ​

Compare execution times before/after optimization:

  1. Add timing measurements to key sections
  2. Run 35+ round battles for statistical validity
  3. Compare average execution times
  4. Ensure win rate doesn't decrease with optimizations

Avoiding Premature Optimization ​

Follow this priority:

  1. Correctness first - Make it work
  2. Clarity second - Make it readable
  3. Performance third - Make it fast (only where needed)

Profile before optimizing - Don't guess what's slow, measure it.

Further Reading ​

Released under the Apache License 2.0.