Your First Bot
On the first day of a contest, aim for a bot that finishes the race and beats the first boss, and leave cleverness for later. This page writes that bot for Mad Pod Racing: it reads the statement as a spec, turns each turn's input into a struct, adds racing rules one at a time, and measures every rule by how many turns the race takes.
- Read a game statement and list exactly what your bot must read, print and stay within on every turn.
- Parse every input line into a struct, and recognize a desync when a bot skips a line.
- Write racing rules in priority order: aim, brake, ease off by angle, correct for drift, boost.
- Judge a rule by its turns to finish over many tracks, instead of by watching one race.
- Submit on day one, and know what a Wood boss series asks of your bot.
01Read the statement as a spec
CodinGame built the first leagues of Mad Pod Racing as a tutorial for newcomers to bot programming. Its description calls the game "an easy introduction to bot programming through a starship race", and says of the leagues from Wood to Silver, "You'll unlock new rules at every league."[1] Before you write any strategy, write down what the referee, CodinGame's program that runs the game, sends you each turn and what it accepts back. The statement, the rules page next to the code editor, says both.
Every bot game's statement has the same parts: the goal, the rules, how you win and lose, the input and output protocol, and constraints such as time limits. Read it the way you'd read an API spec. Each number in the protocol becomes a variable in your code, and each constraint becomes a check. The table does that for the first league of Mad Pod Racing.[1]
| The first league's statement says | So your code must |
|---|---|
| Input each turn: one line of 4 integers, your pod's x and y, then the next checkpoint's x and y | read four integers every turn, in that order |
| Output each turn: "2 integers for the target coordinates of your pod followed by thrust" | print one line, x y thrust, and flush it |
| 0 ≤ thrust ≤ 100 | never print a thrust outside that range |
| Response time: at most 1,000 ms on the first turn and 75 ms on every turn after | answer well inside 75 ms |
| A checkpoint counts when the center of your pod is inside its radius of 600 units | aim for a circle, not a point |
| A pod that doesn't reach its next checkpoint within 100 turns is eliminated | never circle a checkpoint for long |
| The map is 16,000 by 9,000 units with (0, 0) at the top left, and pods may leave it | accept targets and positions off the map |
The lose conditions also list "Your program provides incorrect output" and "Your program times out". A malformed line or a late answer loses the game, wherever your pod is on the track.[1]
Every CodinGame puzzle comes with starter code, which players call the stub. In CodinGame's words it "already parses the inputs in stdin, does nothing with them and prints a default output in the stdout".[2] Run it before you change anything: it proves the protocol works, and its comments describe each field.
The stub the author's lab recorded for this game reads more than the first statement lists: eight numbers on two lines. The first line holds six numbers about your pod, adding the rounded distance to the next checkpoint and the angle in degrees between your pod's heading (the way it points) and that checkpoint. The second line holds the opponent's x and y.[3] This page uses that format, because the rules below need the distance and the angle. If your league sends only the four numbers its statement lists, read exactly those; without the angle and the distance, only rules 1 and 4 below can run. Whichever description you start from, check it: print the raw lines your bot receives on its first turn and compare.
Worked example
One turn arrives as 5504 2269 10269 3294 4874 0, then 5396 3263.
Your pod is at (5504, 2269) and the next checkpoint at (10269, 3294). The distance is √(4765² + 1025²) = √23,755,850 ≈ 4,874 units, sent as a whole number. The angle 0 means the pod already points straight at the checkpoint. The opponent is at (5396, 3263).
The answer 10269 3294 100 aims at the checkpoint at full thrust.
The first statement doesn't say how a pod moves. Mad Pod Racing used to be called Coders Strike Back,[4] a CodinGame contest in 2016, and Magus's guide from that contest lists what happens to a pod each turn. It turns toward its target by at most 18° (on its first turn, by any amount), adds thethrust to its speed along its new heading, and moves. Then its speed is multiplied by 0.85, its position is rounded to whole units, and its speed is truncated toward zero.[5] So a pod keeps most of last turn's speed, and a pod that only aims at the checkpoint slides past it. The widgets on this page move pods under these rules as the author's lab reproduced them from the referee's code and checked them against official games.[3]
02Read every line, every turn
Your bot and the referee talk through a pipe. The referee writes lines into it and your program reads them in order, and the pipe has no idea where one turn ends and the next begins. If your bot reads fewer numbers than the referee sent, the leftovers become the start of its next turn, and every field after that holds the wrong number.
CodinGame's help pages warn about exactly this: "It's really important to continue reading all input data on the stdin each turn; else, your program will desynchronize from the referee program."[6] A bot in that state has a : it keeps running and keeps printing valid moves, but every decision uses numbers from the wrong turn.
The fix is to read every field of every line into a struct, a record type with one named field per number, before you decide anything. That includes fields no rule uses yet. The names help too: turn.nextCheckpointAngle says what "the sixth number on line one" doesn't.
Keep what your bot remembers between turns in a second struct. The input describes this turn only. Memory is what your bot carries forward, such as last turn's position or whether it has used its boost. Keeping the two apart makes each turn's input easy to print and check on its own.
Worked example
A bot reads the six numbers of line one and never reads line two. Turn 1 is fine.
On turn 2 the pipe still holds last turn's opponent line, 5396 3263, ahead of this turn's 5602 2290 10269 3294 4774 0. The bot reads six numbers: x = 5396 and y = 3263 (last turn's opponent), checkpoint x = 5602 and y = 2290 (its own position), distance = 10269 and angle = 3294.
An angle of 3,294° is far more than 90°, so the bot sets its thrust to 0 and prints 6034 -1686 0. That's a valid command. Four numbers now wait unread, the end of this turn's first line and its whole second line, and every turn adds two more.
Nothing in the game's log flags a desync. The commands are well formed, so the referee applies them, and the bot races badly from turn 2 on. That's why the bot at the end of this page reads line by line and checks how many numbers each line holds. With that check, the same bug stops the bot on turn 2 with a message in its error output, instead of losing the race without a trace.
Reading one line too many fails differently. The bot waits for a line that the referee won't send until it has your answer, so the turn's clock runs out. In CodinGame's words, if you start reading turn n+1's input before answering turn n, "the referee program will consider that your program did not respond in due time (timeout)".[6]
03Rules that win races
A pod that points at the checkpoint and thrusts flat out reaches it, then shoots past, because it keeps most of its speed from one turn to the next. On a tight bend it can circle a checkpoint until the 100-turn limit eliminates it. Each rule below fixes one way a simple bot loses time, and the widget after them measures what each one buys.
The angle in the rules is the absolute value of the input's angle: how far the checkpoint is from straight ahead, from 0° to 180°. The rules add up. Rule set 3 is rules 1, 2 and 3 together, and so on.
- Aim at the checkpoint at full thrust. Target the checkpoint's center with thrust 100.
- Don't thrust when the checkpoint is behind you. Past 90°, even after this turn's 18° of turning, thrust would push the pod mostly sideways or away from the checkpoint, so print 0 and let the pod turn first.
- Ease off as the angle grows. Full thrust under 45°, none over 90°, and a straight line between: thrust = 100 × (90 − angle) / 45, rounded down.
- Aim against the drift. Aim at the checkpoint minus k times the pod's velocity, explained below.
- Boost once, on a long straight. Print
BOOSTin place of the thrust when the checkpoint is less than 5° off and more than 6,000 units away, once per race.
Worked example
The checkpoint is 60° off the pod's heading. Rule 3 gives thrust = 100 × (90 − 60) / 45 = 66.7, so the bot prints 66.
At 30° the thrust is 100. At 100° it's 0, from rule 2.
Rule 4 handles drift: the pod's leftover speed carrying it where it no longer wants to go. If the pod already moves straight at the checkpoint, the offset point lies on the same line and nothing changes. If it slides sideways, the offset point moves the other way, so the pod's thrust leans against the slide and the pod curves into the checkpoint instead of past it. Hastings, who finished 4th in Coders Strike Back in 2016, described the rule: his bot "targets the point nextCheckPointPosition - 4 * velocity", and he "used 4 because it performed better than 3 or 5".[7] This page calls the rule .
The early leagues don't send your velocity, so the bot estimates it from how far the pod moved since last turn.
In words: take the checkpoint's position and move it back against the direction your pod traveled last turn, by k times the distance it traveled, and aim at that point. Do it for x and y separately.
Worked example
The checkpoint is at (8000, 4000). Last turn the pod moved from (5000, 3000) to (5300, 2880), a move of (+300, −120).
With k = 4 the bot aims at (8000 − 4 × 300, 4000 + 4 × 120) = (6800, 4480).
The move since last turn isn't quite the velocity Hastings used. Coders Strike Back sent each pod's speed after friction, while the move since last turn is the speed before friction, about 1 / 0.85 ≈ 1.18 times larger. The same k means a little more here, so treat k as a setting to tune.
Rule 5 needs a league with BOOST, a one-time burst: in the referee the author's lab reproduced, it adds 650 to the pod's speed, against 100 for full thrust.[3] The first league's statement allows only a thrust number, so rule set 5 waits for a later league.[1]
The order of the rules is a priority order: the angle sets the thrust, the drift moves the aim point, and the boost, the rarest case, overrides the thrust last.
On track A the turns to finish fall from 222 for rule set 1 to 185 for rule set 5, and over the 55 tracks the averages fall from 280 to 222. Rule 4 buys the most on average, 33 turns over rule set 3.
| Rule set | Rule added | Track A | Average of 55 tracks | Eliminated, of 60 |
|---|---|---|---|---|
| 1 | aim at the checkpoint, full thrust | 222 | 280 | 5 |
| 2 | no thrust when it's behind you | 214 | 263 | 2 |
| 3 | thrust by angle | 197 | 259 | 0 |
| 4 | aim against the drift, k = 4 | 189 | 226 | 0 |
| 5 | BOOST once | 185 | 222 | 0 |
The same table shows why one race is one sample. On track B, easing off by angle needs 175 turns against 149 for the rule set before it, the opposite of its effect on average. On track A, k = 3 finishes in 179 turns against 185 for k = 4, yet over the 55 tracks k = 4 averages 222 turns, k = 3 averages 225, and k = 5, which overshoots, averages 300. Watching a replay tells you how one race went. Counting turns over many tracks tells you whether a rule helps. Measuring Bot Strength works out how many tracks, or games, are enough.
04The whole bot
These complete programs are this page's bot in C++ and Rust, with the same names in both. readTurn reads both lines into the struct and stops the bot if a line holds the wrong count of numbers. Memory holds what the bot keeps between turns. decide applies the rules in priority order. main reads, decides, prints and flushes, once per turn. With no arguments, as on CodinGame, the bot plays rule set 4 with k = 4, because the first league's statement doesn't list BOOST; switch the default to 5 once yours does. Locally, ./bot 3 races rule set 3 and ./bot 5 3 uses k = 3.
// A Mad Pod Racing bot for the leagues with one pod each: two input lines a turn, one output line.
// On CodinGame it runs with no arguments (rule set 4, k = 4). Locally: ./bot <ruleSet 1-5> <k>.
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <optional>
#include <sstream>
#include <string>
#include <vector>
// One turn of input, in the order the referee sends it.
struct TurnInput {
int x, y; // your pod
int nextCheckpointX, nextCheckpointY;
int nextCheckpointDist; // distance to that checkpoint, a whole number
int nextCheckpointAngle; // degrees from your heading to it, -180..180
int opponentX, opponentY;
};
// Reads one line that must hold exactly `count` integers. Stopping loudly on a wrong count
// turns a desync into a crash on the turn it starts, instead of a race that quietly goes wrong.
std::optional<std::vector<int>> readNumbers(std::istream& in, std::size_t count) {
std::string line;
if (!std::getline(in, line)) return std::nullopt; // no more input: the game is over
std::cerr << line << '\n'; // the input isn't stored anywhere else; test-game logs show stderr
std::istringstream fields(line);
std::vector<int> values;
for (int value; fields >> value;) values.push_back(value);
if (values.size() != count) {
std::cerr << "expected " << count << " numbers, got " << values.size() << '\n';
std::exit(1);
}
return values;
}
std::optional<TurnInput> readTurn(std::istream& in) {
const auto pod = readNumbers(in, 6);
if (!pod) return std::nullopt;
const auto opponent = readNumbers(in, 2); // read it even though no rule uses it yet
if (!opponent) return std::nullopt;
const std::vector<int>& podLine = *pod;
const std::vector<int>& opponentLine = *opponent;
return TurnInput{podLine[0], podLine[1], podLine[2], podLine[3], podLine[4], podLine[5],
opponentLine[0], opponentLine[1]};
}
// What the bot keeps between turns. The input has no velocity, so the bot remembers
// last turn's position and treats the move since then as its velocity.
struct Memory {
bool hasLastPosition = false;
int lastX = 0, lastY = 0;
bool boostAvailable = true;
};
struct Command {
long targetX, targetY;
int thrust; // 0..100
bool boost; // print BOOST instead of the thrust
};
// The rules in priority order. Each rule set adds one rule to the one before it.
Command decide(const TurnInput& turn, Memory& memory, int ruleSet, double driftK) {
const int moveX = memory.hasLastPosition ? turn.x - memory.lastX : 0;
const int moveY = memory.hasLastPosition ? turn.y - memory.lastY : 0;
memory.hasLastPosition = true;
memory.lastX = turn.x;
memory.lastY = turn.y;
const int angle = std::abs(turn.nextCheckpointAngle);
int thrust = 100; // 1: full thrust
if (ruleSet >= 2 && angle > 90) thrust = 0; // 2: thrust behind you pushes you away
if (ruleSet >= 3) // 3: ease off as the angle grows
thrust = angle > 90 ? 0 : angle < 45 ? 100 : 100 * (90 - angle) / 45;
double targetX = turn.nextCheckpointX, targetY = turn.nextCheckpointY; // 1: aim at it
if (ruleSet >= 4) { // 4: aim against the drift
targetX -= driftK * moveX;
targetY -= driftK * moveY;
}
bool boost = false;
if (ruleSet >= 5 && memory.boostAvailable && angle < 5 && turn.nextCheckpointDist > 6000) {
boost = true; // 5: once, on a long straight
memory.boostAvailable = false;
}
// The referee wants integers: round to the nearest one instead of cutting the fraction off.
return {std::lround(targetX), std::lround(targetY), thrust, boost};
}
int main(int argc, char** argv) {
// Rule set 4 by default: switch it to 5 once your league's statement lists BOOST.
const int ruleSet = argc > 1 ? std::atoi(argv[1]) : 4;
const double driftK = argc > 2 ? std::atof(argv[2]) : 4.0;
Memory memory;
while (const auto turn = readTurn(std::cin)) {
const Command command = decide(*turn, memory, ruleSet, driftK);
std::cout << command.targetX << ' ' << command.targetY << ' ';
if (command.boost) std::cout << "BOOST";
else std::cout << command.thrust;
std::cout << std::endl; // endl flushes: the referee is waiting for this line
}
}
// A Mad Pod Racing bot for the leagues with one pod each: two input lines a turn, one output line.
// On CodinGame it runs with no arguments (rule set 4, k = 4). Locally: ./bot <rule_set 1-5> <k>.
use std::io::{self, BufRead, Write};
// One turn of input, in the order the referee sends it.
struct TurnInput {
x: i32, y: i32, // your pod
next_checkpoint_x: i32, next_checkpoint_y: i32,
next_checkpoint_dist: i32, // distance to that checkpoint, a whole number
next_checkpoint_angle: i32, // degrees from your heading to it, -180..180
#[allow(dead_code)] // read every turn, used by no rule yet
opponent_x: i32,
#[allow(dead_code)]
opponent_y: i32,
}
// Reads one line that must hold exactly `count` integers. Stopping loudly on a wrong count
// turns a desync into a crash on the turn it starts, instead of a race that quietly goes wrong.
fn read_numbers(lines: &mut impl Iterator<Item = io::Result<String>>, count: usize) -> Option<Vec<i32>> {
let line = lines.next()?.ok()?; // no more input: the game is over
eprintln!("{line}"); // the input isn't stored anywhere else; test-game logs show stderr
let values: Vec<i32> = line.split_whitespace().map_while(|field| field.parse().ok()).collect();
if values.len() != count {
eprintln!("expected {count} numbers, got {}", values.len());
std::process::exit(1);
}
Some(values)
}
fn read_turn(lines: &mut impl Iterator<Item = io::Result<String>>) -> Option<TurnInput> {
let pod = read_numbers(lines, 6)?;
let opponent = read_numbers(lines, 2)?; // read it even though no rule uses it yet
Some(TurnInput {
x: pod[0], y: pod[1], next_checkpoint_x: pod[2], next_checkpoint_y: pod[3],
next_checkpoint_dist: pod[4], next_checkpoint_angle: pod[5],
opponent_x: opponent[0], opponent_y: opponent[1],
})
}
// What the bot keeps between turns. The input has no velocity, so the bot remembers
// last turn's position and treats the move since then as its velocity.
struct Memory {
last_position: Option<(i32, i32)>,
boost_available: bool,
}
struct Command {
target_x: i64, target_y: i64,
thrust: i32, // 0..100
boost: bool, // print BOOST instead of the thrust
}
// The rules in priority order. Each rule set adds one rule to the one before it.
fn decide(turn: &TurnInput, memory: &mut Memory, rule_set: i32, drift_k: f64) -> Command {
let (move_x, move_y) = match memory.last_position {
Some((last_x, last_y)) => (turn.x - last_x, turn.y - last_y),
None => (0, 0),
};
memory.last_position = Some((turn.x, turn.y));
let angle = turn.next_checkpoint_angle.abs();
let mut thrust = 100; // 1: full thrust
if rule_set >= 2 && angle > 90 { thrust = 0; } // 2: thrust behind you pushes you away
if rule_set >= 3 { // 3: ease off as the angle grows
thrust = if angle > 90 { 0 } else if angle < 45 { 100 } else { 100 * (90 - angle) / 45 };
}
let mut target_x = turn.next_checkpoint_x as f64; // 1: aim at it
let mut target_y = turn.next_checkpoint_y as f64;
if rule_set >= 4 { // 4: aim against the drift
target_x -= drift_k * move_x as f64;
target_y -= drift_k * move_y as f64;
}
let mut boost = false;
if rule_set >= 5 && memory.boost_available && angle < 5 && turn.next_checkpoint_dist > 6000 {
boost = true; // 5: once, on a long straight
memory.boost_available = false;
}
// The referee wants integers: round to the nearest one instead of cutting the fraction off.
Command { target_x: target_x.round() as i64, target_y: target_y.round() as i64, thrust, boost }
}
fn main() {
let arguments: Vec<String> = std::env::args().collect();
// Rule set 4 by default: switch it to 5 once your league's statement lists BOOST.
let rule_set: i32 = arguments.get(1).and_then(|text| text.parse().ok()).unwrap_or(4);
let drift_k: f64 = arguments.get(2).and_then(|text| text.parse().ok()).unwrap_or(4.0);
let mut memory = Memory { last_position: None, boost_available: true };
let stdin = io::stdin();
let mut lines = stdin.lock().lines();
let mut out = io::stdout();
while let Some(turn) = read_turn(&mut lines) {
let command = decide(&turn, &mut memory, rule_set, drift_k);
let power = if command.boost { "BOOST".to_string() } else { command.thrust.to_string() };
writeln!(out, "{} {} {}", command.target_x, command.target_y, power).unwrap();
out.flush().unwrap(); // the referee is waiting for this line
}
}
Both programs compile with warnings treated as errors (g++ and clang++ in C++20 mode, rustc with the 2021 edition). Run through a local referee that uses the widgets' physics, each prints the same command as the widget's rule sets on all 141,045 turns of the test: all five rule sets on all 60 tracks, plus rule sets 4 and 5 with k = 3 and k = 5. So they finish track A in the same 222, 214, 197, 189 and 185 turns. The next page shows how to build that local referee.
A clock: this bot answers in microseconds, but a bot that searches has to watch the time. Any use of the opponent: its line is read and ignored, and nothing steers around it or into it. A plan for collisions, which the full game has. And any other input format: if a league sends a different line, the count check stops the bot on turn 1, which is your cue to read the statement again and change readTurn.
05Debug from the logs
Sooner or later you lose a race you didn't watch. Afterwards you have the game's log: the referee's summary, every bot's output and, in your own test games, your stderr, the output stream the referee doesn't read as moves. Ranking battles run without debug output, so their logs have no stderr at all.[8]
The input isn't stored either. CodinGame's documentation suggests printing what your bot reads to stderr, so a test game's log shows it next to your output, and the IDE shows stderr in its console while a game runs.[8][2] The bot above echoes every line it reads. Keep debug text short: a few numbers or a marker per turn is enough.
Each game's track comes from a seed, a number the map generator turns into the same track every time. A log's options line shows it, for example seed=12, and sending it back replays the same map with a new version of your bot.[8] That's how you check a fix: replay the lost race on its seed and see whether the new version does better. Then check other seeds, because a fix for one race can cost you another, as track B showed.
Never print debug text on stdout. The referee reads stdout as your moves, so an extra line is either an invalid move this turn or next turn's move.
06Submit on day one
In the Wood leagues, promotion usually doesn't depend on a leaderboard. When you submit, your bot plays a fixed number of games against the league's boss alone, N battles needing W wins, "for example 5 and 3" in CodinGame's words.[9]
A game counts as a win only when your bot is alone in first place, so draws don't count. If the series ends short of W wins, you stay in the league, and only a new submission plays new battles, counted from zero.[9] A failed series costs you a submission and some waiting.
So test against the boss before you submit. In the game's editor on CodinGame, choose C++ or Rust, replace the stub with a program from The whole bot, and press Play: you get the game's replay, and the console shows your stderr, including every input line the bot echoes.[2] CodinGame's own advice is to play several test games against the boss with different seeds, and to submit when your bot wins most of them.[9] From Bronze up the rule changes: your rating, computed from games against every player in your league room, has to reach the boss's rating. How Bot Contests Work explains that rating.
Submit the first version that finishes races, even if it's crude. Only a submitted bot gets promoted, and the next league's rules can add inputs and actions, so the statement is worth reading again after every promotion.[9]
07Leave room to grow
These rules finish races. On their own they won't reach Legend. From Gold on, CodinGame says, the input gives you "a large number of parameters to manage (list of waypoints, vector speed, remaining boosts ...)",[1] and the top Coders Strike Back bots simulated the game several turns ahead to choose their moves.[5][7] How you lay out your code now decides how much of it survives that change.
Keep four parts apart. Parsing turns text into a struct. Memory holds what you carry between turns. The rules turn the struct and the memory into a command. The main loop moves text in and out. A simulator, the next page's subject, then slots in beside the parser: it produces the same struct from a game it runs itself, so the same decide can race thousands of local tracks. A search, the subject of Phase 3, later replaces decide without touching the parser.
A common question at this point: what do you actually need to build all this by hand? For the first day, only what this page builds: a loop that reads every line, a struct, a few rules and a submission. To climb, you add pieces in order. First a simulator that matches the referee exactly (the next page). Then a way to measure whether a change helped (Measuring Bot Strength), and a search over moves (Phase 3). A trained network, the trainer that trains it and an exporter that packs it into your source file come last, and only for games where they pay off. The capstone lists every piece with the check that says it's done.
CodinGame takes one source file per bot, and "The code size for any game is limited to 100k characters."[10] Keep one file while the bot is small. When it grows into modules, a script can join them into one file before each submission; Fast Bots shows how.
08Where first bots lose
A desynced bot, a bot aiming at the wrong checkpoint and a bot printing a thrust of 0 every turn all send valid commands. Only your own checks and measurements catch them.
Reading a line only when you need it. A bot that reads the opponent line only when the opponent is close desyncs on the first turn it skips, which might be turn 30. Read every line, every turn, whether or not a rule uses it.
Printing floats or out-of-range thrust. The statement asks for integers and a thrust from 0 to 100, and incorrect output loses the game. The referee the author's lab ported rejects a target printed as 6800.0 and a thrust of 120 alike. Round targets to integers and clamp the thrust.
Trusting your own angles to the last digit. The referee sends rounded positions and whole degrees, while your own trigonometry runs on floating-point numbers. An angle you compute from rounded positions won't match the referee's exactly, so allow a margin whenever you compare angles. Floating Point explains why the last digits differ.
Tuning on one track. Track B rewards removing a rule that helps on average, and track A prefers k = 3. Judge every change on many seeded tracks.
09What's next
Referees and Simulators opens up the referee: what it does inside one turn, why its order of operations and its random numbers matter, and how to copy its rules into a simulator of your own that matches it exactly. With one, every rule on this page can be tested on thousands of races in seconds, and the searches in Phase 3 get a game to search.
10Sources
Widget readouts and the turn counts in the text are computed in the page from the pod physics in the author's lab; they are not CodinGame's own measurements.
- CodinGame, 2026. Mad Pod Racing (statement and game description, as served to a visitor who isn't logged in, retrieved 2026-10-30). The first league's protocol, its constraints (thrust 0 to 100, 1,000 ms first turn, 75 ms per turn), win and lose conditions and expert rules; the description of the tutorial leagues from Wood to Silver and of the Gold input.
- CodinGame Help Center. How does it work? The default code that parses the input, and stderr shown in the IDE console.
- Author's lab records, 2026 (private repository).
games/madpodracing/README.md: the Mad Pod Racing engine port, which replays 300 of 300 Gold referee games and 11 of 11 live Gold games exactly, with one BOOST of 650 per pod;games/madpodracing/bots/baseline/original.py: the game's stub as recorded, eight numbers on two lines per turn. - 5DN1L (forum moderator), 2021. Mad Pod Racing - Puzzle Discussion, post #538. "Coders Strike Back is the previous name of Mad Pod Racing".
- Magus, 2016. Coders Strikes Back (English version of his guide). The order of a pod's turn: an 18° turn limit, thrust, movement, friction of 0.85, rounded positions and truncated speeds; simulating several turns ahead.
- CodinGame Help Center. Turn-based Coding Games, section "StdIn/StdOut synch". Desynchronizing by skipping input, and the timeout from reading the next turn before answering.
- Hastings, 2016. Coders Strike Back, your ideas, posts #22 and #26. The drift rule, nextCheckPointPosition − 4 × velocity, and the choice of 4 over 3 or 5; he finished 4th of 2,530 on the contest leaderboard.
- CodinGame, 2026. Agent Skill: CodinGame (API documentation, retrieved 2026-10-30). Battle logs without stderr, test-game logs with it, printing the input to stderr, and replaying a map from its seed.
- CodinGame, 2026. Leagues and promotion (retrieved 2026-10-30). Boss battles in Wood leagues (N battles, W wins, draws not counted), testing against the boss before submitting, ranking above the boss from Bronze, and new rules after a promotion.
- CodinGame Help Center. Languages Versions. "The code size for any game is limited to 100k characters."