On this page
  1. Related
  2. Games
  3. References
  4. Sources

Rubber-band AI keeps computer racers near the player. Mario Kart 64 marks a CPU driver with a flag the decompilers name for rubber-banding, and the flag adds to the kart’s forward force1.

  • Power boostMario Kart 64 (1996). A flag the decompilers call rubber-banding pulls a CPU kart's added force toward 380 while a pacing check passes.1
  • Time scaleExcitebike 64 (2000). Every CPU rider's physics run up to 15% faster or slower by how far the CPU pack leads the humans, around a lead of 30.2
  • Speed stepRidge Racer (1993). Behind the player, the car in slot 0 aims at 1.01 times its field 0x10C; well ahead, at 80% of the lower of its usual target and its field 0xF8.3

Games

GameYearHowNote
Mario Kart 641996Power boosta flag the decompilers call rubber-banding pulls a CPU kart's added force toward 380 while a pacing check passes
Excitebike 642000Time scaleevery CPU rider's physics run up to 15% faster or slower by how far the CPU pack leads the humans, around a lead of 30
Ridge Racer1993Speed stepbehind the player, the car in slot 0 aims at 1.01 times its field 0x10C; well ahead, at 80% of the lower of its usual target and its field 0xF8
Every claim about this mechanic, by game (3)
Ridge Racer 1993In Ridge Racer's Japanese release (SLPS-00001), the computer car in entity slot 0 takes its target speed from func_80022C40 during the race (when its field 0x60 is 0 and the game state isn't 0x1A). Unless its mode field 0xAA is 2: when it's on an earlier lap than the player, or on the same lap and behind the player by more than a margin, its target is 1.01 times its field 0x10C; when it's on a later lap, or more than 0x600 ahead on the same lap, its target is 80% of the lower of the usual target and its field 0xF8. The margin is 0x800, or 0x300 times the player's lap count when D_801D35D0 is 0. Between the two the target is left as it was. When D_801D35D0 is 0 and the player is on the last lap, the target is held to field 0xF8.Code
Mario Kart 64 1996In the US version of Mario Kart 64, CPU_FAST_EFFECT, which the decompilers comment as the "CPU Rubberbanding flag", raises a CPU driver's forward force: while it's set, a term in that force moves toward 380 by half the gap each update, and once it's cleared the term falls toward 0 by a tenth of the gap. regulate_cpu_speed sets the flag on a CPU driver below its target speed when the pacing check func_800088D8 returns true, and clears it otherwise.Code
Excitebike 64 2000In Excitebike 64's US Rev 1 build, riders are rubber-banded through timeScale, which multiplies the length of each of a rider's physics steps (gPhysDeltaTime). After the countdown, outside time trial and with a human racing, race_update_riders steps each CPU rider's scale by 0.0002 per update toward a target: 1 plus its skill (spread evenly over about ±0.1 by race_init_ai_skills, and used when there are two or more CPU riders), less 0.1 when it's more than 64 ahead of the CPU riders' average raceDist, less the CPU pack's lead over the humans' average beyond 30, divided by 256 and held to ±0.15, all times the track's cpuSpeed for the difficulty (a finished rider targets 1, the Beat This cheat uses the hardest difficulty's cpuSpeed plus 0.05, and a duel 1.15 times the track's). The code's comment says "CPU riders slow down as their pack leads the humans by more than 30 m and speed up as it falls behind". A human's scale steps by 0.001 toward 1 plus their distance behind the humans' average (plus 16 when it's over 2) over 1024, held between 0.95 and 1.05. With one human the target is 1, and since the step has no dead zone the scale alternates between 1 and 1.001. Mario Kart 64 instead adds a force term toward 380 while a pacing flag is set (mk64/rubber-band).Code

