What does this program do?
This program runs a short driving test to measure how fast your bot moves based on two stats you choose: SPEED and ENDURANCE. It also simulates how motor power changes as a match goes on — so you can feel what it's like to drive at the start vs. the very end of a game.
While driving, it constantly watches the track sensors and reacts if your bot reaches the border edge. Think of it as a smarter test loop: not just "go fast," but "go fast the way a real match bot would."
SPEED = 0 and ENDURANCE = 0 lines in the on start block and set them to your character's actual stat values. If you leave both at zero, the bot won't move.
Three buttons. That's it.
The whole program runs from the micro:bit's buttons. Here's what each one does:
Run the Test
Calculates power from your stats, then drives straight for the test duration. Watch for slowdown depending on your match time setting.
Set Match Time
Cycles through 0 → 60 → 120 → 180 seconds. Set this before pressing A to simulate how tired the bot is at that point.
Emergency Stop
Cuts all motors immediately. Use this if your bot heads somewhere it shouldn't. Shows an ✕ on the LED screen.
How each section works
let TEST_DURATION_MS = 5000
let BORDER_CHECK_MS = 60
let BORDER_PIVOT_MS = 400
let BORDER_PIVOT_POWER = 50
let BACKUP_MS = 600
let BORDER_THRESHOLD = 200
let SPEED = 0
let ENDURANCE = 0
This block runs once the moment the micro:bit turns on. It fills in all the settings your program needs before any button gets pressed. Think of it as writing values onto a configuration sheet.
TEST_DURATION_MS = 5000 — Each test run lasts 5,000 milliseconds (5 seconds). You can change this number to make tests longer or shorter.BORDER_CHECK_MS = 60 — While driving, the bot checks its sensors every 60ms. The smaller this number, the faster it reacts to the edge.SPEED = 0 and ENDURANCE = 0 — These are yours to fill in. Replace the zeros with your character's stat values before uploading to the micro:bit.
let running = false
let testStart = 0
let BORDER_CHECK_MS = 0
let borderSide = 0
function computeStats() {}
function checkBorder() { return 0 }
function handleBorder() {}
input.onButtonPressed(Button.A, function () {
computeStats()
running = true
testStart = input.runningTime()
basic.pause(BORDER_CHECK_MS)
while (running) {
borderSide = checkBorder()
if (borderSide > 0) {
handleBorder()
}
basic.pause(BORDER_CHECK_MS)
}
running = false
basic.showString("DONE")
basic.pause(500)
basic.clearScreen()
})
Pressing A runs the full test sequence. Steps happen in order, from top to bottom:
running = true and record the start time — this "turns on" the loop below and starts the clock.currentPower — equal power left and right means the bot drives straight with no turning.
let simulatedMatchTime = 0
input.onButtonPressed(Button.B, function () {
if (simulatedMatchTime == 0) {
simulatedMatchTime = 60
basic.showLeds(`
# . . . .
# . . . .
# . . . .
# . . . .
# . . . .
`)
} else if (simulatedMatchTime == 60) {
simulatedMatchTime = 120
basic.showLeds(`
# # . . .
# # . . .
# # . . .
# # . . .
# # . . .
`)
} else if (simulatedMatchTime == 120) {
simulatedMatchTime = 180
basic.showLeds(`
# # # . .
# # # . .
# # # . .
# # # . .
# # # . .
`)
} else {
simulatedMatchTime = 0
basic.showLeds(`
. . . . .
. . . . .
. . # . .
. . . . .
. . . . .
`)
}
basic.pause(500)
basic.clearScreen()
})
Each press of B cycles the simulatedMatchTime through four values: 0 → 60 → 120 → 180 → 0. The LED screen shows you exactly where you are in the cycle so you always know your setting.
let running = false
input.onButtonPressed(Button.AB, function () {
running = false
basic.showLeds(`
# . . . #
. # . # .
. . # . .
. # . # .
# . . . #
`)
basic.pause(500)
basic.clearScreen()
})
Hold both A and B at the same time. The program cuts power and exits the loop instantly — no gradual slowdown. Three things happen:
running = false — this breaks out of the driving while-loop on the very next check (within 60ms).
let SPEED = 0
let ENDURANCE = 0
let simulatedMatchTime = 0
let basePower = 0
let DECAY_RATE = 0
let endurancePercent = 0
let enduranceFactor = 0
let currentPower = 0
function computeStats () {
basePower = SPEED * 100 / 20
DECAY_RATE = 20 - ENDURANCE
endurancePercent = 100 - DECAY_RATE * simulatedMatchTime / 40
endurancePercent = Math.max(endurancePercent, 20)
enduranceFactor = endurancePercent / 100
currentPower = Math.round(basePower * enduranceFactor)
currentPower = Math.max(currentPower, Math.max(Math.round(basePower * 0.2), 10))
}
This function is called every time you press A. It runs the math to figure out what motor power to use right now, based on your stats and where you are in the match.
DECAY_RATE = 20 − ENDURANCE ← lower endurance = faster decay
endurancePercent = 100 − (DECAY_RATE × matchTime ÷ 40) ← shrinks over time
currentPower = basePower × enduranceFactor ← final number sent to motors
let sensor1State = 0
let sensor2State = 0
let sensor3State = 0
let sensor4State = 0
let BORDER_THRESHOLD = 0
function checkBorder () {
if (sensor1State > BORDER_THRESHOLD) {
return 1
} else if (sensor4State > BORDER_THRESHOLD) {
return 2
} else if (sensor2State > BORDER_THRESHOLD || sensor3State > BORDER_THRESHOLD) {
return 3
}
return 0
}
Your Cutebot Pro has four line-tracking sensors on its underside. This function reads all four and returns a number telling the main loop which direction trouble is coming from. The white border reflects more light — that's what the sensor "sees."
let BACKUP_MS = 0
let BORDER_PIVOT_MS = 0
let borderSide = 0
let recheck = 0
function checkBorder() { return 0 }
function handleBorder () {
basic.pause(50)
basic.pause(BACKUP_MS)
basic.pause(100)
if (borderSide == 1) {
basic.pause(BORDER_PIVOT_MS + Math.randomRange(-80, 80))
} else if (borderSide == 2) {
basic.pause(BORDER_PIVOT_MS + Math.randomRange(-80, 80))
} else {
basic.pause(BACKUP_MS + 300)
basic.pause(100)
basic.pause(BORDER_PIVOT_MS * 2)
}
basic.pause(100)
recheck = checkBorder()
if (recheck > 0) {
basic.pause(BACKUP_MS)
basic.pause(100)
basic.pause(BORDER_PIVOT_MS)
basic.pause(100)
}
}
When a sensor detects the border, this function takes over to steer the bot back into the ring. The exact move depends on which sensor fired.
BACKUP_MS (600ms) to create room for a turn.1 (left danger) → pivot right — one wheel forward, one backward2 (right danger) → pivot left3 (head-on) → back up extra, then make a wider turn
checkBorder again. If still on white, does one more backup-and-turn before resuming normal driving.Math.randomRange(-80, 80) adds a small random variation to each pivot time. This prevents the bot from getting stuck in the exact same loop if it hits the same spot twice.
What every variable means
Gold = you set it before uploading. Cyan = the program calculates it automatically. Purple = a fixed constant that controls behavior.
Watching speed decay in action
Here's what currentPower looks like for a bot with SPEED = 10, ENDURANCE = 5 at different points in the match. Low ENDURANCE = steep drop. High ENDURANCE = flat line.
Motor power at each match point · SPEED 10 · ENDURANCE 5
This bot hits the 20% power floor before the match ends. Invest more in ENDURANCE to flatten this curve.
Tips for using this program
Set your stats first
Find the SPEED = 0 and ENDURANCE = 0 lines in on start. Type in your character's real values before uploading to the micro:bit.
Test all 4 time points
Run at 0s, 60s, 120s, and 180s. Watch whether the bot feels different. If it doesn't, your ENDURANCE is strong. If it crawls at 180s, it's low.
Adjust TEST_DURATION_MS
5 seconds is quick. Try 10000 (10s) to see more border reactions — especially useful if you want to watch the recovery logic work multiple times.
Keep A+B handy
If the bot starts heading somewhere bad, hit A+B immediately. It's always safe to emergency stop and re-run from the beginning.
Compare different builds
Try high SPEED / low ENDURANCE vs. low SPEED / high ENDURANCE. Run both at 180s. The results might surprise you.
Read the LED display
One column of LEDs = 60s. Two columns = 120s. Three = 180s. Use this as a quick visual check before pressing A so you always know your current setting.