References

  1. ^ abIn the US version of Mario Kart 64, CPU_FAST_EFFECT, which the decompilers comment as the "CPU Rubberbanding flag", raises a CPU driver's forward force: while it's set, a term in that force moves toward 380 by half the gap each update, and once it's cleared the term falls toward 0 by a tenth of the gap. regulate_cpu_speed sets the flag on a CPU driver below its target speed when the pacing check func_800088D8 returns true, and clears it otherwise.Coden64decomp/mk64
    How we know
    479#define CPU_FAST_EFFECT             0x200000 // CPU Rubberbanding flag
    
    include/defines.h line 479 at 58cfcb0 · upstream ↗
    3086    if ((player->type & PLAYER_HUMAN) != PLAYER_HUMAN) {
    3087        if ((player->effects & CPU_FAST_EFFECT) == CPU_FAST_EFFECT) {
    3088            move_f32_towards(&player->unk_0E8, 380.0f, 0.5f);
    3089        } else {
    3090            move_f32_towards(&player->unk_0E8, 0.0f, 0.1f);
    
    src/player_controller.c lines 3086–3092 at 58cfcb0 · upstream ↗
    3107    var_f2 = (player->unk_08C + player->unk_0E8 + player->boostPower + player->unk_0E4) - player->unk_0A0;
    
    src/player_controller.c line 3107 at 58cfcb0 · upstream ↗
    863void move_f32_towards(f32* startingValue, f32 targetValue, f32 somePercent) {
    864    *startingValue -= ((*startingValue - targetValue) * somePercent);
    
    src/render_player.c lines 863–864 at 58cfcb0 · upstream ↗
    203                if (var_a1 != 1) {
    204                    if (speed < targetSpeed) {
    ⋮
    206                            player_accelerate_alternative(player);
    ⋮
    209                        } else if (func_800088D8(playerId, gLapCountByPlayerId[playerId],
    210                                                 gGPCurrentRaceRankByPlayerIdDup[playerId]) == true) {
    211                            player->effects |= CPU_FAST_EFFECT;
    212                            player_accelerate_alternative(player);
    213                        } else {
    214                            player->effects &= ~CPU_FAST_EFFECT;
    215                            player_decelerate_alternative(player, 1.0f);
    
    src/cpu_vehicles_camera_path/cpu_speed_control.inc.c lines 203–216 at 58cfcb0 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-22.

    In the claim ledger

  2. ^In Excitebike 64's US Rev 1 build, riders are rubber-banded through timeScale, which multiplies the length of each of a rider's physics steps (gPhysDeltaTime). After the countdown, outside time trial and with a human racing, race_update_riders steps each CPU rider's scale by 0.0002 per update toward a target: 1 plus its skill (spread evenly over about ±0.1 by race_init_ai_skills, and used when there are two or more CPU riders), less 0.1 when it's more than 64 ahead of the CPU riders' average raceDist, less the CPU pack's lead over the humans' average beyond 30, divided by 256 and held to ±0.15, all times the track's cpuSpeed for the difficulty (a finished rider targets 1, the Beat This cheat uses the hardest difficulty's cpuSpeed plus 0.05, and a duel 1.15 times the track's). The code's comment says "CPU riders slow down as their pack leads the humans by more than 30 m and speed up as it falls behind". A human's scale steps by 0.001 toward 1 plus their distance behind the humans' average (plus 16 when it's over 2) over 1024, held between 0.95 and 1.05. With one human the target is 1, and since the step has no dead zone the scale alternates between 1 and 1.001. Mario Kart 64 instead adds a force term toward 380 while a pacing flag is set (mk64/rubber-band).Codechasmlol/excitebike64 · n64decomp/mk64revised
    How we know
    2529    gPhysTicks = ticks;
    2530    gPhysDeltaTime = (f32)gPhysTicks * rider->timeScale / (f32)FRAME_RATE;
    
    src/game/bikenew.c lines 2529–2530 at 9cdb194 · upstream ↗
    541    } else if (gGame.mode != GAME_MODE_TIME_TRIAL && gEvent.state >= 5) {
    542        /* after the countdown: CPU riders slow down as their pack leads the humans by more than 30 m and speed up as
    543           it falls behind, around the track's speed for the difficulty and their own skill; humans close up */
    544        cpuLead = ((cpuAvg - humanAvg) - 30.0f) * 0.00390625f;
    545        if (cpuLead > 0.15f) {
    546            cpuLead = 0.15f;
    547        } else if (cpuLead < -0.15f) {
    548            cpuLead = -0.15f;
    ⋮
    552            if (rider->isCpu != 0) {
    553                scale = 0.0f;
    554                if (numCpu >= 2) {
    555                    scale = rider->aiSkill;
    ⋮
    
    src/game/unnamed/s0_80062F50.c lines 541–601 at 9cdb194 · upstream ↗
    790/* Spreads the CPU riders' skills evenly from -0.1 to 0.1 in sAiRiderOrder (then +0.01 for exactly 0.1, -0.01 for
    791   the others) and sets their time scale to 1.03 - skill. */
    ⋮
    800            gRiders[sAiRiderOrder[i]].aiSkill = (i - (count - 1) * 0.5f) / ((count - 1) * 5.0f);
    
    src/game/unnamed/s0_80062F50.c lines 788–807 at 9cdb194 · upstream ↗
    479#define CPU_FAST_EFFECT             0x200000 // CPU Rubberbanding flag
    
    include/defines.h line 479 at 58cfcb0 · upstream ↗
    3086    if ((player->type & PLAYER_HUMAN) != PLAYER_HUMAN) {
    3087        if ((player->effects & CPU_FAST_EFFECT) == CPU_FAST_EFFECT) {
    3088            move_f32_towards(&player->unk_0E8, 380.0f, 0.5f);
    3089        } else {
    3090            move_f32_towards(&player->unk_0E8, 0.0f, 0.1f);
    
    src/player_controller.c lines 3086–3092 at 58cfcb0 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Revised after a check by a second AI model, 2026-10-22.

    In the claim ledger

  3. ^In Ridge Racer's Japanese release (SLPS-00001), the computer car in entity slot 0 takes its target speed from func_80022C40 during the race (when its field 0x60 is 0 and the game state isn't 0x1A). Unless its mode field 0xAA is 2: when it's on an earlier lap than the player, or on the same lap and behind the player by more than a margin, its target is 1.01 times its field 0x10C; when it's on a later lap, or more than 0x600 ahead on the same lap, its target is 80% of the lower of the usual target and its field 0xF8. The margin is 0x800, or 0x300 times the player's lap count when D_801D35D0 is 0. Between the two the target is left as it was. When D_801D35D0 is 0 and the player is on the last lap, the target is held to field 0xF8.Codekazuyette/rr-decomp
    How we know
    10    if (car->hAA != 2) {
    11        lim = 0x800;
    12        if (D_801D35D0 == 0)
    13            lim = D_8007C32C * 3 * 256;
    14        if ((car->lap == D_8007C32C && lim < car->dist) || car->lap < D_8007C32C) {
    15            *spd = car->x10C / 100 + car->x10C;
    16            *acc = car->FC;
    17        } else if ((car->lap == D_8007C32C && car->dist < -0x600) || D_8007C32C < car->lap) {
    18            v = *spd;
    19            if (car->F8 < v)
    20                v = car->F8;
    21            *spd = v * 8 / 10;
    ⋮
    
    src/fn/func_80022C40.c lines 10–27 at 3b42b36 · upstream ↗
    40    if (D_801D35D0 == 0) {
    41        n = 3;
    42        if (D_8007C210 == 1)
    43            n = 2;
    44        if (D_8007C32C == n) {
    45            if (*spd > car->F8) {
    46                if ((*spd = car->F8) < car->x88)
    
    src/fn/func_80022C40.c lines 40–46 at 3b42b36 · upstream ↗
    101    if (D_801D34F8 != 0x1A && car == &D_801E9250[0]) {
    102        acc = s->wA4;
    103        if (s->w60 == 0)
    104            func_80022C40(car, &spd, &acc);
    
    src/fn/func_80022F88.c lines 101–104 at 3b42b36 · upstream ↗
    70    if (D_801D77B8 == 0) {
    71        d = car->pos - D_8007C260;
    72        if (d >= 0)
    73            d = d >= half ? d - len : d;
    74        else
    75            d = half >= -d ? d : len + d;
    76        car->E0 = d;
    
    src/fn/func_800217F4.c lines 70–76 at 3b42b36 · upstream ↗
    47            if (D_801D77B8 == 0) {
    48                if ((((D_801E90E0 << 16) + c->w8) % (D_801E90E0 << 8) & ~0xFF) == 0x2700
    49                    && (((D_801E90E0 << 16) + c->w84) % (D_801E90E0 << 8) & ~0xFF) == 0x2800) c->h9C++;
    
    src/fn/func_80025268.c lines 47–49 at 3b42b36 · upstream ↗
    87    D_801D35A0 = 3;
    ⋮
    93    if (D_8007C210 == 1)
    94        D_801D35A0--;
    
    src/fn/func_8001384C.c lines 87–94 at 3b42b36 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-22.

    In the claim ledger

Sources

Code